Auf GitHub ansehen

Konsolidierungsausbruch-Strategie

Diese Strategie reproduziert das Kernverhalten des ursprünglichen Consolidation Breakout Expert Advisors für MetaTrader. Sie sucht nach engen Konsolidierungen, die durch Momentum- und MACD-Filter bestätigt werden, und eröffnet anschließend eine Position in Ausbruchsrichtung. Risiko wird über feste Take-Profit- und Stop-Loss-Distanzen in Preisschritten (Pips) gesteuert.

Funktionsweise

  1. Der primäre Zeitrahmen wird durch den Parameter CandleType definiert. Alle Trend- und Konsolidierungsprüfungen werden auf diesen Kerzen bewertet.
  2. Zwei linear gewichtete gleitende Durchschnitte (LWMAs), berechnet auf dem typischen Preis, liefern den Richtungsfilter. Long-Setups verlangen, dass die schnelle LWMA über der langsamen LWMA bleibt, während Short-Setups die entgegengesetzte Ausrichtung benötigen.
  3. Eine Konsolidierung wird erkannt, wenn das Tief der Kerze vor zwei Bars unter dem Hoch der vorherigen Kerze bleibt (Long-Fall) oder wenn das vorherige Tief unter dem Hoch von vor zwei Bars liegt (Short-Fall). Dies spiegelt die Overlap-Bar-Logik der MQL-Version.
  4. Momentum muss die Bewegung bestätigen. Der absolute Momentum-Wert (relativ zu null) muss den jeweiligen Kauf- oder Verkaufsschwellenwert überschreiten. Dies nähert den Momentum-Filter des ursprünglichen Expert Advisors um das Niveau 100 an.
  5. Ein separater MACD, berechnet auf dem Zeitrahmen MacdCandleType, muss mit der Handelsrichtung übereinstimmen. Die Strategie prüft, ob die MACD-Linie der Signallinie sowohl auf der positiven als auch auf der negativen Seite der Achse vorausläuft, wodurch die Multi-Timeframe-Bestätigung aus dem Quellcode reproduziert wird.
  6. Wenn alle Filter übereinstimmen und das Konto flat oder in Gegenrichtung positioniert ist, sendet die Strategie eine Marktorder mit der Größe TradeVolume. Schutzlevel werden sofort in Preisschritten neu berechnet, sodass Intrabar-Extreme Ausstiege auslösen können.
  7. Jede abgeschlossene Kerze überwacht auch aktive Positionen. Wenn die Kerzenspanne Stop-Loss- oder Take-Profit-Niveau berührt, schließt die Strategie die Position zum Markt und setzt die Schutzziele zurück.

Indikatoren

  • Linear gewichteter gleitender Durchschnitt (schnell und langsam, typischer Preis)
  • Momentum
  • MACD (mit 12/26/9-Perioden auf einem höheren Zeitrahmen)

Parameter

  • CandleType - primärer Zeitrahmen für die Ausbruchserkennung.
  • MacdCandleType - Zeitrahmen für den bestätigenden MACD-Filter.
  • FastMaPeriod - Länge der schnellen LWMA.
  • SlowMaPeriod - Länge der langsamen LWMA.
  • MomentumLength - Rückblick für den Momentum-Filter.
  • MomentumBuyThreshold - minimales positives Momentum für Long-Trades.
  • MomentumSellThreshold - minimales negatives Momentum für Short-Trades (als absoluter Wert ausgedrückt).
  • StopLossPips - Schutz-Stop-Distanz in Preisschritten.
  • TakeProfitPips - Gewinnziel-Distanz in Preisschritten.
  • TradeVolume - Volumen, das mit jeder Marktorder gesendet wird.

Die Standardwerte spiegeln den veröffentlichten Expert Advisor: LWMA-Perioden 6 und 85, Momentum-Länge 14, Kauf-/Verkaufsschwellen 0.3, Stop-Loss 20 Pips und Take-Profit 50 Pips. Passen Sie pipbasierte Distanzen an, wenn Sie Instrumente mit anderen Preisschritten handeln.

Hinweise

  • Trailing Stops, Break-even-Bewegungen und Money-Management-Module aus dem MQL-Skript werden bewusst weggelassen, damit die StockSharp-Portierung auf die Kernlogik des Ausbruchs fokussiert bleibt.
  • Stellen Sie immer sicher, dass die ausgewählten Zeitrahmen von Ihrem Datenfeed unterstützt werden. Wenn der höhere Zeitrahmen nur spärliche Daten liefert, erwägen Sie einen niedrigeren MacdCandleType, um den MACD-Filter reaktionsfähig zu halten.
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 ConsolidationBreakoutStrategy : 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 ConsolidationBreakoutStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).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;
	}
}