MACD Trend
Strategy based on MACD indicator
Testing indicates an average annual return of about 64%. It performs best in the forex market.
MACD Trend reacts to crossovers between the MACD line and its signal line. Bullish crosses initiate longs while bearish crosses start shorts. Opposite crosses or a stop close the trade.
The moving-average convergence divergence indicator adapts well to shifting markets by measuring momentum. This approach aims to ride trending swings while the indicator maintains a clear bullish or bearish bias.
Details
- Entry Criteria: Signals based on MA, MACD.
- Long/Short: Both directions.
- Exit Criteria: Opposite signal or stop.
- Stops: Yes.
- Default Values:
FastEmaPeriod= 12SlowEmaPeriod= 26SignalPeriod= 9StopLossPercent= 2mCandleType= TimeSpan.FromMinutes(5)
- Filters:
- Category: Trend
- Direction: Both
- Indicators: MA, MACD
- Stops: Yes
- Complexity: Basic
- Timeframe: Intraday (5m)
- Seasonality: No
- Neural Networks: No
- Divergence: No
- Risk Level: Medium
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 MACD indicator.
/// It enters long position when MACD crosses above signal line and short position when MACD crosses below signal line.
/// </summary>
public class MacdTrendStrategy : Strategy
{
private readonly StrategyParam<int> _fastEmaPeriod;
private readonly StrategyParam<int> _slowEmaPeriod;
private readonly StrategyParam<int> _signalPeriod;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
// Current state
private bool _prevIsMacdAboveSignal;
/// <summary>
/// Period for fast EMA in MACD.
/// </summary>
public int FastEmaPeriod
{
get => _fastEmaPeriod.Value;
set => _fastEmaPeriod.Value = value;
}
/// <summary>
/// Period for slow EMA in MACD.
/// </summary>
public int SlowEmaPeriod
{
get => _slowEmaPeriod.Value;
set => _slowEmaPeriod.Value = value;
}
/// <summary>
/// Period for signal line in MACD.
/// </summary>
public int SignalPeriod
{
get => _signalPeriod.Value;
set => _signalPeriod.Value = value;
}
/// <summary>
/// Stop-loss as percentage of entry price.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize the MACD Trend strategy.
/// </summary>
public MacdTrendStrategy()
{
_fastEmaPeriod = Param(nameof(FastEmaPeriod), 200)
.SetDisplay("Fast EMA Period", "Period for fast EMA in MACD", "Indicators")
.SetOptimize(8, 16, 2);
_slowEmaPeriod = Param(nameof(SlowEmaPeriod), 500)
.SetDisplay("Slow EMA Period", "Period for slow EMA in MACD", "Indicators")
.SetOptimize(20, 32, 2);
_signalPeriod = Param(nameof(SignalPeriod), 200)
.SetDisplay("Signal Period", "Period for signal line in MACD", "Indicators")
.SetOptimize(5, 13, 2);
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetDisplay("Stop Loss (%)", "Stop loss as a percentage of entry price", "Risk parameters")
.SetOptimize(1, 3, 0.5m);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevIsMacdAboveSignal = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Create MACD indicator with signal line
var macd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = FastEmaPeriod },
LongMa = { Length = SlowEmaPeriod },
},
SignalMa = { Length = SignalPeriod }
};
// Create subscription and bind indicator
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(macd, ProcessCandle)
.Start();
// Setup chart visualization if available
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, macd);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue)
{
// Skip unfinished candles
if (candle.State != CandleStates.Finished)
return;
// Check if strategy is ready to trade
if (!IsFormedAndOnlineAndAllowTrading())
return;
var macdTyped = (MovingAverageConvergenceDivergenceSignalValue)macdValue;
if (macdTyped.Macd is not decimal macd || macdTyped.Signal is not decimal signal)
return;
// Check MACD position relative to signal line
var isMacdAboveSignal = macd > signal;
// Check for crossovers
var isMacdCrossedAboveSignal = isMacdAboveSignal && !_prevIsMacdAboveSignal;
var isMacdCrossedBelowSignal = !isMacdAboveSignal && _prevIsMacdAboveSignal;
// Entry/exit logic based on MACD crossovers
if (isMacdCrossedAboveSignal && Position <= 0)
{
// MACD crossed above signal line - Buy signal
var volume = Volume + Math.Abs(Position);
BuyMarket(volume);
LogInfo($"Buy signal: MACD ({macd:F5}) crossed above Signal ({signal:F5})");
}
else if (isMacdCrossedBelowSignal && Position >= 0)
{
// MACD crossed below signal line - Sell signal
var volume = Volume + Math.Abs(Position);
SellMarket(volume);
LogInfo($"Sell signal: MACD ({macd:F5}) crossed below Signal ({signal:F5})");
}
// Update previous state
_prevIsMacdAboveSignal = 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_trend_strategy(Strategy):
"""
MACD Trend: enters long on MACD cross above signal, short on cross below.
"""
def __init__(self):
super(macd_trend_strategy, self).__init__()
self._fast_ema = self.Param("FastEmaPeriod", 200).SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_ema = self.Param("SlowEmaPeriod", 500).SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._signal_period = self.Param("SignalPeriod", 200).SetDisplay("Signal Period", "Signal line period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Timeframe", "General")
self._prev_above = False
self._is_init = False
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(macd_trend_strategy, self).OnReseted()
self._prev_above = False
self._is_init = False
def OnStarted2(self, time):
super(macd_trend_strategy, self).OnStarted2(time)
macd = MovingAverageConvergenceDivergenceSignal()
macd.Macd.ShortMa.Length = self._fast_ema.Value
macd.Macd.LongMa.Length = self._slow_ema.Value
macd.SignalMa.Length = self._signal_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(macd, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, macd)
self.DrawOwnTrades(area)
def _process_candle(self, candle, macd_value):
if candle.State != CandleStates.Finished:
return
typed_val = macd_value
if typed_val.Macd is None or typed_val.Signal is None:
return
macd_line = float(typed_val.Macd)
signal_line = float(typed_val.Signal)
is_above = macd_line > signal_line
if not self._is_init:
self._prev_above = is_above
self._is_init = True
return
crossed_above = is_above and not self._prev_above
crossed_below = not is_above and self._prev_above
if crossed_above and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif crossed_below and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._prev_above = is_above
def CreateClone(self):
return macd_trend_strategy()