Стратегия Exp Digital MACD переносит логику экспертного советника MetaTrader 5 «Exp_Digital_MACD» в инфраструктуру StockSharp. Она подписывается на свечи выбранного таймфрейма и реагирует на форму и расположение MACD‑осциллятора. В коде сохранены четыре режима работы:
Breakdown – торговля на пробой нулевой линии осциллятора.
MACD Twist – фиксация разворота наклона линии MACD.
Signal Twist – использование разворота сигнальной линии как подтверждения.
MACD Disposition – анализ расположения MACD относительно сигнальной линии.
В библиотеке StockSharp отсутствует оригинальный фильтр «Digital MACD», поэтому применяется стандартный индикатор MovingAverageConvergenceDivergenceSignal. Значения EMA по умолчанию (12/26) и длина сигнальной EMA (5) подобраны для максимального сходства с MQL-реализацией, где Signal_XMA = 5. Стратегия обрабатывает только закрытые свечи и хранит минимальную историю в приватных полях, повторяя поведение SignalBar = 1.
Параметры
Mode – выбор торгового алгоритма из четырёх доступных. По умолчанию: MacdTwist.
FastPeriod – период быстрой EMA в расчёте MACD. По умолчанию: 12.
SlowPeriod – период медленной EMA. По умолчанию: 26.
SignalPeriod – период EMA сигнальной линии. По умолчанию: 5.
CandleType – тип свечей, который будет подписан (по умолчанию – 4 часа).
OrderVolume – объём каждой рыночной заявки.
StopLossPoints / TakeProfitPoints – защитные отступы в шагах цены. При наличии корректного Security.Step переводятся в абсолютные значения; ноль выключает защиту.
EnableLongEntry / EnableShortEntry – разрешение на открытие длинных или коротких позиций.
EnableLongExit / EnableShortExit – разрешение на закрытие уже открытых позиций соответствующего направления.
Логика торговли
Алгоритм оценивает значения индикаторов на закрытии каждой свечи:
Breakdown: если два бара назад значение MACD было выше нуля, стратегия при необходимости закрывает короткие позиции и открывает длинную, когда следующий бар опускается к нулю или ниже. Если два бара назад MACD был ниже нуля, система закрывает длинные позиции и открывает короткие при обратном пересечении. Это повторяет контртрендовую логику исходника вокруг нулевой линии.
MACD Twist: анализирует три последних значения MACD. Локальный минимум (value[2] > value[1], value[0] > value[1]) формирует сигнал на покупку, локальный максимум – на продажу. Противоположный разворот служит сигналом выхода.
Signal Twist: идентичная проверка применяется к сигнальной линии.
MACD Disposition: сравнивает MACD и сигнальную линию. Если раньше MACD находился выше сигнальной линии, а затем опустился к ней или ниже, стратегия покупает и закрывает шорты; при обратном пересечении продаёт и закрывает лонги.
Каждый вход выполняется рыночной заявкой объёмом OrderVolume + |Position|, что позволяет одновременно развернуть и открыть позицию. Сигналы выхода отправляют рыночные заявки только на закрытие текущего объёма.
Управление рисками
При запуске вызывается StartProtection. Если заданы положительные значения StopLossPoints и/или TakeProfitPoints и у инструмента известен шаг цены, стоп‑приказы выставляются в абсолютных значениях. Нулевые параметры отключают автоматическую защиту.
Особенности реализации
Стратегия оценивает только последнюю завершённую свечу, аналогично SignalBar = 1 в MQL.
Разница между стандартным MACD и оригинальным Digital MACD сохраняется; при необходимости можно подстроить периоды EMA.
Все комментарии в исходнике на английском языке согласно требованию.
Использование
Привяжите стратегию к нужному счёту и инструменту, предоставляющему свечи выбранного таймфрейма.
Настройте параметры под ликвидность и волатильность инструмента.
Запустите стратегию – она автоматически подпишется на свечи, рассчитает MACD и будет размещать заявки в соответствии с выбранным режимом.
Отслеживайте логи или график (при наличии), чтобы контролировать значения индикатора и изменения позиции.
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>
/// Digital MACD strategy using fast/slow EMA crossover (MACD concept).
/// Trades on MACD line zero crossovers.
/// </summary>
public class ExpDigitalMacdStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal? _prevMacd;
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 ExpDigitalMacdStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast EMA for MACD", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow EMA for MACD", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMacd = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevMacd = null;
var fastEma = new ExponentialMovingAverage { Length = FastPeriod };
var slowEma = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastEma, slowEma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastEma);
DrawIndicator(area, slowEma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevMacd = fast - slow;
return;
}
var macd = fast - slow;
if (_prevMacd == null)
{
_prevMacd = macd;
return;
}
// MACD crosses above zero → buy
if (_prevMacd.Value <= 0 && macd > 0)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
}
// MACD crosses below zero → sell
else if (_prevMacd.Value >= 0 && macd < 0)
{
if (Position > 0)
SellMarket();
if (Position >= 0)
SellMarket();
}
_prevMacd = macd;
}
}
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 exp_digital_macd_strategy(Strategy):
def __init__(self):
super(exp_digital_macd_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", 12) \
.SetDisplay("Fast Period", "Fast EMA for MACD", "Indicators")
self._slow_period = self.Param("SlowPeriod", 26) \
.SetDisplay("Slow Period", "Slow EMA for MACD", "Indicators")
self._prev_macd = 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(exp_digital_macd_strategy, self).OnReseted()
self._prev_macd = None
def OnStarted2(self, time):
super(exp_digital_macd_strategy, self).OnStarted2(time)
self._prev_macd = None
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.FastPeriod
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast_ema, slow_ema, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_ema)
self.DrawIndicator(area, slow_ema)
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)
macd = fv - sv
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_macd = macd
return
if self._prev_macd is None:
self._prev_macd = macd
return
# MACD crosses above zero
if self._prev_macd <= 0 and macd > 0:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
# MACD crosses below zero
elif self._prev_macd >= 0 and macd < 0:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
self._prev_macd = macd
def CreateClone(self):
return exp_digital_macd_strategy()