Diese Strategie ist ein direkter Port des klassischen MetaTrader 5 MACD Sample-Expertenberaters. Sie verwendet MACD-Kreuzungen, gefiltert durch einen EMA-Trendindikator. Aufträge werden mit der Standard-Eigenschaft Volume dimensioniert, während das Risikomanagement auf konfigurierbaren Pip-Schwellenwerten für das MACD-Histogramm, Take Profit und Trailing Stop basiert.
Kernlogik
Indikatoren
MovingAverageConvergenceDivergenceSignal mit Perioden (12, 26, 9) liefert MACD- und Signallinien.
ExponentialMovingAverage mit Periode 26 fungiert als Trendfilter.
Einstiegskriterien
Long: MACD liegt unter null, kreuzt die Signallinie nach oben, hat eine Magnitude über dem MACD Open Level und die EMA steigt.
Short: MACD liegt über null, kreuzt die Signallinie nach unten, hat eine Magnitude über dem MACD Open Level und die EMA fällt.
Ausstiegskriterien
MACD kreuzt gegen die Position mit einer Magnitude über dem MACD Close Level.
Take Profit erreicht die konfigurierte Pip-Distanz vom Einstiegspreis.
Trailing Stop (falls durch Gewinn > Trailing-Distanz aktiviert) wird ausgelöst.
Trailing-Stop-Mechanik
Long-Positionen aktivieren den Trailing Stop, sobald der Hochkurs den Einstiegspreis um die Trailing-Distanz überschreitet. Der Stop wird dann bei Hoch − Trailing-Distanz gehalten.
Short-Positionen aktivieren den Trailing Stop, sobald der Tiefstkurs unter den Einstiegspreis um die Trailing-Distanz fällt. Der Stop wird bei Tief + Trailing-Distanz gehalten.
Parameter
Parameter
Standardwert
Beschreibung
FastPeriod
12
Schnelle EMA-Periode innerhalb des MACD.
SlowPeriod
26
Langsame EMA-Periode innerhalb des MACD.
SignalPeriod
9
Signal-EMA-Periode innerhalb des MACD.
TrendPeriod
26
Länge des EMA-Trendfilters.
MacdOpenLevelPips
3
Minimale MACD-Magnitude (in Pips), die zum Öffnen eines Trades erforderlich ist.
MacdCloseLevelPips
2
Minimale MACD-Magnitude (in Pips), die zum Schließen eines Trades bei Kreuzung erforderlich ist.
TakeProfitPips
50
Take-Profit-Distanz in Pips.
TrailingStopPips
30
Trailing-Stop-Distanz in Pips. Auf 0 setzen, um Trailing zu deaktivieren.
CandleType
15-Minuten-Zeitrahmen
Kerzentyp für Berechnungen.
Pip-Konvertierung
Der ursprüngliche Expertenberater verwendete MetaTraders Pip-Normalisierung (Multiplikation mit 10 für 3/5-stellige Symbole). Die Konvertierung folgt derselben Idee durch Inspektion von Security.PriceStep:
Wenn der Preisschritt 3 oder 5 Nachkommastellen hat, ist die Pip-Größe PriceStep * 10.
Andernfalls entspricht die Pip-Größe PriceStep.
Wenn der Preisschritt nicht verfügbar ist, fallen Pip-basierte Schwellenwerte auf Rohwerte zurück.
Verhaltenshinweise
Positionen werden geschlossen, bevor neue Signale ausgewertet werden, was die MT5-Implementierung spiegelt.
LogInfo-Anweisungen melden Einstiege, Ausstiege und Trailing-Stop-Aktualisierungen zur einfacheren Fehlerbehebung.
Schutzorders werden nicht automatisch platziert; Ausstiege werden innerhalb von ProcessCandle verwaltet, um die EA-Logik zu imitieren.
Verwenden Sie Volume, um die Basis-Trade-Größe zu definieren. Umkehrungen gleichen automatisch die aktuelle Exposition aus, indem Math.Abs(Position) zum Auftragsvolumen hinzugefügt wird.
Unterschiede zur MQL5-Version
Die Verarbeitung erfolgt auf abgeschlossenen Kerzen statt bei jedem Tick. Dies vermeidet wiederholte Signale und hält das Verhalten deterministisch.
Trailing-Stop- und Take-Profit-Prüfungen verwenden Kerzenhochs und -tiefs, um Bid/Ask-Treffer aus dem ursprünglichen EA anzunähern.
Wenn Security.PriceStep fehlt, wirken Pip-Parameter als absolute Preisabstände und müssen manuell eingestellt werden.
Passen Sie die Pip-Schwellenwerte und den Kerzentyp an das gehandelte Instrument an, insbesondere beim Portieren auf Märkte mit unterschiedlichen Tick-Größen.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// MACD strategy with EMA trend filter.
/// Buys when MACD histogram is positive and price is above trend EMA.
/// Sells when MACD histogram is negative and price is below trend EMA.
/// </summary>
public class MacdSampleTrendFilterStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _trendPeriod;
private int _prevSignal;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
public int TrendPeriod
{
get => _trendPeriod.Value;
set => _trendPeriod.Value = value;
}
public MacdSampleTrendFilterStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast EMA for MACD", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow EMA for MACD", "Indicators");
_trendPeriod = Param(nameof(TrendPeriod), 100)
.SetGreaterThanZero()
.SetDisplay("Trend Period", "Trend EMA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevSignal = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevSignal = 0;
var fastEma = new ExponentialMovingAverage { Length = FastPeriod };
var slowEma = new ExponentialMovingAverage { Length = SlowPeriod };
var trendEma = new ExponentialMovingAverage { Length = TrendPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastEma, slowEma, trendEma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastEma);
DrawIndicator(area, slowEma);
DrawIndicator(area, trendEma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow, decimal trend)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
var macdLine = fast - slow;
// Signal: MACD positive + price above trend = bullish; MACD negative + price below trend = bearish
var signal = 0;
if (macdLine > 0 && close > trend)
signal = 1;
else if (macdLine < 0 && close < trend)
signal = -1;
if (signal == _prevSignal)
return;
var oldSignal = _prevSignal;
_prevSignal = signal;
if (signal == 1 && oldSignal <= 0)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
}
else if (signal == -1 && oldSignal >= 0)
{
if (Position > 0)
SellMarket();
if (Position >= 0)
SellMarket();
}
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class macd_sample_trend_filter_strategy(Strategy):
def __init__(self):
super(macd_sample_trend_filter_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_period = self.Param("FastPeriod", 12) \
.SetDisplay("Fast Period", "Fast EMA for MACD", "Indicators")
self._slow_period = self.Param("SlowPeriod", 26) \
.SetDisplay("Slow Period", "Slow EMA for MACD", "Indicators")
self._trend_period = self.Param("TrendPeriod", 100) \
.SetDisplay("Trend Period", "Trend EMA period", "Indicators")
self._prev_signal = 0
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
@property
def TrendPeriod(self):
return self._trend_period.Value
def OnReseted(self):
super(macd_sample_trend_filter_strategy, self).OnReseted()
self._prev_signal = 0
def OnStarted2(self, time):
super(macd_sample_trend_filter_strategy, self).OnStarted2(time)
self._prev_signal = 0
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.FastPeriod
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self.SlowPeriod
trend_ema = ExponentialMovingAverage()
trend_ema.Length = self.TrendPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast_ema, slow_ema, trend_ema, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_ema)
self.DrawIndicator(area, slow_ema)
self.DrawIndicator(area, trend_ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value, trend_value):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
fv = float(fast_value)
sv = float(slow_value)
tv = float(trend_value)
macd_line = fv - sv
signal = 0
if macd_line > 0 and close > tv:
signal = 1
elif macd_line < 0 and close < tv:
signal = -1
if signal == self._prev_signal:
return
old_signal = self._prev_signal
self._prev_signal = signal
if signal == 1 and old_signal <= 0:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
elif signal == -1 and old_signal >= 0:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return macd_sample_trend_filter_strategy()