Parabolic SAR Bug-3-Strategie
Überblick
Die Parabolic SAR Bug 3 Strategy ist eine StockSharp High-Level-Portierung des MetaTrader 4 Expert Advisors pSAR_bug_3.mq4 in MQL/9786. Der Roboter reagiert auf den allerersten Parabolic SAR Punkt, der auf der gegenüberliegenden Seite des Preises erscheint. Wenn der SAR unter den Schlusskurs der Kerze fällt, eröffnet die Strategie eine Long-Position, nachdem alle Short-Positionen geschlossen wurden. Wenn der SAR über den Schlusskurs springt, kehrt er sich in eine Short-Position um. Jeder Trade wird durch feste Stop-Loss- und Take-Profit-Levels geschützt, die in Parabolic SAR Punkten gemessen und mit demselben Multiplikator wie im ursprünglichen MQL-Programm skaliert werden.
Handelslogik
- Marktdaten und Indikator – Die Strategie abonniert einen konfigurierbaren Kerzentyp (standardmäßig 15-Minuten-Zeitrahmen) und bindet einen Parabolic SAR-Indikator mit benutzerdefiniertem Beschleunigungsschritt und maximaler Beschleunigung.
- Statusverfolgung – nach der ersten abgeschlossenen Kerze speichert der Code, ob der Wert Parabolic SAR über oder unter dem Schlusskurs liegt. Die nächsten Kerzen vergleichen den neuen Zustand mit dem vorherigen, um den Wechsel des Indikators zu erkennen.
- Long-Einträge – wenn der Kurs Parabolic SAR von über dem Schlusskurs auf darunter wechselt, sendet die Strategie einen Marktauftrag in der Größe, alle aktiven Short-Positionen zu schließen und das konfigurierte Long-Volumen zu eröffnen. Schützende Stop-Loss- und Take-Profit-Preise werden unmittelbar nach der Eingabe berechnet.
- Short-Einträge – wenn der Kurs Parabolic SAR von unterhalb des Schlusskurses nach darüber kreuzt, spiegelt der Code das Verhalten für Short-Trades wider: Er flacht Long-Positionen ab und eröffnet eine Short-Order.
- Exits – bei jeder fertigen Kerze werden die Höchst- und Tiefstpreise mit den hinterlegten Schutzniveaus verglichen. Das Überschreiten des Stop-Loss oder des Take-Profit löst eine Marktorder aus, die die offene Position schließt, was dem MetaTrader-Ansatz von Schutzaufträgen auf Brokerseite entspricht.
Risikomanagement
- Die Stop-Loss- und Take-Profit-Abstände werden durch Multiplikation von
StopLossPoints oder TakeProfitPoints mit dem StopMultiplier und dem Instrument PriceStep (oder 0.0001, wenn das Symbol keine Stufe vorsieht) umgerechnet.
- Marktaufträge werden nur gesendet, wenn
IsFormedAndOnlineAndAllowTrading() bestätigt, dass das Abonnement aktiv ist und der Handel zulässig ist.
- Bei jeder Änderung der Positionsrichtung werden die ungenutzten Schutzebenen für die alte Seite gelöscht, um veraltete Ausgänge zu verhindern.
Parameter
| Name |
Standard |
Beschreibung |
TradeVolume |
0.1 |
Bestellvolumen in Losen. Durch die Aktualisierung des Werts wird auch die Basiseigenschaft Strategy.Volume geändert. |
StopLossPoints |
90 |
Stop-Loss-Distanz ausgedrückt in Parabolic SAR Punkten, später skaliert durch StopMultiplier und den Preisschritt des Instruments. |
TakeProfitPoints |
20 |
Take-Profit-Distanz ausgedrückt in Parabolic SAR Punkten, später skaliert durch StopMultiplier und den Preisschritt. |
StopMultiplier |
10 |
Multiplikator, der die Eingabe MetaTrader StopMult reproduziert und so die Kompatibilität mit Bruchteil-Pip-Brokern ermöglicht. |
SarStep |
0.02 |
Anfänglicher Beschleunigungsfaktor für den Indikator Parabolic SAR. |
SarMaximum |
0.2 |
Maximaler Beschleunigungsfaktor für den Indikator Parabolic SAR. |
CandleType |
15m time-frame |
Kerzentyp, der für Indikatorberechnungen und Signalerkennung verwendet wird. |
Konvertierungshinweise
- MetaTrader schlossen Positionen, bevor sie den entgegengesetzten Handel mit separaten Aufträgen eröffneten. Die StockSharp-Version erzielt das gleiche Ergebnis, indem sie eine einzelne Marktorder sendet, deren Größe so dimensioniert ist, dass sie etwaige gegenteilige Risiken ausgleicht und das neue Positionsvolumen festlegt.
- Stop-Loss- und Take-Profit-Orders auf Brokerseite werden durch die Überwachung von Candle-Extremen und die Übermittlung von Marktausstiegen nachgeahmt, sobald die Schwellenwerte überschritten werden.
- Der zusätzliche Parameter
StopMultiplier akzeptiert jeden positiven Wert, ist jedoch standardmäßig 10, der einzige Multiplikator, der in den ursprünglichen Codekommentaren dokumentiert ist.
- Für diese Konvertierung wird keine Python-Version bereitgestellt, genau wie in der Aufgabenbeschreibung gefordert.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Parabolic SAR Bug 3: EMA crossover with RSI filter and ATR stops.
/// </summary>
public class ParabolicSarBug3Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastEmaLength;
private readonly StrategyParam<int> _slowEmaLength;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
public ParabolicSarBug3Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(2).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_fastEmaLength = Param(nameof(FastEmaLength), 9)
.SetDisplay("Fast EMA Length", "Fast EMA period.", "Indicators");
_slowEmaLength = Param(nameof(SlowEmaLength), 26)
.SetDisplay("Slow EMA Length", "Slow EMA period.", "Indicators");
_rsiLength = Param(nameof(RsiLength), 14)
.SetDisplay("RSI Length", "RSI period.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int FastEmaLength { get => _fastEmaLength.Value; set => _fastEmaLength.Value = value; }
public int SlowEmaLength { get => _slowEmaLength.Value; set => _slowEmaLength.Value = value; }
public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0; _prevSlow = 0; _entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = 0; _prevSlow = 0; _entryPrice = 0;
var fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
var slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fastEma, slowEma, rsi, atr, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fastEma); DrawIndicator(area, slowEma); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal rsiVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished) return;
if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0) { _prevFast = fastVal; _prevSlow = slowVal; return; }
var close = candle.ClosePrice;
if (Position > 0)
{
if ((fastVal < slowVal && _prevFast >= _prevSlow) || close <= _entryPrice - atrVal * 2m) { SellMarket(); _entryPrice = 0; }
}
else if (Position < 0)
{
if ((fastVal > slowVal && _prevFast <= _prevSlow) || close >= _entryPrice + atrVal * 2m) { BuyMarket(); _entryPrice = 0; }
}
if (Position == 0)
{
if (fastVal > slowVal && _prevFast <= _prevSlow && rsiVal > 50) { _entryPrice = close; BuyMarket(); }
else if (fastVal < slowVal && _prevFast >= _prevSlow && rsiVal < 50) { _entryPrice = close; SellMarket(); }
}
_prevFast = fastVal; _prevSlow = slowVal;
}
}
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, RelativeStrengthIndex, AverageTrueRange
class parabolic_sar_bug3_strategy(Strategy):
def __init__(self):
super(parabolic_sar_bug3_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(2))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._fast_ema_length = self.Param("FastEmaLength", 9) \
.SetDisplay("Fast EMA Length", "Fast EMA period.", "Indicators")
self._slow_ema_length = self.Param("SlowEmaLength", 26) \
.SetDisplay("Slow EMA Length", "Slow EMA period.", "Indicators")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastEmaLength(self):
return self._fast_ema_length.Value
@property
def SlowEmaLength(self):
return self._slow_ema_length.Value
@property
def RsiLength(self):
return self._rsi_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(parabolic_sar_bug3_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._fast_ema = ExponentialMovingAverage()
self._fast_ema.Length = self.FastEmaLength
self._slow_ema = ExponentialMovingAverage()
self._slow_ema.Length = self.SlowEmaLength
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.RsiLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._fast_ema, self._slow_ema, self._rsi, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, fast_val, slow_val, rsi_val, atr_val):
if candle.State != CandleStates.Finished:
return
fv = float(fast_val)
sv = float(slow_val)
rv = float(rsi_val)
av = float(atr_val)
if self._prev_fast == 0 or self._prev_slow == 0 or av <= 0:
self._prev_fast = fv
self._prev_slow = sv
return
close = float(candle.ClosePrice)
if self.Position > 0:
if (fv < sv and self._prev_fast >= self._prev_slow) or close <= self._entry_price - av * 2.0:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if (fv > sv and self._prev_fast <= self._prev_slow) or close >= self._entry_price + av * 2.0:
self.BuyMarket()
self._entry_price = 0.0
if self.Position == 0:
if fv > sv and self._prev_fast <= self._prev_slow and rv > 50:
self._entry_price = close
self.BuyMarket()
elif fv < sv and self._prev_fast >= self._prev_slow and rv < 50:
self._entry_price = close
self.SellMarket()
self._prev_fast = fv
self._prev_slow = sv
def OnReseted(self):
super(parabolic_sar_bug3_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
def CreateClone(self):
return parabolic_sar_bug3_strategy()