Этот пример демонстрирует перенос эксперта MetaTrader 4 MACD_v1.mq4 в высокоуровневый API StockSharp. Алгоритм отслеживает пересечения линии MACD и сигнальной линии, открывая сделки в сторону нового импульса. Дополнительно реализованы те же защитные механизмы, что и в исходном советнике: удалённый тейк-профит, стоп-лосс и частичная фиксация прибыли при достижении первой цели.
Логика торговли
Источник данных – стратегия подписывается на выбранный таймфрейм (по умолчанию 5-минутные свечи) и привязывает индикатор MovingAverageConvergenceDivergenceSignal.
Условия входа:
Покупка при пересечении линии MACD выше сигнальной. Если есть короткая позиция, она закрывается перед открытием лонга.
Продажа при пересечении MACD ниже сигнальной линии. Текущая длинная позиция закрывается перед открытием шорта.
Условия выхода:
Обратное пересечение закрывает открытую позицию и инициирует разворот.
StartProtection управляет стоп-лоссом и тейк-профитом в пунктах, повторяя параметры оригинальной реализации.
Частичное закрытие половины позиции происходит один раз, когда цена проходит заданное количество пунктов от средней цены входа.
Параметры
Название
Значение по умолчанию
Описание
Fast Period
23
Длина быстрой EMA в расчёте MACD (соответствует параметру a = 2300).
Slow Period
40
Длина медленной EMA (параметр b = 4000).
Signal Period
8
Длина сигнальной средней (c = 800).
Take Profit
500
Расстояние тейк-профита в пунктах. Ноль отключает защитный тейк.
Stop Loss
80
Расстояние стоп-лосса в пунктах. Ноль отключает стоп.
Partial Profit
70
Расстояние в пунктах до частичного закрытия половины позиции. Ноль отменяет функцию.
Candle Type
5-минутный таймфрейм
Тип свечей для расчёта индикатора.
Дополнительные замечания
В оригинале периоды MACD задавались в сотых долях (2300/4000/800). В версии StockSharp они переведены в привычные значения 23/40/8.
Флаг частичного выхода автоматически сбрасывается при наборе новой позиции, что позволяет повторно использовать цель.
При наличии панели графика стратегия рисует свечи, линии MACD и собственные сделки.
Объём сделок задаётся свойством Volume базовой стратегии. Перед запуском подберите его под требования инструмента.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class MacdSignalCrossoverStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _signalPeriod;
private readonly StrategyParam<DataType> _candleType;
private bool _prevMacdAboveSignal;
private bool _hasPrev;
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
public int SignalPeriod
{
get => _signalPeriod.Value;
set => _signalPeriod.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public MacdSignalCrossoverStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 23);
_slowPeriod = Param(nameof(SlowPeriod), 40);
_signalPeriod = Param(nameof(SignalPeriod), 8);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame());
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMacdAboveSignal = false;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var macd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = FastPeriod },
LongMa = { Length = SlowPeriod },
},
SignalMa = { Length = SignalPeriod }
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(macd, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!macdValue.IsFinal)
return;
var macdTyped = macdValue as MovingAverageConvergenceDivergenceSignalValue;
if (macdTyped == null)
return;
var macdLine = macdTyped.Macd;
var signalLine = macdTyped.Signal;
if (macdLine == null || signalLine == null)
return;
var isMacdAboveSignal = macdLine.Value > signalLine.Value;
if (!_hasPrev)
{
_hasPrev = true;
_prevMacdAboveSignal = isMacdAboveSignal;
return;
}
var crossedAbove = isMacdAboveSignal && !_prevMacdAboveSignal;
var crossedBelow = !isMacdAboveSignal && _prevMacdAboveSignal;
if (crossedAbove)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
}
else if (crossedBelow)
{
if (Position > 0)
SellMarket();
if (Position >= 0)
SellMarket();
}
_prevMacdAboveSignal = isMacdAboveSignal;
}
}
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 MovingAverageConvergenceDivergenceSignal
from StockSharp.Algo.Strategies import Strategy
class macd_signal_crossover_strategy(Strategy):
"""Simple MACD/signal line crossover strategy. Buys when MACD crosses above signal,
sells when MACD crosses below signal."""
def __init__(self):
super(macd_signal_crossover_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 23) \
.SetDisplay("Fast Period", "Fast EMA period for MACD", "Indicators")
self._slow_period = self.Param("SlowPeriod", 40) \
.SetDisplay("Slow Period", "Slow EMA period for MACD", "Indicators")
self._signal_period = self.Param("SignalPeriod", 8) \
.SetDisplay("Signal Period", "Signal line period for MACD", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Candle type used for analysis", "General")
self._prev_macd_above_signal = False
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
@property
def SignalPeriod(self):
return self._signal_period.Value
def OnReseted(self):
super(macd_signal_crossover_strategy, self).OnReseted()
self._prev_macd_above_signal = False
self._has_prev = False
def OnStarted2(self, time):
super(macd_signal_crossover_strategy, self).OnStarted2(time)
macd = MovingAverageConvergenceDivergenceSignal()
macd.Macd.ShortMa.Length = self.FastPeriod
macd.Macd.LongMa.Length = self.SlowPeriod
macd.SignalMa.Length = self.SignalPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(macd, self._process_candle).Start()
def _process_candle(self, candle, macd_value):
if candle.State != CandleStates.Finished:
return
if not macd_value.IsFinal:
return
macd_line = macd_value.Macd
signal_line = macd_value.Signal
if macd_line is None or signal_line is None:
return
is_macd_above_signal = float(macd_line) > float(signal_line)
if not self._has_prev:
self._has_prev = True
self._prev_macd_above_signal = is_macd_above_signal
return
crossed_above = is_macd_above_signal and not self._prev_macd_above_signal
crossed_below = not is_macd_above_signal and self._prev_macd_above_signal
if crossed_above:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
elif crossed_below:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
self._prev_macd_above_signal = is_macd_above_signal
def CreateClone(self):
return macd_signal_crossover_strategy()