Auf GitHub ansehen

MACD Power-Strategie

Überblick

Die MACD Power-Strategie ist ein Multi-Timeframe-Momentum-System, das aus dem ursprünglichen MetaTrader-Expert-Advisor konvertiert wurde. Die Logik kombiniert ein Paar linear gewichteter gleitender Durchschnitte (LWMA), berechnet auf dem primären Zeitrahmen, zwei MACD-Varianten, einen Momentum-Filter auf einem höheren Zeitrahmen und einen monatlichen MACD-Bias. Die Strategie versucht, an impulsiven Bewegungen teilzunehmen, sobald Momentum und Trendbedingungen des höheren Zeitrahmens übereinstimmen.

Kernlogik

  • Primäre gleitende Durchschnitte – Ein schneller und ein langsamer LWMA des typischen Kerzenpreises (((High + Low + Close) / 3)). Die Strategie erfordert, dass der schnelle Durchschnitt unter dem langsamen liegt, bevor ein Signal berücksichtigt wird, was den Originalcode widerspiegelt, der auf Rücksetzer innerhalb eines dominanten bearischen Gefälles wartet, bevor in Richtung des monatlichen Bias eingegangen wird.
  • Doppelte MACD-Bestätigung – Zwei MACD-Indikatoren mit Parametern (12, 26, 1) und (6, 13, 1) müssen beide über null für Long-Trades oder unter null für Short-Trades liegen. Diese Werte reproduzieren die MacdMAIN1- und MacdMAIN2-Bedingungen des MQL-Experten, die kurzfristige Beschleunigung messen.
  • Momentum-Filter – Momentum (Länge 14) wird auf einem höheren Zeitrahmen berechnet, der aus der primären Kerzengröße abgeleitet wird (z.B. 15-Minuten-Basis → 1-Stunden-Momentum). Der absolute Abstand von 100 wird über die letzten drei Momentum-Lesungen überwacht; mindestens eine davon muss den konfigurierten Schwellenwert überschreiten, um zu bestätigen, dass sich der Preis entschlossen bewegt.
  • Monatlicher MACD-Bias – Ein monatlicher (12, 26, 9) MACD (identisch mit MacdMAIN0/MacdSIGNAL0 im EA) muss seine Hauptlinie über der Signallinie für Long-Trades und unter der Signallinie für Shorts haben. Dies schützt vor dem Handel gegen den dominanten Makrotrend.

Handelsverwaltung

  • Entry-Sizing – Der Parameter OrderVolume definiert die Basis-Ordergröße. Wenn eine Positionsumkehr erforderlich ist, fügt die Engine automatisch die Größe der entgegengesetzten Position hinzu, sodass das Nettovolumen in einer einzigen Market-Order umgekehrt wird.
  • Take-Profit / Stop-Loss – Absolute Distanzen werden in Instrumentenpunkten ausgedrückt und mit Security.PriceStep in Preise umgerechnet (mit sicherem Fallback auf 1).
  • Trailing-Stop – Sobald sich der Preis um TrailingActivationPoints zugunsten bewegt, verfolgt der Stop den höchsten (Long) oder niedrigsten (Short) Preis mit einem durch TrailingOffsetPoints definierten Offset.
  • Break-Even – Wenn der Preis BreakEvenTriggerPoints erreicht, wird ein synthetischer Break-Even-Stop bei Entry ± BreakEvenOffsetPoints gesetzt. Wenn der Preis auf dieses Niveau zurückgeht, wird die Position geschlossen.
  • HandelslimitMaxTrades begrenzt die Anzahl der Positionseröffnungen pro Lauf; sobald der Schwellenwert erreicht ist, werden keine neuen Einstiege ausgegeben.

Parameter

Name Beschreibung Standard
CandleType Primärer Zeitrahmen für die Signalgenerierung. 15-Minuten-Kerzen
FastMaLength Länge des schnellen LWMA (typischer Preis). 6
SlowMaLength Länge des langsamen LWMA (typischer Preis). 85
MomentumLength Momentum-Lookback auf dem höheren Zeitrahmen. 14
MomentumBuyThreshold Minimaler absoluter Abstand von 100 für bullisches Momentum. 0.3
MomentumSellThreshold Minimaler absoluter Abstand von 100 für bearisches Momentum. 0.3
TakeProfitPoints Take-Profit-Distanz in Instrumentenpunkten. 50
StopLossPoints Stop-Loss-Distanz in Instrumentenpunkten. 20
TrailingActivationPoints Gewinn (Punkte) erforderlich, bevor Trailing aktiviert. 40
TrailingOffsetPoints Abstand (Punkte) zwischen Trailing-Stop und Extrempreis. 40
BreakEvenTriggerPoints Gewinn (Punkte), der den Break-Even-Schutz aktiviert. 30
BreakEvenOffsetPoints Offset (Punkte) beim Verschieben des Stops auf Break-Even. 30
MaxTrades Maximale Anzahl der Trades pro Sitzung. 10
OrderVolume Basis-Ordervolumen. 1

Unterschiede zum MQL-Experten

  • Die Strategie verwendet die StockSharp High-Level-API (SubscribeCandles + Bind/BindEx) anstelle von direktem Tick-Polling. Indikatorwerte werden nur nach abgeschlossenen Kerzen verarbeitet.
  • Geldbasierte Trailing- und Eigenkapital-Stop-Blöcke aus dem Originalcode werden nicht portiert, da das kontoebene Geldmanagement normalerweise durch das StockSharp-Risikoframework gehandhabt wird. Stattdessen bleiben punktbasiertes Trailing und Break-Even erhalten und können konfiguriert werden, um das EA-Verhalten zu emulieren.
  • Alerts, Benachrichtigungen und manuelle Ordermodifikationshilfen aus MQL werden weggelassen; die StockSharp-Engine verwaltet Orders direkt über Market-Calls.

Verwendungshinweise

  1. Wählen Sie den primären Zeitrahmen durch Einstellen von CandleType. Höherer Zeitrahmen-Momentum und monatlicher MACD werden automatisch gemäß dem in GetMomentumCandleType() implementierten Mapping abgeleitet.
  2. Richten Sie TakeProfitPoints, StopLossPoints und die Trailing/Break-Even-Parameter an der Tick-Größe des Instruments aus. Die Standardwerte spiegeln die 5-stelligen Forex-Einstellungen des EA wider, können aber für andere Märkte angepasst werden.
  3. Überwachen Sie den MaxTrades-Zähler bei automatisierten Backtests; setzen Sie ihn auf eine große Zahl, wenn das martingaleartige Stapelverhalten des ursprünglichen EA gewünscht wird.
  4. Für die visuelle Analyse aktivieren Sie die Diagrammdarstellung in der GUI – die Implementierung zeichnet standardmäßig Kerzen und die beiden LWMA-Kurven.
using System;
using System.Collections.Generic;

using Ecng.Common;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

public class MacdPowerStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public MacdPowerStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 12).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}