Auf GitHub ansehen

Morgen Abend Stochastic Strategie

Diese Strategie portiert den MetaTrader 5 Fachberater Expert_AMS_ES_Stoch (Morning/Evening Star mit Stochastic Bestätigung) in StockSharp. Die Implementierung behält die ursprünglichen Regeln für die Erkennung von Kerzenmustern und die stochastische Bestätigung bei und verwendet gleichzeitig das High-Level-Kerzenabonnement API, sodass jede Entscheidung anhand fertiger Balken getroffen wird.

Strategielogik

  • Indikatoren
    • Standard-Stochastic-Oszillator mit konfigurierbaren %K-, %D- und Verlangsamungsperioden.
    • Einfacher gleitender Durchschnitt der Kerzenkörpergröße (absoluter open-close), um Kerzen wie bei der MQL-Version als „lang“ oder „klein“ zu klassifizieren.
  • Long Entry
    • Morning Star-Muster über die letzten drei abgeschlossenen Kerzen:
      1. Vor zwei Balken: langer bärischer Körper, dessen Größe den Körperdurchschnitt übersteigt.
      2. Vorheriger Balken: Kerze mit kleinem Körper, die unterhalb der vorherigen Kerze schließt und öffnet.
      3. Aktueller Balken: bullischer Schlusskurs über dem Mittelpunkt der ersten Kerze.
    • Die Signallinie Stochastic (%D) liegt unter dem Überverkaufsschwellenwert (Standardwert 30).
    • Das bestehende Short-Engagement wird abgeflacht, bevor die Long-Position eröffnet wird.
  • Short Entry
    • Evening Star-Muster, das die oben genannten Regeln widerspiegelt.
    • Stochastic %D liegt über dem Überkaufschwellenwert (Standardwert 70).
    • Bestehende Long-Positionen werden geschlossen, bevor der Short-Trade eröffnet wird.
  • Positionsausgang
    • Shorts werden geschlossen, wenn %D entweder den Wert für die schnelle Erholung (20) oder den Wert für die extreme Erholung (80) überschreitet.
    • Long-Positionen werden geschlossen, wenn %D entweder 80 oder 20 unterschreitet.
    • Diese Kreuzungen reproduzieren die „Schließbedingungen“ aus dem Signalmodul MQL.

Parameter

Name Beschreibung
CandleType Zeitrahmen (oder ein anderer DataType), der für die Mustererkennung und alle Indikatoren verwendet wird.
StochasticKPeriod, StochasticDPeriod, StochasticSlowing %K, %D und Verlangsamungsperioden des stochastischen Oszillators.
StochasticOverbought, StochasticOversold Signalleitungsschwellenwerte, die zur Bestätigung von Evening/Morning Star-Einträgen verwendet werden.
PatternAveragePeriod Anzahl der fertigen Kerzen zur durchschnittlichen Körpergröße (|öffnen-schließen|).
ShortExitLevel, LongExitLevel %D-Ebenen, die kurze/lange Ausgänge erzwingen, wenn sie in die entgegengesetzte Richtung überquert werden.

Implementierungshinweise

  • Kerzen werden über SubscribeCandles().BindEx(...) verarbeitet; Der Code funktioniert nur mit fertigen Kerzen und ruft niemals GetValue() für Indikatoren auf.
  • Die Mittelung der Körpergröße basiert auf SimpleMovingAverage, das mit absoluten Kerzenkörpern gefüttert wird, um den AvgBody()-Helfer aus der MQL-Bibliothek zu reproduzieren.
  • Musterprüfungen werden mit speziellen Hilfsmethoden implementiert, um die Entscheidungslogik lesbar zu halten und die ursprünglichen CCandlePattern-Regeln widerzuspiegeln.
  • Bevor die Strategie in die entgegengesetzte Richtung eintritt, schließt sie alle bestehenden Risiken, um dem Verhalten des Expert Advisors zu entsprechen, der jeweils eine Nettoposition betreibt.

Unterschiede zum MQL5 Expert

  • Money-Management, Trailing Stop und feste Lot-Einstellungen aus dem MetaTrader-Framework werden nicht reproduziert; Das Auftragsvolumen von StockSharp wird durch die Strategieeigenschaft Volume gesteuert.
  • Der Stochastic-Oszillator verwendet die Indikatorimplementierung von StockSharp; Schwellenwerte bleiben konfigurierbar, sodass Sie das Verhalten optimieren können, wenn der ursprüngliche Broker-Feed leicht unterschiedliche Werte lieferte.
  • Die Protokollierung bietet detaillierte Erklärungen (auf Englisch) für jeden Ein- und Ausgang, um das Debuggen und Backtesting zu erleichtern.
