This sample converts the original MetaTrader 4 expert advisor MACD_v1.mq4 into a StockSharp high-level strategy. The algorithm tracks moving average convergence divergence (MACD) crossovers and trades in the direction of the new trend. Optional protective exits replicate the original advisor's behaviour: a stop-loss, a distant take-profit and a partial profit target that liquidates half of the current position.
Trading Logic
Data source – the strategy subscribes to the configured candle series (5-minute candles by default) and binds a MovingAverageConvergenceDivergenceSignal indicator.
Entry conditions:
Enter long when the MACD line crosses above the signal line. If a short position is active, it is closed before opening the long.
Enter short when the MACD line crosses below the signal line. If a long position exists, it is closed first.
Exit conditions:
Opposite MACD crossover closes the current position and opens a new position in the opposite direction.
A configurable take-profit and stop-loss managed by StartProtection mirror the original TP/SL parameters (measured in instrument points).
A partial profit target closes half of the open position once price advances by a specified amount from the entry level. The partial exit is triggered only once per position.
Parameters
Name
Default
Description
Fast Period
23
Fast EMA length for the MACD calculation (mirrors the MQL parameter a = 2300).
Slow Period
40
Slow EMA length for the MACD calculation (b = 4000 in the source).
Signal Period
8
Signal line length (c = 800 in the source).
Take Profit
500
Distance in price points for the protective take-profit order. Set to 0 to disable.
Stop Loss
80
Distance in price points for the protective stop-loss order. Set to 0 to disable.
Partial Profit
70
Distance in price points to close half of the open position. Set to 0 to disable partial exits.
Candle Type
5-minute time frame
Candle series used for indicator calculations.
Notes
Indicator parameters were scaled to typical MACD lengths (23/40/8) because the MQL script expressed them as hundredths (2300/4000/800).
The strategy automatically restores the partial exit flag whenever a new position is accumulated.
Chart drawing helpers highlight candles, MACD values and the strategy's trades when a chart panel is available.
Volume handling relies on the base strategy Volume property. Adjust it before starting the strategy to match your instrument size.
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()