Стратегия является портом эксперт-советника Exp_AltrTrend_Signal_v2_2 под StockSharp. Она полностью повторяет логику
адаптивного канала индикатора AltrTrend Signal и исполняет сигналы с задержкой по барам, как в оригинале. Значение ADX
сужает или расширяет канал, поэтому пробои возникают только при подтверждённой силе тренда.
Как работает
На каждом завершённом баре заданного таймфрейма строится динамический канал. Его ширина определяется максимумом и минимумом
цены внутри окна, которое растягивается или сжимается пропорционально предыдущему значению ADX (KPeriod / ADX).
Внутренние границы (smin, smax) смещаются к центру на величину KPercent. Для смены тренда цена должна закрыться вне
этих границ.
Если тренд меняется с медвежьего на бычий и закрытие выше верхней границы, формируется сигнал покупки. Обратное условие даёт
сигнал продажи. Заявки отправляются на баре, указанном параметром SignalBar, что соответствует поведению MQL5-версии.
Стоп-лосс и тейк-профит переводятся из пунктов в шаги цены, чтобы защитные выходы работали так же, как в исходном роботе.
Детали
Вход в сделку:
Покупка: предыдущий тренд был нисходящим или нейтральным, цена закрылась выше внутренней верхней границы, разрешены
покупки. Короткие позиции закрываются автоматически, если это разрешено.
Продажа: предыдущий тренд был восходящим или нейтральным, цена закрылась ниже внутренней нижней границы, разрешены
продажи. Длинные позиции закрываются автоматически при разрешении.
Выход из сделки:
Обратный пробой границы при включённом разрешении на закрытие соответствующего направления.
Срабатывание стоп-лосса или тейк-профита в шагах цены.
Направление: Торговля в обе стороны с независимыми переключателями открытия и закрытия.
Управление риском:
StopLossPoints и TakeProfitPoints имитируют модуль MM оригинала, применяя фиксированные защитные расстояния после
открытия рынка.
Настройки индикатора:
KPercent определяет, насколько сильно границы канала сдвигаются к центру.
KStop сохраняет оригинальное значение стрелки для совместимости и логирования.
KPeriod задаёт базовое окно до модификации ADX.
AdxPeriod управляет длиной индекса направленного движения, влияющего на ширину канала.
SignalBar задаёт задержку исполнения сигнала в количестве завершённых свечей.
Рекомендуемые рынки:
Лучше всего подходит для инструментов с ярко выраженными фазами тренда: валютные пары мажоры, золото, фьючерсы на индексы.
Таймфрейм по умолчанию — H1, как в MQL5-шаблоне.
Параметры
Параметр
Описание
CandleType
Таймфрейм, на котором строится адаптивный канал.
KPercent
Процентное сжатие внутренних границ канала.
KStop
Множитель для расчёта проекции стрелки (для совместимости).
KPeriod
Базовое количество свечей до поправки ADX.
AdxPeriod
Период индекса направленного движения, регулирующего ширину канала.
SignalBar
Количество завершённых свечей до исполнения сигнала.
AllowBuyEntries / AllowSellEntries
Включение открытия позиций в каждом направлении.
AllowBuyExits / AllowSellExits
Разрешение автоматического закрытия позиций по обратному сигналу.
StopLossPoints
Дистанция стоп-лосса в шагах цены (0 — отключено).
TakeProfitPoints
Дистанция тейк-профита в шагах цены (0 — отключено).
Порт сохраняет все переключатели и параметры управления риском исходного эксперта, что упрощает перенос результатов в StockSharp
Designer, Shell или 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()