This strategy is a StockSharp port of the MetaTrader expert advisor Exp_AltrTrend_Signal_v2_2. It recreates the adaptive
channel logic of the original AltrTrend Signal indicator and executes trades on delayed bars just like the MQL5 version. The
ADX value contracts or widens the channel so that breakouts only fire when trend strength supports them.
How it works
A dynamic channel is calculated on every completed candle of the configured timeframe. The channel width is defined by the
highest and lowest price inside a lookback that expands or contracts according to the previous ADX value (KPeriod / ADX).
The inner boundaries (smin, smax) are pulled toward the center by KPercent. Price must close outside these inner
bounds to establish a directional trend state.
When the trend flips from bearish to bullish and the close is above the upper bound, a buy signal is generated. A bearish
flip below the lower bound issues a sell signal. Signals are executed on the bar defined by the SignalBar delay, matching
the original expert advisor behaviour.
Optional stop-loss and take-profit levels are mapped from points to price steps so that protective exits mimic the original
order placement with fixed SL/TP values.
Details
Entry Criteria:
Long: Previous trend was bearish or neutral, price closes above the upper contracted bound, and long entries are
enabled. Short positions can be closed automatically if allowed.
Short: Previous trend was bullish or neutral, price closes below the lower contracted bound, and short entries are
enabled. Long positions can be closed automatically if allowed.
Exit Criteria:
Opposite breakout signal when exits are permitted for the current direction.
Stop-loss or take-profit distances expressed in price steps.
Long/Short: Dual direction with independent enable/disable switches for entries and exits.
Risk Management:
StopLossPoints and TakeProfitPoints replicate the original MM module by applying distance-based exits after market
orders are filled.
Indicator Settings:
KPercent controls how much the channel edges are contracted toward the mid-range.
KStop keeps the original arrow projection value for charting and logging.
KPeriod is the base lookback before ADX modulation.
AdxPeriod sets the length of the Average Directional Index that adapts the channel width.
SignalBar delays order execution by the specified number of completed candles.
Recommended Markets:
Works best on instruments with clear swing phases where trend strength varies over time (forex majors, gold, and index
futures). Default timeframe is H1 as in the MQL5 template.
Parameters
Parameter
Description
CandleType
Timeframe used to build the adaptive channel.
KPercent
Percentage pulling the inner channel boundaries inward.
KStop
Multiplier for projected arrow prices (kept for compatibility).
KPeriod
Base number of candles examined before ADX adjustment.
AdxPeriod
Period for the Average Directional Index driving channel width.
SignalBar
Number of completed candles to wait before executing a signal.
AllowBuyEntries / AllowSellEntries
Enable or disable opening positions in each direction.
AllowBuyExits / AllowSellExits
Allow automatic closing of positions on opposite signals.
StopLossPoints
Stop-loss distance measured in price steps (0 disables).
TakeProfitPoints
Take-profit distance measured in price steps (0 disables).
This port keeps the discretionary switches and risk parameters of the original expert advisor, making it easy to reproduce the
same behaviour inside StockSharp Designer, Shell, or Runner.
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>
/// AltrTrend signal strategy using EMA crossover for alternating trend detection.
/// </summary>
public class AltrTrendSignalStrategy : 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 AltrTrendSignalStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 7)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow EMA 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 altr_trend_signal_strategy(Strategy):
def __init__(self):
super(altr_trend_signal_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", 7) \
.SetDisplay("Fast Period", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 14) \
.SetDisplay("Slow Period", "Slow EMA 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(altr_trend_signal_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(altr_trend_signal_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 altr_trend_signal_strategy()