This strategy is a C# port of the MetaTrader expert advisor Exp_XAng_Zad_C_Tm_MMRec. It trades adaptive price envelopes calculated by the custom XAng Zad C indicator and adds a time-based trading window together with a simple money-management recounter. The goal is to capture breakouts when the adaptive upper and lower lines cross each other while dynamically scaling position size after a configurable number of losing trades.
Core logic
Indicator – the XAng Zad C indicator produces an upper and lower adaptive channel. The C# version reproduces the envelope calculation and supports several moving-average smoothers (SMA, EMA, SMMA, LWMA). Exotic smoothers from the original script fall back to EMA.
Entry signals – when the previous candle shows the upper line above the lower line and the current bar closes with the upper line falling below the lower line, a bullish breakout is detected. The opposite configuration produces a bearish breakout. The SignalShift parameter defines how many closed candles back should be compared.
Exit signals – optional flags allow closing longs when the upper line moves back under the lower line and closing shorts on the inverse event. Positions are also closed immediately when the configured trading window ends.
Money management – the strategy keeps a list of historical trade results. If the most recent BuyLossTrigger (or SellLossTrigger) losing trades appear within the last BuyTotalTrigger (or SellTotalTrigger) trades, the next position uses the reduced volume. Otherwise the normal volume is restored.
Risk control – static stop loss and take profit targets are applied in multiples of the instrument price step. If either level is reached during the candle, the position is flattened at the corresponding price.
Parameters
Name
Description
NormalVolume
Default order size used when there is no recent losing streak.
ReducedVolume
Order size applied after a sequence of losing trades.
BuyTotalTrigger / SellTotalTrigger
Number of historical trades inspected when evaluating the loss counter.
BuyLossTrigger / SellLossTrigger
Required losing trades (within the above window) to switch to the reduced volume.
EnableBuyEntries / EnableSellEntries
Allow long or short entries.
EnableBuyExit / EnableSellExit
Allow automatic exit signals based on channel crossings.
UseTradingWindow
Enable the time filter. Outside the window all positions are closed and no new orders are submitted.
WindowStart / WindowEnd
Start and end times of the daily trading window (UTC). The window can span midnight.
StopLoss
Stop loss distance expressed in multiples of Security.PriceStep. Set to 0 to disable.
TakeProfit
Profit target distance expressed in multiples of Security.PriceStep. Set to 0 to disable.
SignalShift
Number of already closed candles used for the crossover comparison.
CandleType
Candle data type used for the indicator (default: 4-hour candles).
SmoothMethods
Moving-average smoother inside the indicator. Unsupported values automatically use EMA.
MaLength
Smoothing length for the indicator.
MaPhase
Additional phase parameter retained from the original indicator (currently informational).
Ki
Ratio controlling how quickly the adaptive envelopes react to price changes.
AppliedPrices
Price source used to feed the indicator (close, open, median, etc.).
Notes compared to the MQL5 version
MetaTrader money-management helpers relied on global trade history. The C# version tracks completed trades locally and applies the same trigger logic.
Lot sizing is expressed directly as strategy volume. Adjust NormalVolume/ReducedVolume to match the target quantity for your venue.
Time windows are configured with TimeSpan values. When WindowStart equals WindowEnd, trading is disabled (matching the zero-width window behaviour of the original script).
The strategy assumes full position reversals and does not keep partial positions from previous signals.
Unsupported smoothing types (JJMA, JurX, ParMA, T3, VIDYA, AMA) default to EMA. Consider extending CreateMovingAverage if you require a specific alternative.
Usage tips
Choose a candle type that matches the indicator timeframe used in MetaTrader (default: H4).
Tune stop-loss and take-profit distances based on the instrument tick size to approximate the point-based values from the original EA.
Optimise the money-management triggers to reflect the asset volatility and your risk tolerance.
Monitor indicator behaviour on a chart (upper/lower channel lines) to confirm that the reconstructed indicator matches expectations before live trading.
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>
/// XAng Zad strategy using SMA crossover with momentum recovery.
/// </summary>
public class XAngZadCTmMmRecStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal? _prevFast;
private decimal? _prevSlow;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
public XAngZadCTmMmRecStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast SMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 30)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow SMA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = null;
_prevSlow = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = null;
_prevSlow = null;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fast);
DrawIndicator(area, slow);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevFast = fastVal;
_prevSlow = slowVal;
return;
}
if (_prevFast == null || _prevSlow == null)
{
_prevFast = fastVal;
_prevSlow = slowVal;
return;
}
var prevAbove = _prevFast.Value > _prevSlow.Value;
var currAbove = fastVal > slowVal;
_prevFast = fastVal;
_prevSlow = slowVal;
if (!prevAbove && currAbove)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
}
else if (prevAbove && !currAbove)
{
if (Position > 0)
SellMarket();
if (Position >= 0)
SellMarket();
}
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class x_ang_zad_c_tm_mm_rec_strategy(Strategy):
def __init__(self):
super(x_ang_zad_c_tm_mm_rec_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_period = self.Param("FastPeriod", 10) \
.SetDisplay("Fast Period", "Fast SMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 30) \
.SetDisplay("Slow Period", "Slow SMA period", "Indicators")
self._prev_fast = None
self._prev_slow = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
def OnReseted(self):
super(x_ang_zad_c_tm_mm_rec_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(x_ang_zad_c_tm_mm_rec_strategy, self).OnStarted2(time)
self._prev_fast = None
self._prev_slow = None
fast = ExponentialMovingAverage()
fast.Length = self.FastPeriod
slow = ExponentialMovingAverage()
slow.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast, slow, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
if self._prev_fast is None or self._prev_slow is None:
self._prev_fast = fv
self._prev_slow = sv
return
prev_above = self._prev_fast > self._prev_slow
curr_above = fv > sv
self._prev_fast = fv
self._prev_slow = sv
if not prev_above and curr_above:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
elif prev_above and not curr_above:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return x_ang_zad_c_tm_mm_rec_strategy()