Auf GitHub ansehen

BeerGod EMA-Timing-Strategie

Diese Strategie repliziert den MetaTrader-Experten BeerGodEA innerhalb von StockSharp. Sie handelt Mean-Reversion-Setups auf einem einzelnen Symbol, indem sie einen exponentiellen gleitenden Durchschnitt (EMA) mit 60 Perioden überwacht und die aktuelle Preisbewegung mit dem vorherigen Balken vergleicht. Signale werden nur einmal pro Balken bei einem konfigurierbaren Minuten-Offset nach der Kerzeneröffnung ausgewertet, um den ursprünglichen EA nachzuahmen, der einige Minuten wartet, bevor er handelt.

Wenn der Preis vorübergehend vom EMA abweicht, während der Durchschnitt in die entgegengesetzte Richtung tendiert, eröffnet die Strategie eine Marktposition in der Erwartung, dass sich die Bewegung umkehrt. Bestehende Positionen in die entgegengesetzte Richtung werden sofort umgekehrt, indem die Ordergröße angepasst wird, sodass Shorts gedeckt werden, bevor eine neue Long-Position aufgebaut wird (und umgekehrt).

Funktionsweise

  1. Abonnieren Sie Zeitrahmen-Kerzen (Standard 5 Minuten) und erstellen Sie einen 60-Perioden-EMA über die Schlusskurse.
  2. Verfolgen Sie die aktuelle Kerze in Echtzeit. Beim ersten Tick jedes neuen Balkens speichern Sie den vorherigen EMA-Wert und den vorherigen Balken-Schluss, damit die Strategie sie später vergleichen kann.
  3. Sobald die konfigurierte Anzahl von Minuten ab der Eröffnung verstrichen ist (Standard 3 Minuten), bewerten Sie die folgenden Bedingungen unter Verwendung des aktuellen Preises und der EMA-Steigung:
    • Kauf-Setup: aktueller Preis < aktueller EMA, EMA liegt unter seinem vorherigen Wert (fallend), und aktueller Preis < vorheriger Balken-Schluss.
    • Verkauf-Setup: aktueller Preis > aktueller EMA, EMA liegt über seinem vorherigen Wert (steigend), und aktueller Preis > vorheriger Balken-Schluss.
  4. Wenn ein Kauf-Setup auftritt, während man noch nicht Long ist, senden Sie eine Market-Buy-Order, die so dimensioniert ist, dass offene Shorts geschlossen und das gewünschte Long-Volumen aufgebaut wird. Dieselbe Logik gilt symmetrisch für Verkauf-Setups.
  5. Nachdem ein Trade ausgelöst wurde, gilt das Signal für diese Kerze als verarbeitet, um doppelte Einstiege zu verhindern.

Parameter

  • Volume – Ordergröße in Lots (Standard 1). Die Strategie addiert automatisch den Absolutwert der aktuellen Position, wenn sie die Richtung wechseln muss, sodass die neue Order die alte Exposure schließt und den neuen Trade in einer einzigen Transaktion eröffnet.
  • EMA Length – Lookback-Periode für den exponentiellen gleitenden Durchschnitt (Standard 60).
  • Trigger Minutes – Anzahl der Minuten nach der Balkeneröffnung, wenn die Eintrittsbedingungen geprüft werden (Standard 3). Wenn das Fenster verpasst wird, wartet die Strategie auf die nächste Kerze.
  • Candle Type – Kerzendatentyp für Berechnungen (Standard 5-Minuten-Zeitrahmen).

Handelshinweise

  • Die Logik funktioniert auf jedem Symbol, solange Kerzendaten und Level-1-Preise verfügbar sind. Passen Sie die Kerzendauer an, wenn das Instrument in anderen Sessions als das ursprüngliche MetaTrader-Setup handelt.
  • Es wird jeweils nur eine Position (Long oder Short) gehalten. Das Wechseln der Richtung erfolgt durch die Dimensionierung der neuen Marktorder, um die ausstehende Position zu decken und den neuen Trade in einem Schritt zu eröffnen.
  • Im ursprünglichen EA sind keine expliziten Stop-Loss- oder Take-Profit-Niveaus definiert. Das Risikomanagement sollte extern hinzugefügt werden, falls erforderlich.
  • Der Startschutz ist aktiviert, damit StockSharp automatisch Notfall-Positionsausstiege behandelt, wenn manuelle Eingriffe oder Verbindungsprobleme auftreten.
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>
/// Mean-reversion strategy that triggers trades a few minutes after the bar opens using an EMA trend filter.
/// </summary>
public class BeerGodEmaTimingStrategy : Strategy
{
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<int> _triggerMinutes;
	private readonly StrategyParam<DataType> _candleType;

	private EMA _ema = null!;
	private DateTimeOffset _currentCandleOpenTime = DateTimeOffset.MinValue;
	private decimal _currentEma;
	private decimal _previousEma;
	private decimal _currentClose;
	private decimal _previousClose;


	/// <summary>
	/// EMA length used as the directional filter.
	/// </summary>
	public int EmaLength
	{
		get => _emaLength.Value;
		set => _emaLength.Value = value;
	}

	/// <summary>
	/// Minutes from the candle open when the entry check is performed.
	/// </summary>
	public int TriggerMinutesFromOpen
	{
		get => _triggerMinutes.Value;
		set => _triggerMinutes.Value = value;
	}

	/// <summary>
	/// Candle type used for signal generation.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of <see cref="BeerGodEmaTimingStrategy"/>.
	/// </summary>
	public BeerGodEmaTimingStrategy()
	{

		_emaLength = Param(nameof(EmaLength), 60)
			.SetGreaterThanZero()
			.SetDisplay("EMA Length", "EMA length for the trend filter", "Indicator")
			
			.SetOptimize(20, 120, 10);

		_triggerMinutes = Param(nameof(TriggerMinutesFromOpen), 3)
			.SetNotNegative()
			.SetDisplay("Trigger Minutes", "Minutes after open to check signals", "Timing")
			
			.SetOptimize(1, 10, 1);

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Primary candle type", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_currentCandleOpenTime = DateTimeOffset.MinValue;
		_currentEma = 0m;
		_previousEma = 0m;
		_currentClose = 0m;
		_previousClose = 0m;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_ema = new EMA
		{
			Length = EmaLength
		};

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_ema, ProcessCandle)
			.Start();

		// no fixed protection needed
	}

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

		_previousEma = _currentEma;
		_previousClose = _currentClose;

		_currentEma = emaValue;
		_currentClose = candle.ClosePrice;

		if (!_ema.IsFormed || _previousEma == 0m)
			return;

		var price = candle.ClosePrice;
		var maCurrent = _currentEma;
		var maPrevious = _previousEma;
		var prevClose = _previousClose;

		var newBuy = price < maCurrent && maCurrent < maPrevious && price < prevClose;
		var newSell = price > maCurrent && maCurrent > maPrevious && price > prevClose;

		if (!newBuy && !newSell)
			return;

		if (newBuy && Position <= 0)
		{
			BuyMarket();
		}
		else if (newSell && Position >= 0)
		{
			SellMarket();
		}
	}
}