MACD Mean Reversion-Strategie
Diese Methode verfolgt das MACD-Histogramm im Verhältnis zu seinem eigenen Durchschnitt. Extreme Histogrammwerte kehren oft um, sobald der Schwung nachlässt. Durch die Überwachung der Differenz zwischen MACD und seiner Signallinie findet die Strategie überdehnte Bewegungen.
Tests zeigen eine durchschnittliche jährliche Rendite von etwa 67%. Er funktioniert am besten auf dem Aktienmarkt.
Eine Long-Position wird eingegangen, wenn das MACD-Histogramm um DeviationMultiplier Standardabweichungen unter den Mittelwert fällt. Eine Short-Position wird eröffnet, wenn das Histogramm um denselben Betrag über den Mittelwert steigt. Der Trade wird geschlossen, wenn das Histogramm wieder durch seinen Durchschnitt kreuzt.
Dieser Ansatz richtet sich an Trader, die sich damit wohlfühlen, gegen Momentumextreme zu handeln. Ein Stop-Loss, gemessen als Prozentsatz des Einstiegspreises, schützt gegen Trends, die weiter an Stärke gewinnen.
Details
- Einstiegskriterien:
- Long: MACD Histogram < Avg - DeviationMultiplier * StdDev
- Short: MACD Histogram > Avg + DeviationMultiplier * StdDev
- Long/Short: Beide Seiten.
- Ausstiegskriterien:
- Long: Ausstieg wenn Histogram > Avg
- Short: Ausstieg wenn Histogram < Avg
- Stops: Ja, prozentualer Stop-Loss.
- Standardwerte:
FastMacdPeriod= 12SlowMacdPeriod= 26SignalPeriod= 9AveragePeriod= 20DeviationMultiplier= 2mCandleType= TimeSpan.FromMinutes(5)
- Filter:
- Kategorie: Mean Reversion
- Richtung: Beide
- Indikatoren: MACD
- Stops: Ja
- Komplexität: Mittel
- Zeitrahmen: Intraday
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Mittel
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo;
using StockSharp.Algo.Candles;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// MACD Histogram Mean Reversion strategy.
/// This strategy enters positions when MACD Histogram is significantly below or above its average value.
/// </summary>
public class MacdMeanReversionStrategy : Strategy
{
private readonly StrategyParam<int> _fastMacdPeriod;
private readonly StrategyParam<int> _slowMacdPeriod;
private readonly StrategyParam<int> _signalPeriod;
private readonly StrategyParam<int> _averagePeriod;
private readonly StrategyParam<decimal> _deviationMultiplier;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _stopLossPercent;
private decimal _prevMacdHist;
private decimal _avgMacdHist;
private decimal _stdDevMacdHist;
private decimal _sumMacdHist;
private decimal _sumSquaresMacdHist;
private int _count;
private readonly Queue<decimal> _macdHistValues = [];
/// <summary>
/// Fast EMA period for MACD.
/// </summary>
public int FastMacdPeriod
{
get => _fastMacdPeriod.Value;
set => _fastMacdPeriod.Value = value;
}
/// <summary>
/// Slow EMA period for MACD.
/// </summary>
public int SlowMacdPeriod
{
get => _slowMacdPeriod.Value;
set => _slowMacdPeriod.Value = value;
}
/// <summary>
/// Signal line period for MACD.
/// </summary>
public int SignalPeriod
{
get => _signalPeriod.Value;
set => _signalPeriod.Value = value;
}
/// <summary>
/// Period for calculating mean and standard deviation of MACD Histogram.
/// </summary>
public int AveragePeriod
{
get => _averagePeriod.Value;
set => _averagePeriod.Value = value;
}
/// <summary>
/// Deviation multiplier for entry signals.
/// </summary>
public decimal DeviationMultiplier
{
get => _deviationMultiplier.Value;
set => _deviationMultiplier.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Stop-loss percentage.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public MacdMeanReversionStrategy()
{
_fastMacdPeriod = Param(nameof(FastMacdPeriod), 12)
.SetGreaterThanZero()
.SetOptimize(8, 16, 4)
.SetDisplay("Fast EMA Period", "Fast EMA period for MACD", "Indicators");
_slowMacdPeriod = Param(nameof(SlowMacdPeriod), 26)
.SetGreaterThanZero()
.SetOptimize(20, 30, 5)
.SetDisplay("Slow EMA Period", "Slow EMA period for MACD", "Indicators");
_signalPeriod = Param(nameof(SignalPeriod), 9)
.SetGreaterThanZero()
.SetOptimize(5, 13, 4)
.SetDisplay("Signal Period", "Signal line period for MACD", "Indicators");
_averagePeriod = Param(nameof(AveragePeriod), 20)
.SetGreaterThanZero()
.SetOptimize(10, 50, 10)
.SetDisplay("Average Period", "Period for calculating MACD Histogram average", "Settings");
_deviationMultiplier = Param(nameof(DeviationMultiplier), 2m)
.SetGreaterThanZero()
.SetOptimize(1.5m, 3m, 0.5m)
.SetDisplay("Deviation Multiplier", "Multiplier for standard deviation", "Settings");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetGreaterThanZero()
.SetOptimize(1m, 3m, 0.5m)
.SetDisplay("Stop Loss %", "Stop loss as percentage of entry price", "Risk Management");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMacdHist = 0;
_avgMacdHist = 0;
_stdDevMacdHist = 0;
_sumMacdHist = 0;
_sumSquaresMacdHist = 0;
_count = 0;
_macdHistValues.Clear();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
// Reset variables
// Create MACD indicator
var macd = new MovingAverageConvergenceDivergenceHistogram
{
Macd =
{
ShortMa = { Length = FastMacdPeriod },
LongMa = { Length = SlowMacdPeriod },
},
SignalMa = { Length = SignalPeriod }
};
var macdHistogram = new MovingAverageConvergenceDivergenceHistogram(macd.Macd, new());
// Create subscription and bind indicator
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(macdHistogram, ProcessCandle)
.Start();
// Setup chart visualization
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, macd);
DrawIndicator(area, macdHistogram);
DrawOwnTrades(area);
}
// Enable position protection
StartProtection(
takeProfit: new Unit(0m), // We'll manage exits ourselves based on MACD Histogram
stopLoss: new Unit(StopLossPercent, UnitTypes.Percent)
);
base.OnStarted2(time);
}
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;
// Extract MACD Histogram value
var macdTyped = (MovingAverageConvergenceDivergenceHistogramValue)macdValue;
if (macdTyped.Macd is not decimal macd || macdTyped.Signal is not decimal signal)
{
return;
}
// Update MACD Histogram statistics
UpdateMacdHistStatistics(macd);
// Save current MACD Histogram for next iteration
_prevMacdHist = macd;
// If we don't have enough data yet for statistics
if (_count < AveragePeriod)
return;
// Check for entry conditions
if (Position == 0)
{
// Long entry - MACD Histogram is significantly below its average
if (macd < _avgMacdHist - DeviationMultiplier * _stdDevMacdHist)
{
BuyMarket(Volume);
LogInfo($"Long entry: MACD Hist = {macd}, Avg = {_avgMacdHist}, StdDev = {_stdDevMacdHist}");
}
// Short entry - MACD Histogram is significantly above its average
else if (macd > _avgMacdHist + DeviationMultiplier * _stdDevMacdHist)
{
SellMarket(Volume);
LogInfo($"Short entry: MACD Hist = {macd}, Avg = {_avgMacdHist}, StdDev = {_stdDevMacdHist}");
}
}
// Check for exit conditions
else if (Position > 0) // Long position
{
if (macd > _avgMacdHist)
{
ClosePosition();
LogInfo($"Long exit: MACD Hist = {macd}, Avg = {_avgMacdHist}");
}
}
else if (Position < 0) // Short position
{
if (macd < _avgMacdHist)
{
ClosePosition();
LogInfo($"Short exit: MACD Hist = {macd}, Avg = {_avgMacdHist}");
}
}
}
private void UpdateMacdHistStatistics(decimal currentMacdHist)
{
// Add current value to the queue
_macdHistValues.Enqueue(currentMacdHist);
_sumMacdHist += currentMacdHist;
_sumSquaresMacdHist += currentMacdHist * currentMacdHist;
_count++;
// If queue is larger than period, remove oldest value
if (_macdHistValues.Count > AveragePeriod)
{
var oldestMacdHist = _macdHistValues.Dequeue();
_sumMacdHist -= oldestMacdHist;
_sumSquaresMacdHist -= oldestMacdHist * oldestMacdHist;
_count--;
}
// Calculate average and standard deviation
if (_count > 0)
{
_avgMacdHist = _sumMacdHist / _count;
if (_count > 1)
{
var variance = (_sumSquaresMacdHist - (_sumMacdHist * _sumMacdHist) / _count) / (_count - 1);
_stdDevMacdHist = variance <= 0 ? 0 : (decimal)Math.Sqrt((double)variance);
}
else
{
_stdDevMacdHist = 0;
}
}
}
}
import clr
import math
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import MovingAverageConvergenceDivergenceSignal
from StockSharp.Algo.Strategies import Strategy
class macd_mean_reversion_strategy(Strategy):
"""
MACD Histogram Mean Reversion: enters when histogram is significantly above/below its average.
"""
def __init__(self):
super(macd_mean_reversion_strategy, self).__init__()
self._fast_period = self.Param("FastMacdPeriod", 12).SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowMacdPeriod", 26).SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._signal_period = self.Param("SignalPeriod", 9).SetDisplay("Signal Period", "Signal line period", "Indicators")
self._average_period = self.Param("AveragePeriod", 20).SetDisplay("Average Period", "Period for histogram stats", "Settings")
self._dev_mult = self.Param("DeviationMultiplier", 2.0).SetDisplay("Dev Mult", "Stddev multiplier", "Settings")
self._sl_pct = self.Param("StopLossPercent", 2.0).SetDisplay("SL %", "Stop loss percent", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Timeframe", "General")
self._hist_values = []
self._avg_hist = 0.0
self._std_hist = 0.0
self._sum = 0.0
self._sum_sq = 0.0
self._cnt = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(macd_mean_reversion_strategy, self).OnReseted()
self._hist_values = []
self._avg_hist = 0.0
self._std_hist = 0.0
self._sum = 0.0
self._sum_sq = 0.0
self._cnt = 0
def OnStarted2(self, time):
super(macd_mean_reversion_strategy, self).OnStarted2(time)
macd = MovingAverageConvergenceDivergenceSignal()
macd.Macd.ShortMa.Length = self._fast_period.Value
macd.Macd.LongMa.Length = self._slow_period.Value
macd.SignalMa.Length = self._signal_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(macd, self._process_candle).Start()
self.StartProtection(None, Unit(self._sl_pct.Value, UnitTypes.Percent))
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
if not self.IsFormedAndOnlineAndAllowTrading():
return
typed_val = macd_value
if typed_val.Macd is None or typed_val.Signal is None:
return
macd_line = float(typed_val.Macd)
# CS uses macd line value for statistics (not histogram)
lb = int(self._average_period.Value)
self._hist_values.append(macd_line)
self._sum += macd_line
self._sum_sq += macd_line * macd_line
self._cnt += 1
if len(self._hist_values) > lb:
oldest = self._hist_values.pop(0)
self._sum -= oldest
self._sum_sq -= oldest * oldest
self._cnt -= 1
if self._cnt > 0:
avg = self._sum / self._cnt
if self._cnt > 1:
variance = (self._sum_sq - (self._sum * self._sum) / self._cnt) / (self._cnt - 1)
std = math.sqrt(variance) if variance > 0 else 0.0
else:
std = 0.0
else:
avg = 0.0
std = 0.0
self._avg_hist = avg
self._std_hist = std
if self._cnt < lb:
return
dm = float(self._dev_mult.Value)
if self.Position == 0:
if macd_line < avg - dm * std:
self.BuyMarket(self.Volume)
elif macd_line > avg + dm * std:
self.SellMarket(self.Volume)
elif self.Position > 0:
if macd_line > avg:
self.ClosePosition()
elif self.Position < 0:
if macd_line < avg:
self.ClosePosition()
def CreateClone(self):
return macd_mean_reversion_strategy()