namespace StockSharp.Samples.Strategies;

using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Morning/Evening Star pattern strategy with Stochastic confirmation.
/// Buys on morning star + oversold stochastic, sells on evening star + overbought stochastic.
/// </summary>
public class MorningEveningStochasticStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _stochPeriod;
	private readonly StrategyParam<decimal> _oversold;
	private readonly StrategyParam<decimal> _overbought;
	private readonly StrategyParam<int> _signalCooldownCandles;

	private readonly List<ICandleMessage> _candles = new();
	private decimal _prevK;
	private bool _hasPrevK;
	private int _candlesSinceTrade;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int StochPeriod { get => _stochPeriod.Value; set => _stochPeriod.Value = value; }
	public decimal Oversold { get => _oversold.Value; set => _oversold.Value = value; }
	public decimal Overbought { get => _overbought.Value; set => _overbought.Value = value; }
	public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }

	public MorningEveningStochasticStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_stochPeriod = Param(nameof(StochPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("Stoch Period", "Stochastic K period", "Indicators");
		_oversold = Param(nameof(Oversold), 30m)
			.SetDisplay("Oversold", "Stochastic oversold level", "Signals");
		_overbought = Param(nameof(Overbought), 70m)
			.SetDisplay("Overbought", "Stochastic overbought level", "Signals");
		_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 6)
			.SetGreaterThanZero()
			.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_candles.Clear();
		_prevK = 0m;
		_hasPrevK = false;
		_candlesSinceTrade = SignalCooldownCandles;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_candles.Clear();
		_hasPrevK = false;
		_candlesSinceTrade = SignalCooldownCandles;
		var stoch = new StochasticOscillator { K = { Length = StochPeriod }, D = { Length = 3 } };
		var subscription = SubscribeCandles(CandleType);
		subscription.BindEx(stoch, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue stochValue)
	{
		if (candle.State != CandleStates.Finished) return;

		if (_candlesSinceTrade < SignalCooldownCandles)
			_candlesSinceTrade++;

		var stochTyped = stochValue as StochasticOscillatorValue;
		if (stochTyped?.K is not decimal kValue) return;

		_candles.Add(candle);
		if (_candles.Count > 5)
			_candles.RemoveAt(0);

		if (_candles.Count >= 3)
		{
			var c3 = _candles[^1]; // current
			var c2 = _candles[^2]; // middle (star)
			var c1 = _candles[^3]; // first

			var body1 = Math.Abs(c1.ClosePrice - c1.OpenPrice);
			var body2 = Math.Abs(c2.ClosePrice - c2.OpenPrice);
			var body3 = Math.Abs(c3.ClosePrice - c3.OpenPrice);

			// Morning Star: bearish + small body + bullish, close above midpoint of first
			var isMorningStar = c1.OpenPrice > c1.ClosePrice  // first bearish
				&& body2 < body1 * 0.5m                       // small middle body
				&& c3.ClosePrice > c3.OpenPrice                // third bullish
				&& c3.ClosePrice > (c1.OpenPrice + c1.ClosePrice) / 2m;

			// Evening Star: bullish + small body + bearish, close below midpoint of first
			var isEveningStar = c1.ClosePrice > c1.OpenPrice   // first bullish
				&& body2 < body1 * 0.5m                        // small middle body
				&& c3.OpenPrice > c3.ClosePrice                // third bearish
				&& c3.ClosePrice < (c1.OpenPrice + c1.ClosePrice) / 2m;

			if (isMorningStar && kValue < Oversold && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				BuyMarket();
				_candlesSinceTrade = 0;
			}
			else if (isEveningStar && kValue > Overbought && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				SellMarket();
				_candlesSinceTrade = 0;
			}
		}

		// Exit on stochastic cross
		if (_hasPrevK)
		{
			if (Position > 0 && _prevK >= Overbought && kValue < Overbought && _candlesSinceTrade >= SignalCooldownCandles)
			{
				SellMarket();
				_candlesSinceTrade = 0;
			}
			else if (Position < 0 && _prevK <= Oversold && kValue > Oversold && _candlesSinceTrade >= SignalCooldownCandles)
			{
				BuyMarket();
				_candlesSinceTrade = 0;
			}
		}

		_prevK = kValue;
		_hasPrevK = true;
	}
}