Стратегия AbsolutelyNoLagLWMA Range Channel TM Plus
Общее описание
Стратегия является портом эксперт-советника MetaTrader «Exp_AbsolutelyNoLagLwma_Range_Channel_Tm_Plus». Она торгует канал, построенный на двойном сглаживании максимумов и минимумов свечей линейно-взвешенным скользящим средним (LWMA). В версии для StockSharp полностью сохранена исходная логика: сигналы рассчитываются только по закрывшимся свечам выбранного таймфрейма, состояние канала кодируется так же, как в оригинальном индикаторе, а управление позицией выполняется в порядке «выход по времени → выход по индикатору → открытие новой позиции».
Построение индикатора
После закрытия каждой свечи значения High и Low подаются в первую LWMA с длиной Length.
Результат первой LWMA повторно сглаживается второй LWMA той же длины, что воспроизводит фильтр AbsolutelyNoLagLWMA.
Итоговые верхняя и нижняя границы сравниваются с ценой закрытия:
Закрытие выше верхней границы → состояние «бычий пробой».
Закрытие ниже нижней границы → состояние «медвежий пробой».
Закрытие внутри канала → нейтральное состояние.
Стратегия хранит последнюю последовательность состояний канала. Параметр SignalBar задаёт, по какой свече анализировать сигнал (0 — последняя закрытая свеча), полностью повторяя одноимённый параметр из MQL.
Интерпретация сигналов
Открытие лонга (EnableBuyEntries) — срабатывает, когда на свече SignalBar + 1 фиксируется бычий пробой, а свеча с индексом SignalBar уже вернулась внутрь канала. Это копирует проверку «пробой на предыдущей свече» оригинального эксперта.
Открытие шорта (EnableSellEntries) — зеркальная логика для медвежьего пробоя.
Закрытие лонга (EnableBuyExits) — медвежий пробой на контрольной свече закрывает лонговые позиции, если текущая свеча ещё не инициировала выход по времени.
Закрытие шорта (EnableSellExits) — бычий пробой закрывает короткие позиции, если они не были закрыты из-за ограничения по времени.
Управление позицией
Объём заявок задаётся параметром OrderVolume. При развороте стратегия добавляет абсолютное значение текущей позиции, чтобы не оставлять остаточного объёма.
Стоп-лосс / тейк-профит задаются параметрами StopLossPoints и TakeProfitPoints в пунктах инструмента. Положительные значения пересчитываются в абсолютный ценовой сдвиг через PriceStep и передаются в StartProtection; нулевые значения отключают соответствующую защиту.
Ограничение по времени повторяет логику TimeTrade/nTime: параметры UseTimeExit и HoldingLimit задают необходимость и длительность удержания позиции. Проверка выполняется на каждой закрывшейся свече до обработки индикаторных сигналов.
Отметка времени позиции — стратегия сохраняет время последней сделки, приведшей к появлению лонга или шорта, и использует эти значения при расчёте ограничения по времени.
Параметры
Параметр
Назначение
Length
Длина обеих LWMA, формирующих канал.
SignalBar
Индекс свечи для анализа сигнала (0 — последняя закрытая).
CandleType
Таймфрейм, по которому строятся индикатор и сигналы.
OrderVolume
Объём новых входов.
StopLossPoints
Размер стоп-лосса в шагах цены (0 — отключено).
TakeProfitPoints
Размер тейк-профита в шагах цены (0 — отключено).
EnableBuyEntries
Разрешение на открытие длинных позиций.
EnableSellEntries
Разрешение на открытие коротких позиций.
EnableBuyExits
Разрешение на закрытие лонгов по сигналу индикатора.
EnableSellExits
Разрешение на закрытие шортов по сигналу индикатора.
UseTimeExit
Включение выхода по времени удержания позиции.
HoldingLimit
Максимальное время удержания позиции.
Замечания
Канал рассчитывается строго по максимумам и минимумам свечей, идентично индикатору AbsolutelyNoLagLwma_Range_Channel из комплекта MQL.
Стратегия игнорирует незакрытые свечи, что исключает ложные срабатывания.
Значение SignalBar = 0 соответствует типовой конфигурации MT5; значение 1 (по умолчанию) повторяет задержку в одну свечу из оригинального эксперта.
Если у инструмента отсутствует PriceStep, параметры стоп-лосса и тейк-профита не применяются, что полностью повторяет поведение исходного скрипта при нулевых значениях.
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()