The CBC_WS_RSI Strategy is a high-level StockSharp implementation of the MQL5 expert advisor that combines the "Three White Soldiers" and "Three Black Crows" candlestick patterns with RSI confirmation. The strategy focuses on identifying strong multi-candle reversals and only enters a trade when market momentum, measured by RSI, agrees with the pattern. Exits are controlled by RSI threshold crossovers and optional risk management through stop-loss and take-profit protections.
The strategy subscribes to a configurable candle series and processes data exclusively on fully formed candles. All logic is implemented using StockSharp's high-level API (SubscribeCandles().Bind(...)) without direct access to indicator buffers.
Trading Logic
Long Setup
Detects three consecutive bullish candles forming the Three White Soldiers pattern:
Each candle closes above its open.
Every close is higher than the previous close.
The second and third candle open inside the body of the previous candle.
Confirms that the RSI value of the current candle is below or equal to the Long Confirmation level (default 40).
If the account is flat, the strategy buys Volume lots at market. If a short position exists, it is covered before opening a new long position.
Short Setup
Detects three consecutive bearish candles forming the Three Black Crows pattern:
Each candle closes below its open.
Every close is lower than the previous close.
The second and third candle open inside the body of the previous candle.
Confirms that the RSI value of the current candle is above or equal to the Short Confirmation level (default 60).
If the account is flat, the strategy sells Volume lots at market. If a long position exists, it is closed before opening a new short position.
Exit Rules
Close Longs: RSI crossing below either the Upper Exit level (default 70) or the Lower Exit level (default 30).
Close Shorts: RSI crossing above either the Lower Exit level (default 30) or the Upper Exit level (default 70).
Protection: Optional stop-loss and take-profit values can be defined as percentages of the entry price. When non-zero, they are managed via StartProtection.
All exit conditions use the most recent two RSI values to detect a level crossover, ensuring trades are closed as soon as momentum contradicts the active position.
Parameters
Name
Description
Default
CandleType
Candle data type and timeframe to subscribe to.
1-hour time frame
RsiPeriod
RSI period used for confirmation.
37
LongConfirmationLevel
Maximum RSI value that permits a long entry.
40
ShortConfirmationLevel
Minimum RSI value that permits a short entry.
60
LowerExitLevel
RSI level used to detect momentum reversal near oversold territory.
30
UpperExitLevel
RSI level used to detect momentum reversal near overbought territory.
70
StopLossPercent
Optional stop-loss in percent; 0 disables the protection.
1
TakeProfitPercent
Optional take-profit in percent; 0 disables the protection.
2
All numeric parameters can be optimized via the built-in optimizer thanks to SetCanOptimize(true).
Visualization
When a chart area is available, the strategy draws:
The selected candle series.
The RSI indicator.
Executed trades, making it easy to inspect pattern detections and exits.
Usage Notes
Ensure Volume is configured before starting the strategy.
Works on any instrument that supports OHLC candle data.
The pattern detection logic filters out doji-like candles by requiring non-zero candle bodies.
RSI confirmations protect against false signals during weak reversals, keeping the strategy aligned with momentum.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// CBC WS RSI strategy: 3 Black Crows / 3 White Soldiers with RSI confirmation.
/// Buys after 3 bullish candles with RSI below 60, sells after 3 bearish with RSI above 40.
/// </summary>
public class CbcWsRsiStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
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 RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }
public CbcWsRsiStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI 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 rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(rsi, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal rsi)
{
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 RSI extremes
if (Position > 0 && rsi > 75 && _candlesSinceTrade >= SignalCooldownCandles)
{
SellMarket();
_candlesSinceTrade = 0;
}
else if (Position < 0 && rsi < 25 && _candlesSinceTrade >= SignalCooldownCandles)
{
BuyMarket();
_candlesSinceTrade = 0;
}
// Entry on pattern
if (_bullCount >= 3 && rsi < 60 && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
BuyMarket();
_bullCount = 0;
_candlesSinceTrade = 0;
}
else if (_bearCount >= 3 && rsi > 40 && 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 RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class cbc_ws_rsi_strategy(Strategy):
def __init__(self):
super(cbc_ws_rsi_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI period for confirmation", "Indicators")
self._signal_cooldown = self.Param("SignalCooldownCandles", 6) \
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading")
self._rsi = None
self._bull_count = 0
self._bear_count = 0
self._candles_since_trade = 0
@property
def rsi_period(self):
return self._rsi_period.Value
@property
def signal_cooldown(self):
return self._signal_cooldown.Value
def OnReseted(self):
super(cbc_ws_rsi_strategy, self).OnReseted()
self._rsi = None
self._bull_count = 0
self._bear_count = 0
self._candles_since_trade = self.signal_cooldown
def OnStarted2(self, time):
super(cbc_ws_rsi_strategy, self).OnStarted2(time)
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.rsi_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._rsi, self._process_candle)
subscription.Start()
def _process_candle(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
if not self._rsi.IsFormed:
return
rsi_val = float(rsi_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 rsi_val > 75.0 and self._candles_since_trade >= self.signal_cooldown:
self.SellMarket()
self._candles_since_trade = 0
elif self.Position < 0 and rsi_val < 25.0 and self._candles_since_trade >= self.signal_cooldown:
self.BuyMarket()
self._candles_since_trade = 0
if self._bull_count >= 3 and rsi_val < 60.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 rsi_val > 40.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 cbc_ws_rsi_strategy()