Die Grr Al Breakout-Strategie ist ein direkter Port des MetaTrader-Expertenberaters grr-al.mq5. Sie beobachtet den ersten Preis zu Beginn jeder Kerze und wartet darauf, dass sich der Markt eine konfigurierbare Distanz von diesem Ankerniveau entfernt. Wenn die Bewegung den Schwellenwert überschreitet, führt die Strategie genau einen Trade für diese Kerze aus und kehrt optional die bestehende Exposure um.
Die StockSharp-Implementierung behält das Verhalten des ursprünglichen timer-gesteuerten Roboters bei, übersetzt es aber in das High-Level-Kerzen-Abonnement-Modell. Jeder neue Kerzen-Snapshot liefert den anfänglichen Referenzpreis, während nachfolgende Aktualisierungen derselben Kerze den neuesten Schlusskurs als Live-Marktpreis bereitstellen. Dieser Ansatz recreiert die Tick-für-Tick-Ausbruchserkennung ohne auf Low-Level-Ereignisverarbeitung zurückgreifen zu müssen.
Handelslogik
Ankererkennung – wenn eine neue Kerze beginnt, speichert die Strategie ihren Eröffnungspreis (oder den ersten verfügbaren Schlusskurs wenn die Eröffnung noch nicht verfügbar ist) und setzt den Pro-Kerzen-Trigger zurück.
Ausbruchsprüfung – solange während der aktuellen Kerze kein Trade ausgeführt wurde, wird der neueste Schlusskurs mit dem Anker verglichen. Wenn der Preis um mehr als DeltaPoints steigt (in Preis umgerechnet durch die Instrumenten-Punktgröße), wird eine Short-Position eröffnet. Wenn der Preis um dieselbe Distanz fällt, wird eine Long-Position eröffnet.
Einzelausführung pro Kerze – sobald ein Ausbruchs-Trade ausgelöst wird, sind keine weiteren Orders erlaubt bis die nächste Kerze beginnt, was das br-Flag des ursprünglichen EA imitiert.
Risikomanagement – optionale Stop-Loss- und Take-Profit-Abstände werden unmittelbar nach dem Öffnen einer Position angewendet. Wenn die Order nur eine entgegengesetzte Exposure reduziert, werden die Schutz-Brackets übersprungen, um zu vermeiden, Stops an ein flaches Portfolio anzuhängen.
Positionsgrößenbestimmung – die Strategie kann mit einem fixen Volumen handeln oder die Ordergröße auf einen Bruchteil des vom Broker gemeldeten Maximalvolumens begrenzen.
Parameter
Volume – Basisvolumen (in Kontrakten) wenn RiskFraction null ist. Entspricht der BASELOT-Konstante der MQL-Version.
RiskFraction – Wert zwischen 0 und 1. Wenn größer als null, begrenzt die Strategie die Ordergröße durch Multiplikation des Broker-Maximalvolumens mit diesem Bruchteil und verwendet den kleineren Wert zwischen diesem Limit und Volume.
DeltaPoints – Anzahl der Instrumentenpunkte, die der Preis von der Kerzeneröffnung weg bewegen muss, um einen Trade auszulösen. Äquivalent zur DELTA-Konstante.
StopLossPoints – Schutz-Stop-Abstand in Punkten. Null deaktiviert den Stop, wie die SL-Konstante null in MQL.
TakeProfitPoints – Take-Profit-Abstand in Punkten. Null deaktiviert das Ziel und repliziert das TP-Konstantenverhalten.
CandleType – StockSharp-Kerzendeskriptor für den Zeitrahmen zum Ankern und Überwachen von Ausbrüchen. Standardmäßig Fünf-Minuten-Zeitrahmen, kann aber auf jeden unterstützten Zeitraum geändert werden.
Hinweise und Unterschiede zur MQL-Version
Der ursprüngliche EA verwendete Tick-Ereignisse mit einem Einsekundentimer. Dieser Port nutzt die StockSharp-Kerzen-Abonnement-API, die automatisch den neuesten Kerzenstatus liefert; kein manuelles Timer-Management erforderlich.
Bid/Ask-Differenzierung ist in der High-Level-Schnittstelle nicht verfügbar, daher verwendet die Strategie den Kerzen-Schlusskurs als Proxy für den Transaktionspreis. Stop-Loss- und Take-Profit-Offsets werden weiterhin in Punkten angewendet, was dem MetaTrader-Punktarithmtik-Verhalten entspricht.
Die risikobasierte Volumenberechnung in MetaTrader basierte auf Margenschätzung für eine feste Eins-Lot-Order. In diesem Port wird die Berechnung auf einen Maximalvolumen-Bruchteil vereinfacht, damit sie broker-agnostisch bleibt.
Da StockSharp-Strategien netto-positionsbasiert sind, kann das Senden einer Order in die entgegengesetzte Richtung die Exposure automatisch glätten oder umkehren, ähnlich dem OrderSend-Aufruf mit Netting-Modus in MetaTrader 5.
Verwendung
Die Strategie an ein Wertpapier und Portfolio in Designer, Runner oder einer benutzerdefinierten StockSharp-Host-Anwendung anhängen.
Gewünschten Kerzen-Zeitrahmen, Ausbruchsdistanz, Stop-Loss, Take-Profit und Volumen-Parameter konfigurieren.
Strategie starten. Sie abonniert automatisch die gewählten Kerzen, überwacht jede neue Kerze auf eine Ausbruchsbewegung und platziert Marktorders wenn die konfigurierten Bedingungen erfüllt sind.
Originalquelle
MetaTrader 5-Expertenberater: MQL/244/grr-al.mq5
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 GrrAlBreakoutStrategy : 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 GrrAlBreakoutStrategy()
{
_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;
}
}
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.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class grr_al_breakout_strategy(Strategy):
def __init__(self):
super(grr_al_breakout_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(grr_al_breakout_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(grr_al_breakout_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return grr_al_breakout_strategy()