This strategy is a StockSharp port of the MetaTrader Expert_ADC_PL_CCI advisor. Escanea la acción del precio en busca de reversiones de velas de Piercing Line y Dark Cloud Cover y utiliza el índice del canal de productos básicos (CCI) como confirmación. Una vez que se detecta un patrón válido junto con una lectura extrema de CCI, la estrategia abre una posición de mercado en la dirección de la reversión y luego sale cuando el CCI sale de su zona extrema.
Indicadores
Índice del canal de productos básicos (CCI): confirma los extremos del impulso y produce las condiciones de salida.
Longitud promedio del cuerpo (SMA): mide el tamaño del cuerpo de la vela para validar velas "largas" dentro de la definición del patrón.
Average close price (SMA): acts as a simple trend filter that mirrors the moving average used in the original MQL logic.
Reglas de trading
Entrada
Bullish signal (Piercing Line):
Previous candle must be a long bearish candle that opens above its close.
La última vela debe ser una vela alcista larga que se abra por debajo del mínimo anterior y cierre dentro del cuerpo anterior, por encima de su punto medio pero por debajo de la apertura anterior.
The midpoint of the older candle has to be below the moving average to confirm a short-term downtrend.
The most recent completed CCI value must be less than or equal to -EntryConfirmationLevel (default 50).
If a short position exists it is fully closed before entering long.
Señal bajista (Cubierta de nubes oscuras): lógica reflejada de la señal alcista con una vela alcista larga seguida de una vela bajista larga que se abre, penetra el cuerpo anterior y cierra por debajo de su punto medio mientras CCI es mayor o igual a EntryConfirmationLevel.
Salir
Posiciones largas: se cierran cuando el CCI cruza por debajo de ExitLevel o cruza por debajo de -ExitLevel desde arriba, lo que indica que el impulso se ha normalizado.
Short positions: closed when the CCI crosses up above -ExitLevel or above ExitLevel from below.
Dimensionamiento de posiciones
Uses the base Volume property. Cuando la señal requiere revertir una posición existente, la estrategia agrega automáticamente el tamaño absoluto de la posición actual al volumen de la orden, asegurando un cambio completo.
Parámetros
Nombre
Descripción
Predeterminado
CandleType
Tipo de vela y período de tiempo utilizado para la detección.
1H período de tiempo
CciPeriod
Longitud retrospectiva del índice del canal de productos básicos.
49
AverageBodyPeriod
Número de velas para la media móvil del tamaño corporal.
11
EntryConfirmationLevel
Absolute CCI level that validates pattern entries.
50
ExitLevel
Absolute CCI level that triggers position exits.
80
Notas
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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import CommodityChannelIndex
from StockSharp.Algo.Strategies import Strategy
class dark_cloud_piercing_cci_strategy(Strategy):
def __init__(self):
super(dark_cloud_piercing_cci_strategy, self).__init__()
self._cci_period = self.Param("CciPeriod", 14) \
.SetDisplay("CCI Period", "CCI period", "Indicators")
self._entry_level = self.Param("EntryLevel", 50.0) \
.SetDisplay("Entry Level", "CCI level for confirmation", "Signals")
self._signal_cooldown = self.Param("SignalCooldownCandles", 6) \
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading")
self._cci = None
self._candles = []
self._has_prev_cci = False
self._candles_since_trade = 0
@property
def cci_period(self):
return self._cci_period.Value
@property
def entry_level(self):
return self._entry_level.Value
@property
def signal_cooldown(self):
return self._signal_cooldown.Value
def OnReseted(self):
super(dark_cloud_piercing_cci_strategy, self).OnReseted()
self._cci = None
self._candles = []
self._has_prev_cci = False
self._candles_since_trade = self.signal_cooldown
def OnStarted2(self, time):
super(dark_cloud_piercing_cci_strategy, self).OnStarted2(time)
self._cci = CommodityChannelIndex()
self._cci.Length = self.cci_period
self._candles = []
self._has_prev_cci = False
self._candles_since_trade = self.signal_cooldown
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._cci, self._process_candle)
subscription.Start()
self.StartProtection(takeProfit=Unit(2, UnitTypes.Percent), stopLoss=Unit(1, UnitTypes.Percent))
def _process_candle(self, candle, cci_value):
if candle.State != CandleStates.Finished:
return
cci_val = float(cci_value)
if self._candles_since_trade < self.signal_cooldown:
self._candles_since_trade += 1
self._candles.append(candle)
if len(self._candles) > 5:
self._candles.pop(0)
if len(self._candles) >= 2 and self._has_prev_cci:
curr = self._candles[-1]
prev = self._candles[-2]
is_piercing = (float(prev.OpenPrice) > float(prev.ClosePrice)
and float(curr.ClosePrice) > float(curr.OpenPrice)
and float(curr.OpenPrice) < float(prev.LowPrice)
and float(curr.ClosePrice) > (float(prev.OpenPrice) + float(prev.ClosePrice)) / 2.0)
is_dark_cloud = (float(prev.ClosePrice) > float(prev.OpenPrice)
and float(curr.OpenPrice) > float(curr.ClosePrice)
and float(curr.OpenPrice) > float(prev.HighPrice)
and float(curr.ClosePrice) < (float(prev.OpenPrice) + float(prev.ClosePrice)) / 2.0)
if is_piercing and cci_val < -self.entry_level and self.Position == 0 and self._candles_since_trade >= self.signal_cooldown:
self.BuyMarket()
self._candles_since_trade = 0
elif is_dark_cloud and cci_val > self.entry_level and self.Position == 0 and self._candles_since_trade >= self.signal_cooldown:
self.SellMarket()
self._candles_since_trade = 0
self._has_prev_cci = True
def CreateClone(self):
return dark_cloud_piercing_cci_strategy()