Ver no GitHub

Dark Cloud Piercing CCI Strategy

Overview

This strategy is a StockSharp port of the MetaTrader Expert_ADC_PL_CCI advisor. It scans the price action for Piercing Line and Dark Cloud Cover candlestick reversals and uses the Commodity Channel Index (CCI) as confirmation. Once a valid pattern is detected together with an extreme CCI reading, the strategy opens a market position in the direction of the reversal and later exits when the CCI moves out of its extreme zone.

Indicators

  • Commodity Channel Index (CCI): confirms momentum extremes and produces the exit conditions.
  • Average body length (SMA): measures candle body size to validate “long” candles inside the pattern definition.
  • Average close price (SMA): acts as a simple trend filter that mirrors the moving average used in the original MQL logic.

Trading Rules

Entry

  • Bullish signal (Piercing Line):
    1. Previous candle must be a long bearish candle that opens above its close.
    2. Latest candle must be a long bullish candle that opens below the previous low and closes within the previous body, above its midpoint but below the prior open.
    3. The midpoint of the older candle has to be below the moving average to confirm a short-term downtrend.
    4. The most recent completed CCI value must be less than or equal to -EntryConfirmationLevel (default 50).
    5. If a short position exists it is fully closed before entering long.
  • Bearish signal (Dark Cloud Cover): mirrored logic of the bullish signal with a long bullish candle followed by a long bearish candle that gaps up, penetrates the prior body, and closes below its midpoint while CCI is greater than or equal to EntryConfirmationLevel.

Exit

  • Long positions: closed when the CCI crosses down below ExitLevel or crosses down below -ExitLevel from above, signalling that momentum has normalised.
  • Short positions: closed when the CCI crosses up above -ExitLevel or above ExitLevel from below.

Position Sizing

  • Uses the base Volume property. When the signal requires reversing an existing position the strategy automatically adds the absolute size of the current position to the order volume, ensuring a full flip.

Parameters

Name Description Default
CandleType Candle type and timeframe used for detection. 1H time frame
CciPeriod Lookback length of the Commodity Channel Index. 49
AverageBodyPeriod Number of candles for the body-size moving average. 11
EntryConfirmationLevel Absolute CCI level that validates pattern entries. 50
ExitLevel Absolute CCI level that triggers position exits. 80

Notes

  • The strategy processes only finished candles and ignores partial updates.
  • No stop-loss or take-profit orders are set automatically; exits are purely signal based as in the original expert advisor.
  • Ensure the instrument has a price step configured because the equality tolerance of the candlestick logic depends on the security settings.
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;
	}
}