The strategy replicates the logic of the Caudate X Period Candle TM Plus expert advisor. It smooths the candle open, high, low and close prices with a configurable moving average, builds a Donchian-style range and classifies each finished candle into one of six color codes depending on the position of the body inside the range. Long entries are triggered by the bullish lower-tail colors (0 or 1), while short entries are triggered by the bearish upper-tail colors (5 or 6). Opposite color groups are used to exit existing positions.
Trading rules
Subscribe to the selected candle series and smooth each component with the chosen moving average.
Compute the highest high and lowest low of the smoothed highs and lows over the specified Donchian Period, then expand the range so that it always contains the smoothed open and close.
Determine the candle color:
Colors 0/1 – body near the top of the range (lower tail).
Colors 2/4 – body centered inside the range.
Colors 5/6 – body near the bottom of the range (upper tail).
Evaluate the color of the bar offset by Signal Bar (default 1 uses the previous completed candle).
Open positions when the color belongs to the entry group and the opposite position is not active.
Close positions when the color belongs to the exit group or the maximum holding time expires.
Optional stop-loss and take-profit offsets are set through the built-in protection module.
Parameters
Parameter
Description
Candle Type
Time frame used for signal calculations.
Donchian Period
Number of candles for the smoothed high/low range.
Signal Bar
Number of bars to delay signal evaluation (0 = current bar).
Smoothing Method
Moving average applied to OHLC prices (SMA, EMA, SMMA, LWMA, Jurik JJMA approximation, Kaufman AMA).
MA Length
Length of the smoothing filter.
MA Phase
Reserved for JJMA compatibility (not used by StockSharp averages).
Enable Long/Short Entries
Toggle opening new long or short positions.
Enable Long/Short Exits
Toggle closing existing long or short positions on signals.
Enable Time Exit
Enable the maximum holding time filter.
Time Exit (minutes)
Holding duration before a forced exit.
Stop Loss (points)
Stop-loss distance in price steps (multiplied by Security.PriceStep).
Take Profit (points)
Take-profit distance in price steps.
Notes
Signal Bar = 1 matches the MQL5 expert behaviour by acting on the last fully closed candle.
When stop or target distances are greater than zero the strategy calls StartProtection with absolute offsets based on the instrument price step.
MA Phase is kept for compatibility but is not consumed by the StockSharp moving-average implementations.
Set the base order size through the inherited Strategy.Volume property; the implementation always closes opposite positions before opening a new one.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Caudate X Period Candle TM Plus strategy (simplified). Detects candle body/tail
/// patterns using ATR-based filtering and EMA trend direction.
/// </summary>
public class CaudateXPeriodCandleTmPlusStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<int> _emaLength;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public CaudateXPeriodCandleTmPlusStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candles", "General");
_atrLength = Param(nameof(AtrLength), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Length", "ATR period", "Indicators");
_emaLength = Param(nameof(EmaLength), 20)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "Trend EMA period", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var atr = new AverageTrueRange { Length = AtrLength };
var ema = new ExponentialMovingAverage { Length = EmaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, ema, (ICandleMessage candle, decimal atrValue, decimal emaValue) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (atrValue <= 0)
return;
var close = candle.ClosePrice;
var body = Math.Abs(close - candle.OpenPrice);
// Strong body candle in the trend direction.
if (body > atrValue * 0.75m)
{
if (close > candle.OpenPrice && close > emaValue && Position <= 0)
BuyMarket();
else if (close < candle.OpenPrice && close < emaValue && Position >= 0)
SellMarket();
}
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import AverageTrueRange, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class caudate_x_period_candle_tm_plus_strategy(Strategy):
def __init__(self):
super(caudate_x_period_candle_tm_plus_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Candles", "General")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period", "Indicators")
self._ema_length = self.Param("EmaLength", 20) \
.SetDisplay("EMA Length", "Trend EMA period", "Indicators")
@property
def CandleType(self):
return self._candle_type.Value
@property
def AtrLength(self):
return self._atr_length.Value
@property
def EmaLength(self):
return self._ema_length.Value
def OnStarted2(self, time):
super(caudate_x_period_candle_tm_plus_strategy, self).OnStarted2(time)
atr = AverageTrueRange()
atr.Length = self.AtrLength
ema = ExponentialMovingAverage()
ema.Length = self.EmaLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(atr, ema, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, atr_value, ema_value):
if candle.State != CandleStates.Finished:
return
av = float(atr_value)
if av <= 0:
return
close = float(candle.ClosePrice)
open_p = float(candle.OpenPrice)
ev = float(ema_value)
body = abs(close - open_p)
if body > av * 0.75:
if close > open_p and close > ev and self.Position <= 0:
self.BuyMarket()
elif close < open_p and close < ev and self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return caudate_x_period_candle_tm_plus_strategy()