Стратегия является C#-портом советника MetaTrader Exp_XAng_Zad_C_Tm_MMRec. Она использует адаптивные каналы индикатора XAng Zad C, ограничение по торговой сессии и блок управления капиталом, который уменьшает объем сделок после серии убыточных трейдов. Цель — отлавливать пробои в момент пересечения верхней и нижней линий канала и снижать риск при неблагоприятной серии сделок.
Основные элементы
Индикатор. XAng Zad C строит адаптивные верхнюю и нижнюю границы. В C#-версии воспроизведены расчеты канала и реализованы сглаживания SMA, EMA, SMMA и LWMA. Экзотические методы, отсутствующие в StockSharp, автоматически заменяются на EMA.
Входы. Если на предыдущей свече верхняя линия была выше нижней, а на текущей закрывшейся свече опустилась ниже, формируется сигнал на покупку. Обратная ситуация — сигнал на продажу. Параметр SignalShift задает, сколько закрытых свечей участвует в сравнении.
Выходы. Дополнительные флаги позволяют закрывать длинные позиции при повторном падении верхней линии и закрывать короткие позиции при росте нижней. Выход также выполняется сразу, когда цена выходит за рамки разрешенного торгового окна.
Управление капиталом. Стратегия хранит историю результатов. Если среди последних BuyTotalTrigger (или SellTotalTrigger) сделок обнаруживается не менее BuyLossTrigger (или SellLossTrigger) убыточных, следующий вход выполняется уменьшенным объемом. При отсутствии серии убытков возвращается стандартный объем.
Риск. Фиксированные стоп-лосс и тейк-профит задаются в шагах цены (Security.PriceStep). При достижении уровня внутри свечи позиция закрывается по соответствующей цене.
Параметры
Имя
Описание
NormalVolume
Базовый объем сделки при отсутствии серии убытков.
ReducedVolume
Объем сделки после серии убыточных трейдов.
BuyTotalTrigger / SellTotalTrigger
Количество последних сделок, которые анализируются при оценке серии.
BuyLossTrigger / SellLossTrigger
Число убыточных сделок в анализируемом окне, необходимое для уменьшения объема.
EnableBuyEntries / EnableSellEntries
Разрешение на открытие длинных / коротких позиций.
EnableBuyExit / EnableSellExit
Разрешение на автоматическое закрытие позиций по сигналам индикатора.
UseTradingWindow
Включение ограничения по времени торговли.
WindowStart / WindowEnd
Начало и конец торгового окна (UTC). Окно может пересекать полуночь.
StopLoss
Расстояние стоп-лосса в шагах цены (0 — отключено).
TakeProfit
Расстояние тейк-профита в шагах цены (0 — отключено).
SignalShift
Количество закрытых свечей, участвующих в сравнении линий.
CandleType
Тип свечей для расчета индикатора (по умолчанию 4 часа).
SmoothMethods
Метод сглаживания внутри индикатора. Не поддерживаемые методы автоматически заменяются на EMA.
MaLength
Длина сглаживания.
MaPhase
Дополнительный параметр фазы (сохранен для совместимости).
Ki
Коэффициент, определяющий скорость реакции канала на цену.
AppliedPrices
Тип цены, подаваемой на вход индикатора (Close, Open, Median и т.д.).
Отличия от версии MQL5
В оригинале использовались глобальные функции работы с историей сделок. В C#-версии результаты фиксируются внутри стратегии, но логика триггеров сохранена.
Объем сделок задается напрямую через параметры NormalVolume и ReducedVolume и должен соответствовать инфраструктуре брокера.
Торговое окно задается через TimeSpan. Если WindowStart совпадает с WindowEnd, торговля отключается (поведение нулевого окна в оригинале).
Стратегия предполагает полный разворот позиции — частичное удержание старых позиций не реализовано.
Для отсутствующих в StockSharp сглаживаний (JJMA, JurX, ParMA, T3, VIDYA, AMA) используется EMA по умолчанию. При необходимости можно расширить метод CreateMovingAverage.
Рекомендации по применению
Подбирайте тип свечей, соответствующий таймфрейму оригинального советника (по умолчанию H4).
Настраивайте стоп-лосс и тейк-профит в шагах цены так, чтобы приблизительно соответствовать точкам из MQL5.
Оптимизируйте параметры управления капиталом под волатильность инструмента и допустимый риск.
Перед запуском сравните линии индикатора на графике, чтобы убедиться, что портированная версия ведет себя так же, как оригинал.
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()