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
Abonnieren Sie Zeitrahmen-Kerzen (Standard 5 Minuten) und erstellen Sie einen 60-Perioden-EMA über die Schlusskurse.
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.
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.
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.
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();
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
from StockSharp.Algo.Indicators import ExponentialMovingAverage
class beer_god_ema_timing_strategy(Strategy):
"""BeerGod EMA Timing: mean-reversion with EMA trend filter."""
def __init__(self):
super(beer_god_ema_timing_strategy, self).__init__()
self._ema_length = self.Param("EmaLength", 60) \
.SetGreaterThanZero() \
.SetDisplay("EMA Length", "EMA length for the trend filter", "Indicator")
self._trigger_minutes = self.Param("TriggerMinutesFromOpen", 3) \
.SetDisplay("Trigger Minutes", "Minutes after open to check signals", "Timing")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Primary candle type", "General")
self._current_ema = 0.0
self._previous_ema = 0.0
self._current_close = 0.0
self._previous_close = 0.0
@property
def EmaLength(self):
return int(self._ema_length.Value)
@property
def TriggerMinutesFromOpen(self):
return int(self._trigger_minutes.Value)
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(beer_god_ema_timing_strategy, self).OnStarted2(time)
self._current_ema = 0.0
self._previous_ema = 0.0
self._current_close = 0.0
self._previous_close = 0.0
self._ema = ExponentialMovingAverage()
self._ema.Length = self.EmaLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._ema, self.process_candle).Start()
def process_candle(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
self._previous_ema = self._current_ema
self._previous_close = self._current_close
self._current_ema = float(ema_value)
self._current_close = float(candle.ClosePrice)
if not self._ema.IsFormed or self._previous_ema == 0:
return
price = float(candle.ClosePrice)
ma_current = self._current_ema
ma_previous = self._previous_ema
prev_close = self._previous_close
new_buy = price < ma_current and ma_current < ma_previous and price < prev_close
new_sell = price > ma_current and ma_current > ma_previous and price > prev_close
if not new_buy and not new_sell:
return
if new_buy and self.Position <= 0:
self.BuyMarket()
elif new_sell and self.Position >= 0:
self.SellMarket()
def OnReseted(self):
super(beer_god_ema_timing_strategy, self).OnReseted()
self._current_ema = 0.0
self._previous_ema = 0.0
self._current_close = 0.0
self._previous_close = 0.0
def CreateClone(self):
return beer_god_ema_timing_strategy()