Diese Strategie reproduziert das Verhalten von Yury Reshetovs "MACDSimple"-MetaTrader-Expertenberaters im StockSharp-Framework. Sie arbeitet mit einem einzigen Wertpapier und bewertet klassische MACD-Signale, die durch zwei Offset-Parameter modifiziert werden. Der Algorithmus verarbeitet nur abgeschlossene Kerzen, stellt sicher, dass alle Handelsentscheidungen auf bestätigten Daten getroffen werden, und vermeidet Intrabar-Rauschen.
Indikatoren und Berechnungen
MACD (Moving Average Convergence Divergence) – die MACD-Linie und Signallinie werden mit benutzerdefinierten Perioden berechnet:
Schnelle EMA-Periode = SignalPeriod + DF
Langsame EMA-Periode = SignalPeriod + DS + DF
Signallinienperiode = SignalPeriod
Die Offsets DF und DS folgen den ursprünglichen Experteineingaben und ermöglichen dem Trader, die MACD-Komponenten zu strecken oder zu komprimieren, während ihre Beziehung intakt bleibt.
Parameter
Name
Beschreibung
Standard
Volume
Für jeden Markteinstieg verwendete Ordergröße.
2
DF
Zum schnellen MACD-EMA-Länge hinzugefügter Offset. Muss null oder positiv sein.
1
DS
Zusätzlicher Offset auf die langsame MACD-EMA-Länge angewendet. Muss null oder positiv sein.
2
SignalPeriod
Basisperiode, aus der die schnellen und langsamen EMA-Längen abgeleitet werden.
10
CandleType
Zeitrahmen der für Analyse und Handel verwendeten Kerzen.
30-Minuten-Zeitrahmen
Handelslogik
Positionshandling
Bei jeder fertigen Kerze aktualisiert die Strategie den MACD-Indikator und ignoriert die Bar, wenn der Indikator noch nicht vollständig geformt ist.
Wenn eine Long-Position offen ist und die MACD-Linie unter null fällt, schließt die Strategie die gesamte Long-Position zum Marktpreis.
Wenn eine Short-Position offen ist und die MACD-Linie über null steigt, schließt die Strategie die gesamte Short-Position zum Marktpreis.
Nach dem Schließen einer Position in einer gegebenen Bar hört der Algorithmus auf, diese Bar zu verarbeiten, und spiegelt das Verhalten des ursprünglichen Expertenberaters wider.
Einstiegsregeln
Der Algorithmus überprüft, dass sowohl die MACD-Linie als auch die Signallinie dasselbe Vorzeichen haben (beide positiv oder beide negativ). Gemischte Vorzeichen produzieren keine Trades.
Wenn beide Linien positiv sind, wird eine Long-Position eröffnet, wenn die MACD-Linie über der Signallinie liegt.
Wenn beide Linien negativ sind, wird eine Short-Position eröffnet, wenn die MACD-Linie unter der Signallinie liegt.
Marktorders haben die mit dem Volume-Parameter konfigurierte Größe. Es kann jeweils nur eine Position existieren.
Ausstiegsregeln
Ausstiege werden ausschließlich durch die MACD-Linie getrieben, die das Null-Niveau gegen die offene Position kreuzt, wie im Positionshandling-Abschnitt beschrieben. Standardmäßig sind keine Teilausstiege, Stop-Losses oder Take-Profits implementiert.
Zusätzliche Hinweise
Die Strategie handelt nur wenn IsFormedAndOnlineAndAllowTrading() erfüllt ist, um sicherzustellen, dass Live-Daten verfügbar und der Handel aktiviert sind, bevor neue Positionen eingegangen werden.
Es ist kein automatisches Risikomanagement eingebaut. Benutzer können bei Bedarf benutzerdefinierte Schutzmaßnahmen wie StartProtection() hinzufügen oder die Strategie mit Portfolio-Risikokontrollen kombinieren.
Da die MACD-Parameter aus einer einzigen Basisperiode plus Offsets abgeleitet werden, beeinflusst das Anpassen von SignalPeriod, DF oder DS alle Komponenten gleichzeitig und bewahrt den relativen Abstand des ursprünglichen Expertenberaters.
Implementierungsdetails
Die Indikatorbindung verwendet StockSharp's High-Level SubscribeCandles().Bind() API und hält die Implementierung prägnant und ereignisgesteuert.
Die Konvertierung folgt dem in AGENTS.md beschriebenen Regelwerk: Tabs werden für Einrückungen verwendet, Indikatorwerte werden direkt aus dem Binding-Callback konsumiert, und Handelsfunktionen BuyMarket/SellMarket verwalten Ein- und Ausstiege.
Die Strategiestruktur ist bereit für Erweiterungen (z.B. Hinzufügen von Filtern oder Risikostatik) und bleibt dabei der ursprünglichen MetaTrader-Expertenlogik treu.
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>
/// MACD-based strategy adapted from Yury Reshetov's MACDSimple expert advisor.
/// </summary>
public class MacdSimpleReshetovStrategy : Strategy
{
private readonly StrategyParam<int> _df;
private readonly StrategyParam<int> _ds;
private readonly StrategyParam<int> _signalPeriod;
private readonly StrategyParam<DataType> _candleType;
private MovingAverageConvergenceDivergenceSignal _macd;
public int Df { get => _df.Value; set => _df.Value = value; }
public int Ds { get => _ds.Value; set => _ds.Value = value; }
public int SignalPeriod { get => _signalPeriod.Value; set => _signalPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MacdSimpleReshetovStrategy()
{
_df = Param(nameof(Df), 1)
.SetNotNegative()
.SetDisplay("DF", "Offset for the fast EMA", "Indicators");
_ds = Param(nameof(Ds), 2)
.SetNotNegative()
.SetDisplay("DS", "Offset for the slow EMA", "Indicators");
_signalPeriod = Param(nameof(SignalPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("Signal Period", "Signal line period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// The MQL version derives MACD periods from the signal period with DF and DS offsets.
var fastPeriod = SignalPeriod + Df;
var slowPeriod = SignalPeriod + Ds + Df;
_macd = new MovingAverageConvergenceDivergenceSignal
{
Macd = { ShortMa = { Length = fastPeriod }, LongMa = { Length = slowPeriod } },
SignalMa = { Length = SignalPeriod }
};
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var result = _macd.Process(candle);
if (!_macd.IsFormed)
return;
var macdValue = result as MovingAverageConvergenceDivergenceSignalValue;
if (macdValue == null)
return;
var macdLine = macdValue.Macd ?? 0m;
var signalLine = macdValue.Signal ?? 0m;
// Manage existing positions before evaluating new signals.
if (Position > 0)
{
if (macdLine < 0m)
SellMarket(Position);
return;
}
if (Position < 0)
{
if (macdLine > 0m)
BuyMarket(-Position);
return;
}
// Enter only when MACD and signal lines share the same sign.
if (macdLine * signalLine <= 0m)
return;
if (macdLine > 0m && macdLine > signalLine)
{
BuyMarket(Volume);
}
else if (macdLine < 0m && macdLine < signalLine)
{
SellMarket(Volume);
}
}
}
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, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
class macd_simple_reshetov_strategy(Strategy):
def __init__(self):
super(macd_simple_reshetov_strategy, self).__init__()
self._df = self.Param("Df", 1)
self._ds = self.Param("Ds", 2)
self._signal_period = self.Param("SignalPeriod", 10)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4)))
self._macd = None
@property
def Df(self):
return self._df.Value
@property
def Ds(self):
return self._ds.Value
@property
def SignalPeriod(self):
return self._signal_period.Value
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(macd_simple_reshetov_strategy, self).OnStarted2(time)
fast_period = self.SignalPeriod + self.Df
slow_period = self.SignalPeriod + self.Ds + self.Df
self._macd = MovingAverageConvergenceDivergenceSignal()
self._macd.Macd.ShortMa.Length = fast_period
self._macd.Macd.LongMa.Length = slow_period
self._macd.SignalMa.Length = self.SignalPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
civ = CandleIndicatorValue(self._macd, candle)
civ.IsFinal = True
result = self._macd.Process(civ)
if not self._macd.IsFormed:
return
try:
macd_line = float(result.Macd) if result.Macd is not None else 0.0
signal_line = float(result.Signal) if result.Signal is not None else 0.0
except:
return
pos = float(self.Position)
if pos > 0:
if macd_line < 0:
self.SellMarket(pos)
return
if pos < 0:
if macd_line > 0:
self.BuyMarket(abs(pos))
return
if macd_line * signal_line <= 0:
return
if macd_line > 0 and macd_line > signal_line:
self.BuyMarket(float(self.Volume))
elif macd_line < 0 and macd_line < signal_line:
self.SellMarket(float(self.Volume))
def OnReseted(self):
super(macd_simple_reshetov_strategy, self).OnReseted()
self._macd = None
def CreateClone(self):
return macd_simple_reshetov_strategy()