Strategie Simple MACD
Simple MACD repliziert die Logik des MQL5-Beraters Simple_MACD.mq5 in StockSharp. Die Strategie folgt der Steigung der MACD-Hauptlinie, die auf abgeschlossenen Kerzen berechnet wird, und fügt der Position weiterhin hinzu, solange die Steigung in dieselbe Richtung geht.
Überblick
- Markt: Jedes Instrument mit Kerzendate und durchgehenden Handelszeiten.
- Kernindikator: Moving Average Convergence Divergence (MACD) mit exponentiellen gleitenden Durchschnitten 12/26 und Signal 9.
- Ansatz: Momentum-Following. Die Strategie vergleicht die beiden aktuellsten abgeschlossenen MACD-Lesewerte und geht long, wenn die Linie steigt, oder short, wenn die Linie fällt.
- Ordertyp: Nur Marktorders. Jedes Signal aggregiert den Betrag, der zum Schließen der entgegengesetzten Position erforderlich ist, und fügt das konfigurierte Handelsvolumen hinzu, was den Original-Expertenberater widerspiegelt.
Konvertierungshinweise
- Der MQL5-Bot löste einmal pro neuer Bar aus, indem er
MACD(1) und MACD(2) (die vorherigen zwei abgeschlossenen Bars) verglich. In StockSharp wird derselbe Vergleich ausgeführt, wenn eine Kerze endet, bevor die nächste Bar beginnt.
- Die MQL-Version stützte sich auf explizite Positions-Enumeration und manuelle Volumenprüfungen. Die StockSharp-Version aggregiert das Volumen automatisch mit
BuyMarket/SellMarket-Aufrufen und dem TradeVolume-Parameter der Strategie.
- Hedging-Prüfungen aus dem MQL-Code sind nicht erforderlich, da StockSharp die Nettoposition direkt verfolgt.
Handelsregeln
Einstieg und Skalierung
- MACD-Hauptlinie auf jeder fertigen Kerze berechnen.
- Die letzten zwei MACD-Werte speichern und vergleichen:
- Wenn
MACD(1) > MACD(2) ist die Steigung bullisch. Die Strategie kauft ein Volumen gleich TradeVolume + max(0, -Position), um Shorts zu schließen und neue Longs hinzuzufügen.
- Wenn
MACD(1) < MACD(2) ist die Steigung bärisch. Die Strategie verkauft TradeVolume + max(0, Position), um Longs zu schließen und neue Shorts hinzuzufügen.
- Wenn beide Werte gleich sind, werden keine neuen Orders eingereicht.
Positionsmanagement
- Die Strategie stapelt weiterhin Orders in der aktuellen Richtung, solange das MACD-Gefälle kein Vorzeichen ändert, genau wie der ursprüngliche Berater, der auf jeder qualifizierenden Bar einen Kauf oder Verkauf sendete.
- Entgegengesetzte Signale schließen jede offene Exposure, bevor die neue Position aufgebaut wird.
- Keine Stop-Loss- oder Take-Profit-Niveaus sind eingebettet; die Risikokontrolle basiert auf externen Money-Management-Regeln oder manueller Überwachung.
Zusätzliche Sicherheitsvorkehrungen
- Das Trading wird übersprungen, bis der MACD-Indikator vollständig gebildet ist.
- Nur abgeschlossene Kerzen (
CandleStates.Finished) werden verarbeitet, um vorzeitige Aktionen bei unvollständigen Daten zu verhindern.
- Log-Nachrichten verfolgen jeden Trade und zeigen die beiden MACD-Werte, die für die Entscheidung verwendet wurden, für einfachere Backtesting-Analyse.
Parameter
| Parameter |
Standardwert |
Beschreibung |
FastPeriod |
12 |
Schnelle EMA-Länge für die MACD-Berechnung. |
SlowPeriod |
26 |
Langsame EMA-Länge für die MACD-Berechnung. |
SignalPeriod |
9 |
Signal-EMA-Periode, die aus Kompatibilitätsgründen mit den ursprünglichen Einstellungen beibehalten wird. |
TradeVolume |
0.1 |
Volumen, das bei jedem Signal vor Berücksichtigung der Positionsumkehr hinzugefügt wird. |
CandleType |
1-Minuten-Zeitrahmen |
Kerzentyp zur Speisung des Indikators. Auf jeden gewünschten Zeitrahmen anpassbar. |
Alle Parameter sind als Strategieparameter exponiert und wo sinnvoll als optimierbar markiert.
Visualisierung
- Die Strategie erstellt automatisch einen Chartbereich (wenn verfügbar) mit den Preiskerzen und überlagert den MACD-Indikatorausgang.
- Eigene Trades werden auf dem Chart gezeichnet, um zu zeigen, wie häufig die Strategie Positionen in Trendbedingungen skaliert.
Empfohlene Verwendung
- Auf Trendinstrumenten anwenden, bei denen Momentum mehrere Bars anhält; seitwärts laufende Märkte werden häufige Umkehrungen und Whipsaw-Trades verursachen.
- Mit Portfolio-Level-Risikomanagement kombinieren, da die Basislogik keinen intrinsischen Stop-Mechanismus hat.
- In Betracht ziehen,
TradeVolume und MACD-Perioden für das Zielinstrument und den Zeitrahmen zu optimieren.
Dateien
CS/SimpleMacdStrategy.cs – StockSharp-Implementierung der Strategielogik.
README.md, README_ru.md, README_zh.md – detaillierte Dokumentation in drei Sprachen.
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>
/// Simple MACD slope-following strategy converted from MQL5 Simple_MACD.mq5.
/// The strategy evaluates the MACD main line on completed candles and builds positions accordingly.
/// </summary>
public class SimpleMacdStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _signalPeriod;
private readonly StrategyParam<decimal> _tradeVolume;
private readonly StrategyParam<DataType> _candleType;
private MovingAverageConvergenceDivergence _macd = null!;
private decimal? _previousMacdValue;
private decimal? _prePreviousMacdValue;
private int? _previousSlope;
/// <summary>
/// Fast EMA period used for the MACD main line.
/// </summary>
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
/// <summary>
/// Slow EMA period used for the MACD main line.
/// </summary>
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
/// <summary>
/// Signal EMA period used by the MACD indicator.
/// </summary>
public int SignalPeriod
{
get => _signalPeriod.Value;
set => _signalPeriod.Value = value;
}
/// <summary>
/// Trading volume applied when new orders are sent.
/// </summary>
public decimal TradeVolume
{
get => _tradeVolume.Value;
set => _tradeVolume.Value = value;
}
/// <summary>
/// Candle type used to feed the MACD indicator.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize Simple MACD strategy with default parameters.
/// </summary>
public SimpleMacdStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 12)
.SetDisplay("MACD Fast Period", "Fast EMA length for MACD calculation", "Indicators")
.SetOptimize(6, 18, 2);
_slowPeriod = Param(nameof(SlowPeriod), 26)
.SetDisplay("MACD Slow Period", "Slow EMA length for MACD calculation", "Indicators")
.SetOptimize(20, 40, 2);
_signalPeriod = Param(nameof(SignalPeriod), 9)
.SetDisplay("MACD Signal Period", "Signal EMA length maintained for compatibility", "Indicators")
.SetOptimize(6, 18, 1);
_tradeVolume = Param(nameof(TradeVolume), 0.1m)
.SetDisplay("Trade Volume", "Order volume used for each signal", "Risk")
.SetOptimize(0.1m, 1m, 0.1m);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for MACD calculations", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_previousMacdValue = null;
_prePreviousMacdValue = null;
_previousSlope = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Configure MACD indicator to match the source MQL strategy settings.
_macd = new MovingAverageConvergenceDivergence
{
ShortMa = { Length = FastPeriod },
LongMa = { Length = SlowPeriod },
};
// Subscribe to candle data and bind the MACD indicator.
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_macd, ProcessCandle)
.Start();
// Prepare visual elements when charts are available.
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _macd);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue)
{
// React only to completed candles to avoid premature decisions.
if (candle.State != CandleStates.Finished)
return;
// Ensure the indicator produced a valid value.
if (!_macd.IsFormed || !macdValue.IsFinal)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var macdLine = macdValue.ToDecimal();
// Accumulate historical MACD values for slope calculations.
if (_previousMacdValue is null)
{
_previousMacdValue = macdLine;
return;
}
if (_prePreviousMacdValue is null)
{
_prePreviousMacdValue = _previousMacdValue;
_previousMacdValue = macdLine;
return;
}
var macdPrev = _previousMacdValue.Value;
var macdPrevPrev = _prePreviousMacdValue.Value;
var currentSlope = macdPrev > macdPrevPrev ? 1 : macdPrev < macdPrevPrev ? -1 : 0;
if (currentSlope == 0)
{
_prePreviousMacdValue = _previousMacdValue;
_previousMacdValue = macdLine;
return;
}
if (_previousSlope == currentSlope)
{
_prePreviousMacdValue = _previousMacdValue;
_previousMacdValue = macdLine;
return;
}
if (currentSlope > 0)
{
// Close shorts and open (or add to) longs when the MACD slope turns positive.
var volumeToBuy = TradeVolume + Math.Max(0m, -Position);
if (volumeToBuy > 0m)
{
BuyMarket(volumeToBuy);
LogInfo($"Bullish slope detected. MACD(1)={macdPrev:F5}, MACD(2)={macdPrevPrev:F5}. Buying {volumeToBuy}.");
}
}
else
{
// Close longs and open (or add to) shorts when the MACD slope turns negative.
var volumeToSell = TradeVolume + Math.Max(0m, Position);
if (volumeToSell > 0m)
{
SellMarket(volumeToSell);
LogInfo($"Bearish slope detected. MACD(1)={macdPrev:F5}, MACD(2)={macdPrevPrev:F5}. Selling {volumeToSell}.");
}
}
// Update stored values so the next candle compares the two previous MACD readings.
_previousSlope = currentSlope;
_prePreviousMacdValue = _previousMacdValue;
_previousMacdValue = macdLine;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import MovingAverageConvergenceDivergence
from StockSharp.Algo.Strategies import Strategy
class simple_macd_strategy(Strategy):
def __init__(self):
super(simple_macd_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 12)
self._slow_period = self.Param("SlowPeriod", 26)
self._signal_period = self.Param("SignalPeriod", 9)
self._trade_volume = self.Param("TradeVolume", 0.1)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(simple_macd_strategy, self).OnReseted()
self._prev_macd = None
self._prev_prev_macd = None
self._prev_slope = None
def OnStarted2(self, time):
super(simple_macd_strategy, self).OnStarted2(time)
self._prev_macd = None
self._prev_prev_macd = None
self._prev_slope = None
self._macd = MovingAverageConvergenceDivergence()
self._macd.ShortMa.Length = self._fast_period.Value
self._macd.LongMa.Length = self._slow_period.Value
sub = self.SubscribeCandles(self.CandleType)
sub.BindEx(self._macd, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, self._macd)
self.DrawOwnTrades(area)
def OnProcess(self, candle, macd_val):
if candle.State != CandleStates.Finished:
return
if not self._macd.IsFormed or not macd_val.IsFinal:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
macd_line = float(macd_val)
if self._prev_macd is None:
self._prev_macd = macd_line
return
if self._prev_prev_macd is None:
self._prev_prev_macd = self._prev_macd
self._prev_macd = macd_line
return
prev = self._prev_macd
prev_prev = self._prev_prev_macd
if prev > prev_prev:
current_slope = 1
elif prev < prev_prev:
current_slope = -1
else:
current_slope = 0
if current_slope == 0:
self._prev_prev_macd = self._prev_macd
self._prev_macd = macd_line
return
if self._prev_slope == current_slope:
self._prev_prev_macd = self._prev_macd
self._prev_macd = macd_line
return
trade_vol = self._trade_volume.Value
if current_slope > 0:
volume_to_buy = trade_vol + max(0, -self.Position)
if volume_to_buy > 0:
self.BuyMarket(volume_to_buy)
else:
volume_to_sell = trade_vol + max(0, self.Position)
if volume_to_sell > 0:
self.SellMarket(volume_to_sell)
self._prev_slope = current_slope
self._prev_prev_macd = self._prev_macd
self._prev_macd = macd_line
def CreateClone(self):
return simple_macd_strategy()