This strategy reproduces the MetaTrader expert "Exp_MACDCandle". It converts the color output of a MACD-based candle indicator into trading signals using the StockSharp high-level API.
Concept
The MACD Candle indicator builds synthetic candles from MACD values calculated on the open and close prices. If the MACD computed on the close is above the MACD computed on the open, the candle is considered bullish (color 2). The opposite yields a bearish candle (color 0). A neutral color (1) appears when both values are equal.
The strategy opens long positions when a bullish candle appears after a non‑bullish one and opens short positions when a bearish candle follows a non‑bearish one. Existing positions are reversed in the new direction.
Parameters
FastLength – fast EMA period for MACD (default 12).
SlowLength – slow EMA period for MACD (default 26).
SignalLength – signal line period for MACD (default 9).
CandleType – candle type used for calculations, default TimeFrameCandle with a four‑hour period.
All parameters are configurable and support optimization.
Entry and Exit Rules
Long entry: the MACD on the close rises above the MACD on the open while the previous candle was not bullish.
Short entry: the MACD on the open rises above the MACD on the close while the previous candle was not bearish.
Exit: the strategy closes current position when an opposite signal occurs; no explicit stop or take‑profit is applied.
Notes
The strategy uses market orders (BuyMarket and SellMarket).
Signals are evaluated only on finished candles to avoid noise.
The example is intended for educational purposes and does not include risk management.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on the MACD Candle indicator.
/// Compares MACD computed on opens vs closes.
/// </summary>
public class MacdCandleStrategy : Strategy
{
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<int> _signalLength;
private readonly StrategyParam<DataType> _candleType;
private MovingAverageConvergenceDivergenceSignal _macdOpen;
private MovingAverageConvergenceDivergenceSignal _macdClose;
private decimal? _previousColor;
public int FastLength { get => _fastLength.Value; set => _fastLength.Value = value; }
public int SlowLength { get => _slowLength.Value; set => _slowLength.Value = value; }
public int SignalLength { get => _signalLength.Value; set => _signalLength.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MacdCandleStrategy()
{
_fastLength = Param(nameof(FastLength), 12)
.SetGreaterThanZero()
.SetDisplay("Fast EMA", "Fast EMA period", "Indicator")
.SetOptimize(8, 16, 2);
_slowLength = Param(nameof(SlowLength), 26)
.SetGreaterThanZero()
.SetDisplay("Slow EMA", "Slow EMA period", "Indicator")
.SetOptimize(20, 40, 2);
_signalLength = Param(nameof(SignalLength), 9)
.SetGreaterThanZero()
.SetDisplay("Signal", "Signal period", "Indicator")
.SetOptimize(5, 15, 1);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle type for indicators", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_previousColor = null;
_macdOpen = default;
_macdClose = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_macdOpen = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = FastLength },
LongMa = { Length = SlowLength },
},
SignalMa = { Length = SignalLength }
};
_macdClose = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = FastLength },
LongMa = { Length = SlowLength },
},
SignalMa = { Length = SignalLength }
};
Indicators.Add(_macdOpen);
Indicators.Add(_macdClose);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var openInput = new DecimalIndicatorValue(_macdOpen, candle.OpenPrice, candle.OpenTime) { IsFinal = true };
var closeInput = new DecimalIndicatorValue(_macdClose, candle.ClosePrice, candle.OpenTime) { IsFinal = true };
var openValue = _macdOpen.Process(openInput);
var closeValue = _macdClose.Process(closeInput);
if (!IsFormedAndOnlineAndAllowTrading())
return;
var openMacd = ((IMovingAverageConvergenceDivergenceSignalValue)openValue).Macd;
var closeMacd = ((IMovingAverageConvergenceDivergenceSignalValue)closeValue).Macd;
if (openMacd == null || closeMacd == null)
return;
var color = openMacd < closeMacd ? 2m : openMacd > closeMacd ? 0m : 1m;
if (_previousColor is null)
{
_previousColor = color;
return;
}
if (color == 2m && _previousColor < 2m)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
}
else if (color == 0m && _previousColor > 0m)
{
if (Position > 0)
SellMarket();
if (Position >= 0)
SellMarket();
}
_previousColor = color;
}
}
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
from indicator_extensions import *
class macd_candle_strategy(Strategy):
def __init__(self):
super(macd_candle_strategy, self).__init__()
self._fast_length = self.Param("FastLength", 12) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicator")
self._slow_length = self.Param("SlowLength", 26) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicator")
self._signal_length = self.Param("SignalLength", 9) \
.SetDisplay("Signal", "Signal period", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle type for indicators", "General")
self._macd_open = None
self._macd_close = None
self._previous_color = None
@property
def fast_length(self):
return self._fast_length.Value
@property
def slow_length(self):
return self._slow_length.Value
@property
def signal_length(self):
return self._signal_length.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(macd_candle_strategy, self).OnReseted()
self._previous_color = None
self._macd_open = None
self._macd_close = None
def OnStarted2(self, time):
super(macd_candle_strategy, self).OnStarted2(time)
self._macd_open = MovingAverageConvergenceDivergenceSignal()
self._macd_open.Macd.ShortMa.Length = self.fast_length
self._macd_open.Macd.LongMa.Length = self.slow_length
self._macd_open.SignalMa.Length = self.signal_length
self._macd_close = MovingAverageConvergenceDivergenceSignal()
self._macd_close.Macd.ShortMa.Length = self.fast_length
self._macd_close.Macd.LongMa.Length = self.slow_length
self._macd_close.SignalMa.Length = self.signal_length
self.Indicators.Add(self._macd_open)
self.Indicators.Add(self._macd_close)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.process_candle).Start()
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
open_value = process_float(self._macd_open, candle.OpenPrice, candle.OpenTime, True)
close_value = process_float(self._macd_close, candle.ClosePrice, candle.OpenTime, True)
open_macd = open_value.Macd
close_macd = close_value.Macd
if open_macd is None or close_macd is None:
return
open_macd = float(open_macd)
close_macd = float(close_macd)
if open_macd < close_macd:
color = 2.0
elif open_macd > close_macd:
color = 0.0
else:
color = 1.0
if self._previous_color is None:
self._previous_color = color
return
if color == 2.0 and self._previous_color < 2.0:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
elif color == 0.0 and self._previous_color > 0.0:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
self._previous_color = color
def CreateClone(self):
return macd_candle_strategy()