AbsolutelyNoLagLWMA Range Channel TM Plus Strategy
Overview
This strategy is a direct port of the MetaTrader expert "Exp_AbsolutelyNoLagLwma_Range_Channel_Tm_Plus". It trades a price channel derived from a double-smoothed linear weighted moving average (LWMA) of the candle highs and lows. The StockSharp version keeps the original behaviour: signals are evaluated on finished candles of a selectable timeframe, the channel state is encoded in the same way as the MQL indicator, and position management follows the same priority order (time exit first, indicator exits second, new entries last).
Indicator construction
For every finished candle the high and low series are pushed into a first LWMA. The length parameter is shared between the high and low streams.
The output of the first LWMA is smoothed again with another LWMA of the same length. This recreates the "AbsolutelyNoLagLWMA" smoothing used by the original indicator.
The final upper and lower channel values are compared with the candle close:
Close above the upper line → bullish breakout state.
Close below the lower line → bearish breakout state.
Close inside the channel → neutral state.
The strategy stores the most recent channel states. The SignalBar parameter controls which bar index is checked for signal generation (0 = last closed candle, 1 = one bar back, etc.), matching the SignalBar input of the MQL program.
Signal interpretation
Long entry – enabled by EnableBuyEntries. The strategy looks for a bullish breakout on the bar indexed by SignalBar + 1 while the bar at SignalBar has already returned inside the channel. The behaviour replicates the original “previous bar breakout” test.
Short entry – enabled by EnableSellEntries. Mirrors the long logic for bearish breakouts.
Long exit – enabled by EnableBuyExits. A bearish breakout on the reference bar closes existing long positions, unless they were already closed by the time-based exit on the current candle.
Short exit – enabled by EnableSellExits. A bullish breakout on the reference bar closes open shorts, unless the time-based exit already requested the close.
Trade management
Order volume – taken from the OrderVolume parameter. Reversal orders automatically add the absolute value of the current position to avoid residual exposure.
Stop loss / Take profit – optional absolute offsets defined in instrument points (StopLossPoints, TakeProfitPoints). When positive they are converted to price offsets using the instrument PriceStep and passed to StartProtection.
Time-based exit – the original EA closes positions that exceed a holding time (TimeTrade, nTime). In StockSharp this is handled by UseTimeExit and HoldingLimit. The exit is evaluated before indicator signals on every finished candle.
Position timing – the strategy records the timestamp of the last trade that resulted in a long or short position. These timestamps are used for the time-based exit.
Parameters
Parameter
Description
Length
Length of both LWMA passes that shape the channel.
SignalBar
Shift of the bar examined for signals (0 = last closed candle).
CandleType
Timeframe used for the indicator and trade evaluation.
OrderVolume
Volume used when submitting new entry orders.
StopLossPoints
Stop-loss distance in instrument points (0 disables the stop).
TakeProfitPoints
Take-profit distance in instrument points (0 disables the target).
EnableBuyEntries
Allow or forbid new long positions.
EnableSellEntries
Allow or forbid new short positions.
EnableBuyExits
Allow the indicator to close long positions.
EnableSellExits
Allow the indicator to close short positions.
UseTimeExit
Enable closing positions after HoldingLimit elapses.
HoldingLimit
Maximum holding time before the time exit is triggered.
Notes
The channel is computed from the candle highs and lows exactly like the bundled MQL indicator AbsolutelyNoLagLwma_Range_Channel.
The strategy ignores unfinished candles and works only with completed data to avoid premature signals.
Setting SignalBar to 0 matches the typical MT5 configuration where the last closed candle is analysed. Higher values reproduce the delayed confirmation used by the default EA (SignalBar = 1).
If PriceStep is not available for the selected instrument the stop-loss and take-profit offsets are ignored, preserving the behaviour of zero-valued inputs in the original script.
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>
/// LWMA range channel breakout strategy.
/// Uses fast/slow WMA crossover with mid-channel exit logic.
/// </summary>
public class AbsolutelyNoLagLwmaRangeChannelTmPlusStrategy : 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 AbsolutelyNoLagLwmaRangeChannelTmPlusStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast WMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 30)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow WMA 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 fastWma = new WeightedMovingAverage { Length = FastPeriod };
var slowWma = new WeightedMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastWma, slowWma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastWma);
DrawIndicator(area, slowWma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevFast = fast;
_prevSlow = slow;
return;
}
if (_prevFast == null || _prevSlow == null)
{
_prevFast = fast;
_prevSlow = slow;
return;
}
var prevAbove = _prevFast.Value > _prevSlow.Value;
var currAbove = fast > slow;
_prevFast = fast;
_prevSlow = slow;
// Crossover: fast crosses above slow → buy
if (!prevAbove && currAbove)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
}
// Crossover: fast crosses below slow → sell
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 WeightedMovingAverage
from StockSharp.Algo.Strategies import Strategy
class absolutely_no_lag_lwma_range_channel_tm_plus_strategy(Strategy):
def __init__(self):
super(absolutely_no_lag_lwma_range_channel_tm_plus_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 WMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 30) \
.SetDisplay("Slow Period", "Slow WMA 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(absolutely_no_lag_lwma_range_channel_tm_plus_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(absolutely_no_lag_lwma_range_channel_tm_plus_strategy, self).OnStarted2(time)
self._prev_fast = None
self._prev_slow = None
fast_wma = WeightedMovingAverage()
fast_wma.Length = self.FastPeriod
slow_wma = WeightedMovingAverage()
slow_wma.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast_wma, slow_wma, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_wma)
self.DrawIndicator(area, slow_wma)
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 absolutely_no_lag_lwma_range_channel_tm_plus_strategy()