The Test MACD Strategy is a faithful conversion of the MetaTrader TestMACD expert advisor into the StockSharp high-level API. It uses the Moving Average Convergence Divergence (MACD) indicator to detect momentum shifts and executes trades whenever the MACD line crosses the signal line on closed candles. The strategy operates on a single instrument and timeframe supplied through the CandleType parameter.
Trading Logic
Subscribe to candle data defined by CandleType and calculate a MACD indicator with configurable fast, slow, and signal periods.
Monitor the MACD value difference (MACD - Signal) on every finished candle.
Trigger a bullish entry when the difference changes sign from non-positive to positive, meaning the MACD line has crossed above the signal line. Any short exposure is closed before opening the long position.
Trigger a bearish entry when the difference changes sign from non-negative to negative, meaning the MACD line has crossed below the signal line. Any long exposure is closed before opening the short position.
All orders are issued at market with a fixed volume configured by the TradeVolume parameter.
Each entry is automatically protected with stop-loss and take-profit levels expressed in price steps to replicate the point-based risk management from the original expert.
Risk Management
Stop-loss and take-profit distances mirror the MetaTrader inputs and are supplied in price steps. If the security lacks PriceStep information, the strategy falls back to absolute price distances using MinPriceStep or 1 as the multiplier.
Protective orders are created once, when the strategy starts, via StartProtection, ensuring they apply to every subsequent trade without reconfiguration.
Parameters
Parameter
Description
Default
FastPeriod
Fast EMA length used in MACD calculations.
12
SlowPeriod
Slow EMA length used in MACD calculations.
24
SignalPeriod
Signal line EMA length for MACD smoothing.
9
StopLossPoints
Stop-loss distance expressed in price steps.
90
TakeProfitPoints
Take-profit distance expressed in price steps.
110
TradeVolume
Fixed volume for all market orders.
1
CandleType
Candle data type and timeframe subscribed by the strategy.
30-minute time frame
Usage Notes
Attach the strategy to a security before starting it so that PriceStep and MinPriceStep are available.
Ensure market data is provided for the selected CandleType; otherwise the MACD indicator will not form and trading will not occur.
The strategy logs every crossover event, making it easy to trace trade decisions during backtests.
Conversion Details
Original MetaTrader classes CSignalMACD, CTrailingNone, and CMoneyFixedLot are replaced by StockSharp's indicator binding and StartProtection mechanisms.
Logic from ExtStateMACD that checked for MACD crossovers is represented by a sign-change detector on the MACD difference between consecutive finished candles.
Money management is simplified to a fixed volume parameter, closely resembling the fixed-lot behavior of CMoneyFixedLot when percent-based sizing is disabled.
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Test MACD strategy: MACD histogram zero-cross.
/// Buys when MACD histogram crosses above zero.
/// Sells when MACD histogram crosses below zero.
/// </summary>
public class TestMacdStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public TestMacdStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var macd = new MovingAverageConvergenceDivergenceSignal
{
Macd = { ShortMa = { Length = 12 }, LongMa = { Length = 26 } },
SignalMa = { Length = 9 }
};
decimal? prevHistogram = null;
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(macd, (candle, macdVal) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (macdVal.IsEmpty)
return;
var v = (MovingAverageConvergenceDivergenceSignalValue)macdVal;
if (v.Macd is not decimal macdDec || v.Signal is not decimal signalDec)
return;
var histogram = macdDec - signalDec;
if (prevHistogram.HasValue)
{
if (prevHistogram.Value <= 0m && histogram > 0m && Position <= 0)
BuyMarket();
else if (prevHistogram.Value >= 0m && histogram < 0m && Position >= 0)
SellMarket();
}
prevHistogram = histogram;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, macd);
DrawOwnTrades(area);
}
}
}
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 test_macd_strategy(Strategy):
def __init__(self):
super(test_macd_strategy, self).__init__()
self._prev_histogram = None
self._macd = None
def OnReseted(self):
super(test_macd_strategy, self).OnReseted()
self._prev_histogram = None
self._macd = None
def OnStarted2(self, time):
super(test_macd_strategy, self).OnStarted2(time)
self._macd = MovingAverageConvergenceDivergenceSignal()
self._macd.Macd.ShortMa.Length = 12
self._macd.Macd.LongMa.Length = 26
self._macd.SignalMa.Length = 9
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(30)))
subscription.BindEx(self._macd, self._process_candle)
subscription.Start()
def _process_candle(self, candle, macd_val):
if candle.State != CandleStates.Finished:
return
if not self._macd.IsFormed:
return
macd_v = float(macd_val.Macd)
signal_v = float(macd_val.Signal)
histogram = macd_v - signal_v
if self._prev_histogram is not None:
if self._prev_histogram <= 0.0 and histogram > 0.0 and self.Position <= 0:
self.BuyMarket()
elif self._prev_histogram >= 0.0 and histogram < 0.0 and self.Position >= 0:
self.SellMarket()
self._prev_histogram = histogram
def CreateClone(self):
return test_macd_strategy()