The AbcWsCci Strategy combines two classic Japanese candlestick reversal patterns — Three White Soldiers and Three Black Crows — with the Commodity Channel Index (CCI) indicator for confirmation. The system scans finished candles, measures the body size relative to a moving average baseline, and opens trades only when strong multi-candle momentum aligns with CCI extremes. Position exits are triggered when the CCI leaves the extreme zones, signalling that momentum is fading.
Trading Logic
Maintain a moving average of candle body sizes to qualify “long” candles.
Detect the Three White Soldiers pattern (three consecutive strong bullish candles with rising midpoints).
Detect the Three Black Crows pattern (three consecutive strong bearish candles with falling midpoints).
Confirm bullish entries with CCI dropping below -50 and bearish entries with CCI rising above 50.
Close long positions when CCI crosses out of the -80 or 80 levels, and close short positions on the mirrored conditions.
Parameters
Name
Description
Default
CciPeriod
Length of the CCI indicator used for confirmation.
37
BodyAveragePeriod
Number of candles in the moving average that defines the minimum “strong” body size.
13
CandleType
Candle time frame used for pattern detection.
1 hour
Indicators
Commodity Channel Index (CCI): Evaluates momentum extremes for confirmation and exit signals.
Simple Moving Average of candle bodies: Establishes the minimum candle size required for a valid pattern.
Position Management
Enter long when Three White Soldiers form and CCI is below -50 while no long position is active.
Enter short when Three Black Crows form and CCI is above 50 while no short position is active.
Exit long positions when CCI leaves the -80/80 band, indicating the bullish impulse is exhausted.
Exit short positions when CCI leaves the +80/-80 band, signalling bearish momentum loss.
Usage Notes
The strategy is event-driven: only fully completed candles are processed.
Works best on trending instruments where multi-candle momentum combined with oscillator extremes provides reliable signals.
Consider combining with additional risk management rules (stop-loss, position sizing) depending on your trading environment.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// ABC WS CCI strategy: 3 white soldiers / 3 black crows pattern with CCI confirmation.
/// Buys after 3 bullish candles with CCI below 100, sells after 3 bearish candles with CCI above -100.
/// </summary>
public class AbcWsCciStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<int> _signalCooldownCandles;
private int _bullCount;
private int _bearCount;
private int _candlesSinceTrade;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int CciPeriod { get => _cciPeriod.Value; set => _cciPeriod.Value = value; }
public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }
public AbcWsCciStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_cciPeriod = Param(nameof(CciPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("CCI Period", "CCI period for confirmation", "Indicators");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 6)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_bullCount = 0;
_bearCount = 0;
_candlesSinceTrade = SignalCooldownCandles;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_bullCount = 0;
_bearCount = 0;
_candlesSinceTrade = SignalCooldownCandles;
var cci = new CommodityChannelIndex { Length = CciPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(cci, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal cci)
{
if (candle.State != CandleStates.Finished) return;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
if (candle.ClosePrice > candle.OpenPrice)
{
_bullCount++;
_bearCount = 0;
}
else if (candle.ClosePrice < candle.OpenPrice)
{
_bearCount++;
_bullCount = 0;
}
else
{
_bullCount = 0;
_bearCount = 0;
}
// Exit on CCI reversal
if (Position > 0 && cci > 200 && _candlesSinceTrade >= SignalCooldownCandles)
{
SellMarket();
_candlesSinceTrade = 0;
}
else if (Position < 0 && cci < -200 && _candlesSinceTrade >= SignalCooldownCandles)
{
BuyMarket();
_candlesSinceTrade = 0;
}
// Entry on 3 soldiers/crows pattern
if (_bullCount >= 3 && cci < 100 && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
BuyMarket();
_bullCount = 0;
_candlesSinceTrade = 0;
}
else if (_bearCount >= 3 && cci > -100 && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
SellMarket();
_bearCount = 0;
_candlesSinceTrade = 0;
}
}
}
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
from StockSharp.Algo.Indicators import CommodityChannelIndex
from StockSharp.Algo.Strategies import Strategy
class abc_ws_cci_strategy(Strategy):
def __init__(self):
super(abc_ws_cci_strategy, self).__init__()
self._cci_period = self.Param("CciPeriod", 14) \
.SetDisplay("CCI Period", "CCI period for confirmation", "Indicators")
self._signal_cooldown = self.Param("SignalCooldownCandles", 6) \
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading")
self._cci = None
self._bull_count = 0
self._bear_count = 0
self._candles_since_trade = 0
@property
def cci_period(self):
return self._cci_period.Value
@property
def signal_cooldown(self):
return self._signal_cooldown.Value
def OnReseted(self):
super(abc_ws_cci_strategy, self).OnReseted()
self._cci = None
self._bull_count = 0
self._bear_count = 0
self._candles_since_trade = self.signal_cooldown
def OnStarted2(self, time):
super(abc_ws_cci_strategy, self).OnStarted2(time)
self._cci = CommodityChannelIndex()
self._cci.Length = self.cci_period
self._bull_count = 0
self._bear_count = 0
self._candles_since_trade = self.signal_cooldown
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(60)))
subscription.Bind(self._cci, self._process_candle)
subscription.Start()
def _process_candle(self, candle, cci_value):
if candle.State != CandleStates.Finished:
return
if not self._cci.IsFormed:
return
cci_val = float(cci_value)
if self._candles_since_trade < self.signal_cooldown:
self._candles_since_trade += 1
close = float(candle.ClosePrice)
open_p = float(candle.OpenPrice)
if close > open_p:
self._bull_count += 1
self._bear_count = 0
elif close < open_p:
self._bear_count += 1
self._bull_count = 0
else:
self._bull_count = 0
self._bear_count = 0
if self.Position > 0 and cci_val > 200.0 and self._candles_since_trade >= self.signal_cooldown:
self.SellMarket()
self._candles_since_trade = 0
elif self.Position < 0 and cci_val < -200.0 and self._candles_since_trade >= self.signal_cooldown:
self.BuyMarket()
self._candles_since_trade = 0
if self._bull_count >= 3 and cci_val < 100.0 and self.Position <= 0 and self._candles_since_trade >= self.signal_cooldown:
self.BuyMarket()
self._bull_count = 0
self._candles_since_trade = 0
elif self._bear_count >= 3 and cci_val > -100.0 and self.Position >= 0 and self._candles_since_trade >= self.signal_cooldown:
self.SellMarket()
self._bear_count = 0
self._candles_since_trade = 0
def CreateClone(self):
return abc_ws_cci_strategy()