Открыть на GitHub

Стратегия Dark Cloud Piercing CCI

Обзор

Стратегия является портом советника MetaTrader Expert_ADC_PL_CCI на платформу StockSharp. Алгоритм ищет на графике свечные разворотные комбинации «Пронзание» и «Темное облако» и подтверждает их с помощью индикатора Commodity Channel Index (CCI). После появления корректного паттерна и экстремального значения CCI стратегия открывает позицию в направлении разворота, а закрывает её, когда CCI выходит из зоны экстремума.

Индикаторы

  • Commodity Channel Index (CCI) — отслеживает экстремальные значения импульса и формирует условия выхода.
  • Средний размер тела свечи (SMA) — определяет «длинные» свечи, необходимые для паттернов.
  • Среднее значение закрытия (SMA) — фильтрует тренд, повторяя использование скользящей средней в оригинальном MQL-коде.

Правила торговли

Вход

  • Покупка (Piercing Line):
    1. Предыдущая свеча — длинная медвежья, её открытие выше закрытия.
    2. Текущая завершенная свеча — длинная бычья, открывается ниже минимума предыдущей и закрывается внутри её тела, выше середины, но ниже предыдущего открытия.
    3. Середина тела старшей свечи должна находиться ниже скользящей средней, подтверждая нисходящий импульс.
    4. Последнее завершенное значение CCI должно быть меньше или равно -EntryConfirmationLevel (по умолчанию 50).
    5. При наличии короткой позиции она полностью закрывается перед открытием лонга.
  • Продажа (Dark Cloud Cover): зеркальные условия — длинная бычья свеча, за которой следует длинная медвежья с гэпом вверх и закрытием ниже середины предыдущего тела при CCI ≥ EntryConfirmationLevel.

Выход

  • Длинные позиции: закрываются, когда CCI пересекает сверху вниз уровень ExitLevel или опускается ниже -ExitLevel.
  • Короткие позиции: закрываются, когда CCI пересекает снизу вверх уровень -ExitLevel или ExitLevel.

Размер позиции

  • Используется базовый параметр Volume. При развороте позиции к объёму ордера добавляется абсолютное значение текущей позиции, чтобы выполнить полный переворот.

Параметры

Параметр Описание Значение по умолчанию
CandleType Тип и таймфрейм свечей для анализа. Таймфрейм 1 час
CciPeriod Период расчёта CCI. 49
AverageBodyPeriod Период усреднения длины тел свечей. 11
EntryConfirmationLevel Абсолютное значение CCI для подтверждения входа. 50
ExitLevel Абсолютное значение CCI для выхода. 80

Примечания

  • Стратегия обрабатывает только завершённые свечи.
  • Стоп-лосс и тейк-профит не выставляются автоматически: выход осуществляется исключительно по сигналам, как и в оригинальном советнике.
  • Для корректной работы необходимо задать минимальный шаг цены инструмента, поскольку условия свечных паттернов используют ценовые сравнения.
namespace StockSharp.Samples.Strategies;

using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Dark Cloud Piercing CCI strategy: trades Dark Cloud Cover and Piercing Line
/// candlestick patterns confirmed by CCI indicator levels.
/// </summary>
public class DarkCloudPiercingCciStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _cciPeriod;
	private readonly StrategyParam<decimal> _entryLevel;
	private readonly StrategyParam<int> _signalCooldownCandles;

	private readonly List<ICandleMessage> _candles = new();
	private decimal _prevCci;
	private bool _hasPrevCci;
	private int _candlesSinceTrade;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int CciPeriod { get => _cciPeriod.Value; set => _cciPeriod.Value = value; }
	public decimal EntryLevel { get => _entryLevel.Value; set => _entryLevel.Value = value; }
	public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }

	public DarkCloudPiercingCciStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_cciPeriod = Param(nameof(CciPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("CCI Period", "CCI period", "Indicators");
		_entryLevel = Param(nameof(EntryLevel), 50m)
			.SetDisplay("Entry Level", "CCI level for confirmation", "Signals");
		_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 6)
			.SetGreaterThanZero()
			.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_candles.Clear();
		_prevCci = 0m;
		_hasPrevCci = false;
		_candlesSinceTrade = SignalCooldownCandles;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_candles.Clear();
		_hasPrevCci = false;
		_candlesSinceTrade = SignalCooldownCandles;
		var cci = new CommodityChannelIndex { Length = CciPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(cci, ProcessCandle).Start();

		StartProtection(
			takeProfit: new Unit(2, UnitTypes.Percent),
			stopLoss: new Unit(1, UnitTypes.Percent)
		);
	}

	private void ProcessCandle(ICandleMessage candle, decimal cciValue)
	{
		if (candle.State != CandleStates.Finished) return;

		if (_candlesSinceTrade < SignalCooldownCandles)
			_candlesSinceTrade++;

		_candles.Add(candle);
		if (_candles.Count > 5)
			_candles.RemoveAt(0);

		if (_candles.Count >= 2 && _hasPrevCci)
		{
			var curr = _candles[^1];
			var prev = _candles[^2];

			// Piercing Line: prev bearish, curr bullish, curr opens below prev low, closes above midpoint
			var isPiercing = prev.OpenPrice > prev.ClosePrice
				&& curr.ClosePrice > curr.OpenPrice
				&& curr.OpenPrice < prev.LowPrice
				&& curr.ClosePrice > (prev.OpenPrice + prev.ClosePrice) / 2m;

			// Dark Cloud Cover: prev bullish, curr bearish, curr opens above prev high, closes below midpoint
			var isDarkCloud = prev.ClosePrice > prev.OpenPrice
				&& curr.OpenPrice > curr.ClosePrice
				&& curr.OpenPrice > prev.HighPrice
				&& curr.ClosePrice < (prev.OpenPrice + prev.ClosePrice) / 2m;

			if (isPiercing && cciValue < -EntryLevel && Position == 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				BuyMarket();
				_candlesSinceTrade = 0;
			}
			else if (isDarkCloud && cciValue > EntryLevel && Position == 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				SellMarket();
				_candlesSinceTrade = 0;
			}
		}

		_prevCci = cciValue;
		_hasPrevCci = true;
	}
}