This strategy is a StockSharp port of the MetaTrader expert advisor Exp_XPeriodCandleSystem_Tm_Plus. The original robot relies on the custom XPeriod Candle System indicator that smooths candle data and colors bars according to Bollinger Band breakouts. The translated version reproduces this behaviour by applying exponential smoothing to the OHLC series, mapping the same applied price modes, and driving trades from the resulting color states. A time-based exit and configurable protective orders complement the breakout logic.
Trading Logic
Smoothed candles – Exponential moving averages with configurable length build synthetic open, high, low, and close values that approximate the source indicator.
Applied price – The user can select any of the twelve price formulas (close, open, median, trend-following variations, Demark, etc.) before feeding data into the Bollinger Bands.
Band analysis – A Bollinger Bands indicator (length and deviation configurable) processes the smoothed price series. Finished bands are required before signals are evaluated.
Color states –
Bullish bar above the upper band → color 0 (breakout).
Bearish bar below the lower band → color 4 (breakdown).
Other bullish bars → color 1; other bearish bars → color 3.
A configurable breakout offset (converted to price units using the symbol tick size when possible) avoids false triggers.
Entries – The strategy looks at the candle defined by SignalBar and its predecessor:
Open long when the previous bar was a bullish breakout (0) and the signal bar is not.
Open short when the previous bar was a bearish breakout (4) and the signal bar is not.
Exits –
Close longs when the reference bar is bearish (> 2).
Close shorts when the reference bar is bullish (< 2).
Optional holding timer (TimeTrade and HoldingMinutes) closes positions after the specified minutes.
Risk – StartProtection deploys optional absolute take-profit and stop-loss distances for every trade.
Parameters
Parameter
Description
Default
OrderVolume
Base order size used for market entries.
0.1
BuyPosOpen / SellPosOpen
Enable/disable long or short entries.
true
BuyPosClose / SellPosClose
Allow long or short position exits.
true
TimeTrade
Enables the time-based exit filter.
true
HoldingMinutes
Maximum holding time before the time filter closes a position.
960
CandleType
Candle data type (time frame) requested from the market.
4 hours
Period
Length of the smoothing exponential moving averages.
5
BollingerLength
Number of smoothed bars inside the Bollinger calculation window.
20
BandsDeviation
Band width multiplier.
1.001
AppliedPriceMode
Price transformation used before the Bollinger indicator (close, open, median, trend-following, Demark, etc.).
Close
SignalBar
Index of the bar used for signal evaluation (1 = last closed bar).
1
StopLoss / TakeProfit
Absolute distances (in price units) used by the protective engine.
1000 / 2000
Deviation
Extra breakout offset added above/below the Bollinger bands.
10
Usage Notes
The smoothing step uses exponential moving averages to replicate the proprietary XPeriod calculation. Smaller periods keep the synthetic candles closer to market prices, while larger periods emphasise trend structure.
SignalBar must remain within the stored history (up to 14 positions after the current bar). Values greater than the available history will automatically skip trading.
The breakout offset is multiplied by PriceStep when the security exposes a tick size. This keeps the behaviour similar to the MetaTrader version where Deviation is defined in points.
StopLoss and TakeProfit are specified in absolute price units. Set them to zero to disable protective orders while keeping the management infrastructure active.
No Python translation is provided yet; this folder contains only the C# implementation.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// XPeriod candle system with Bollinger Bands breakout.
/// Buys on close above upper band with bullish candle, sells on close below lower band with bearish candle.
/// </summary>
public class XPeriodCandleSystemTmPlusStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _bbPeriod;
private readonly StrategyParam<decimal> _bbWidth;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int BbPeriod
{
get => _bbPeriod.Value;
set => _bbPeriod.Value = value;
}
public decimal BbWidth
{
get => _bbWidth.Value;
set => _bbWidth.Value = value;
}
public XPeriodCandleSystemTmPlusStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_bbPeriod = Param(nameof(BbPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("BB Period", "Bollinger Bands period", "Indicators");
_bbWidth = Param(nameof(BbWidth), 2m)
.SetGreaterThanZero()
.SetDisplay("BB Width", "Bollinger Bands width", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var bb = new BollingerBands { Length = BbPeriod, Width = BbWidth };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(bb, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, bb);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue value)
{
if (candle.State != CandleStates.Finished)
return;
var bb = (BollingerBandsValue)value;
if (bb.UpBand is not decimal upper ||
bb.LowBand is not decimal lower ||
bb.MovingAverage is not decimal middle)
return;
var close = candle.ClosePrice;
var isBullish = candle.ClosePrice > candle.OpenPrice;
var isBearish = candle.ClosePrice < candle.OpenPrice;
// Buy: close above upper band with bullish candle
if (close > upper && isBullish && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Sell: close below lower band with bearish candle
else if (close < lower && isBearish && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
// Exit long at middle band
else if (Position > 0 && close < middle)
{
SellMarket();
}
// Exit short at middle band
else if (Position < 0 && close > middle)
{
BuyMarket();
}
}
}
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 BollingerBands
from StockSharp.Algo.Strategies import Strategy
class x_period_candle_system_tm_plus_strategy(Strategy):
def __init__(self):
super(x_period_candle_system_tm_plus_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._bb_period = self.Param("BbPeriod", 20) \
.SetDisplay("BB Period", "Bollinger Bands period", "Indicators")
self._bb_width = self.Param("BbWidth", 2.0) \
.SetDisplay("BB Width", "Bollinger Bands width", "Indicators")
@property
def CandleType(self):
return self._candle_type.Value
@property
def BbPeriod(self):
return self._bb_period.Value
@property
def BbWidth(self):
return self._bb_width.Value
def OnStarted2(self, time):
super(x_period_candle_system_tm_plus_strategy, self).OnStarted2(time)
bb = BollingerBands()
bb.Length = self.BbPeriod
bb.Width = self.BbWidth
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(bb, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, bb)
self.DrawOwnTrades(area)
def _on_process(self, candle, bb_value):
if candle.State != CandleStates.Finished:
return
upper = bb_value.UpBand
lower = bb_value.LowBand
middle = bb_value.MovingAverage
if upper is None or lower is None or middle is None:
return
upper = float(upper)
lower = float(lower)
middle = float(middle)
close = float(candle.ClosePrice)
is_bullish = float(candle.ClosePrice) > float(candle.OpenPrice)
is_bearish = float(candle.ClosePrice) < float(candle.OpenPrice)
# Buy: close above upper band with bullish candle
if close > upper and is_bullish and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
# Sell: close below lower band with bearish candle
elif close < lower and is_bearish and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
# Exit long at middle band
elif self.Position > 0 and close < middle:
self.SellMarket()
# Exit short at middle band
elif self.Position < 0 and close > middle:
self.BuyMarket()
def CreateClone(self):
return x_period_candle_system_tm_plus_strategy()