Konvertierung des MetaTrader 4 Expert Advisors RUBBERBANDS_2.mq4.
Führt ein symmetrisches Raster um den aktuellen Preis aus, wobei die besten Geld-/Briefkurse anstelle von Kerzen verwendet werden.
Führt separate Hauptbücher für Long- und Short-Engagements, damit das Verhalten mit der abgesicherten MT4-Implementierung übereinstimmt.
Implementiert Gewinn- und Verlustkontrollen auf Sitzungsebene und einen manuellen Ruhe-/Stoppmodus, der mit den ursprünglichen Eingaben identisch ist.
Handelslogik
Die Strategie abonniert SubscribeLevel1() und reagiert auf jede Änderung des besten Gebots und besten Briefs.
Zwei gleitende Extreme (_upperExtreme / _lowerExtreme) erfassen den höchsten und niedrigsten Briefkurs, der seit dem letzten Zurücksetzen erreicht wurde. Sie werden aus Parametern initialisiert, wenn UseInitialValues wahr ist, andernfalls wird der erste empfangene Briefkurs verwendet.
Wenn es keine offenen Trades gibt und die Serverzeit den ersten Tick einer Minute erreicht (Sekunde gleich Null), fordert die Strategie sowohl einen Marktkauf als auch einen Marktverkauf an. Dies spiegelt das MT4-Verhalten wider, bei dem jede Minute Kauf-/Verkaufsflags gesetzt werden, während das Buch leer ist.
Jedes Mal, wenn der Briefkurs um GridStepPoints Punkte über den gespeicherten Höchstwert steigt, wird ein neuer Verkaufsauftrag erteilt. Jeder Rückgang um die gleiche Distanz unter das gespeicherte Tief löst eine neue Kauforder aus. Die Extremwerte werden nach jedem Auslöser auf den aktuellen Brief aktualisiert, sodass sich die Leiter mit dem Preis „dehnt“.
Die Gesamtzahl der gleichzeitig offenen Trades (Summe aus Long- und Short-Legs) ist durch MaxTrades begrenzt.
Der variable Gewinn wird aus dem aktuellen Geld-/Briefkurs berechnet: Long-Gewinne basieren auf dem Geldkurs minus dem durchschnittlichen Long-Preis, Short-Gewinne basieren auf dem durchschnittlichen Short-Preis minus dem Briefkurs. Der Helfer PriceToMoney konvertiert Preisunterschiede mithilfe von PriceStep/StepPrice in die Kontowährung, sofern verfügbar.
Wenn der variable Gewinn SessionTakeProfitPerLot * OrderVolume erreicht und UseSessionTakeProfit aktiviert ist, wird das gesamte Risiko reduziert. Ebenso löst ein schwebender Verlust unter -SessionStopLossPerLot * OrderVolume einen vollständigen Exit aus, wenn UseSessionStopLoss aktiviert ist.
Manuelle Flags reproduzieren die ursprünglichen EA-Optionen: CloseNow erzwingt einen flachen Start, QuiesceMode hält die Strategie inaktiv, während sie flach ist, und StopNow stoppt neue Eingaben, ohne bestehende Positionen zu beeinträchtigen.
Parameter
Parameter
Beschreibung
OrderVolume
Volumen für jede Marktorder (MT4 Lots).
MaxTrades
Maximale Anzahl gleichzeitig offener Trades (MT4 maxcount).
GridStepPoints
Abstand in Preispunkten zwischen Rasterebenen (MT4 pipstep).
QuiesceMode
Wenn aktiviert, wartet die Strategie einmal flach, identisch mit quiescenow.
TriggerImmediateEntries
Öffnet einen ersten Kauf und Verkauf, sobald die Strategie fertig ist (donow).
StopNow
Pausiert automatische Eingaben und behält gleichzeitig die aktuellen Positionen bei (stopnow).
CloseNow
Fordert eine sofortige Reduzierung beim Start an (closenow).
UseSessionTakeProfit & SessionTakeProfitPerLot
Variables Gewinnziel auf Sitzungsebene pro Los.
UseSessionStopLoss & SessionStopLossPerLot
Floating-Loss-Grenzwert auf Sitzungsebene pro Los.
UseInitialValues, InitialMax, InitialMin
Optionale Neustartunterstützung, die vorherige Extreme (useinvalues, inmax, inmin) wiederverwendet.
Implementierungshinweise
Der gesamte interne Status wird durch Tabulatoren eingerückt und in Feldern statt in Sammlungen gespeichert, um den Projektrichtlinien zu folgen.
Marktaufträge werden durch die Verfolgung von _activeBuyOrder und _activeSellOrder gedrosselt, sodass keine doppelten Anfragen gesendet werden, während die vorherige aussteht.
Die abgesicherte Bilanzierung wird in OnOwnTradeReceived durchgeführt, wobei Long- und Short-Durchschnittspreise/Volumen unabhängig voneinander aktualisiert und für die Stop-Logik in variable Gewinne umgewandelt werden.
TryCloseAll() spiegelt die MT4 close1by1()-Routine wider, indem es gegensätzliche Marktaufträge übermittelt, bis beide Ledger flach sind, und dann die Extreme auf den letzten Brief zurücksetzt.
Die Strategie basiert ausschließlich auf API-Aufrufen auf hoher Ebene (SubscribeLevel1(), BuyMarket, SellMarket) und vermeidet den direkten Indikatorzugriff, wie er in den Repository-Regeln erforderlich ist.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Rubber Bands Grid: Mean reversion grid using SMA+ATR bands.
/// </summary>
public class RubberBandsGridStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _smaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _entryPrice;
private int _gridCount;
public RubberBandsGridStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_smaLength = Param(nameof(SmaLength), 20)
.SetDisplay("SMA Length", "SMA 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 SmaLength
{
get => _smaLength.Value;
set => _smaLength.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0;
_gridCount = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
_gridCount = 0;
var sma = new SimpleMovingAverage { Length = SmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal smaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
if (atrVal <= 0)
return;
var close = candle.ClosePrice;
var lower = smaVal - atrVal * 2m;
var upper = smaVal + atrVal * 2m;
if (Position > 0)
{
if (close >= smaVal)
{
SellMarket();
_entryPrice = 0;
_gridCount = 0;
}
else if (close <= _entryPrice - atrVal * 4m)
{
SellMarket();
_entryPrice = 0;
_gridCount = 0;
}
else if (_gridCount < 3 && close <= _entryPrice - atrVal)
{
_entryPrice = (_entryPrice + close) / 2m;
_gridCount++;
BuyMarket();
}
}
else if (Position < 0)
{
if (close <= smaVal)
{
BuyMarket();
_entryPrice = 0;
_gridCount = 0;
}
else if (close >= _entryPrice + atrVal * 4m)
{
BuyMarket();
_entryPrice = 0;
_gridCount = 0;
}
else if (_gridCount < 3 && close >= _entryPrice + atrVal)
{
_entryPrice = (_entryPrice + close) / 2m;
_gridCount++;
SellMarket();
}
}
if (Position == 0)
{
if (close <= lower)
{
_entryPrice = close;
_gridCount = 0;
BuyMarket();
}
else if (close >= upper)
{
_entryPrice = close;
_gridCount = 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.Indicators import SimpleMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class rubber_bands_grid_strategy(Strategy):
def __init__(self):
super(rubber_bands_grid_strategy, self).__init__()
self._sma_length = self.Param("SmaLength", 20).SetDisplay("SMA Length", "SMA period", "Indicators")
self._atr_length = self.Param("AtrLength", 14).SetDisplay("ATR Length", "ATR period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))).SetDisplay("Candle Type", "Timeframe", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(rubber_bands_grid_strategy, self).OnReseted()
self._entry_price = 0
self._grid_count = 0
def OnStarted2(self, time):
super(rubber_bands_grid_strategy, self).OnStarted2(time)
self._entry_price = 0
self._grid_count = 0
sma = SimpleMovingAverage()
sma.Length = self._sma_length.Value
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(sma, atr, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def OnProcess(self, candle, sma_val, atr_val):
if candle.State != CandleStates.Finished:
return
if atr_val <= 0:
return
close = float(candle.ClosePrice)
lower = sma_val - atr_val * 2.0
upper = sma_val + atr_val * 2.0
if self.Position > 0:
if close >= sma_val:
self.SellMarket()
self._entry_price = 0
self._grid_count = 0
elif close <= self._entry_price - atr_val * 4.0:
self.SellMarket()
self._entry_price = 0
self._grid_count = 0
elif self._grid_count < 3 and close <= self._entry_price - atr_val:
self._entry_price = (self._entry_price + close) / 2.0
self._grid_count += 1
self.BuyMarket()
elif self.Position < 0:
if close <= sma_val:
self.BuyMarket()
self._entry_price = 0
self._grid_count = 0
elif close >= self._entry_price + atr_val * 4.0:
self.BuyMarket()
self._entry_price = 0
self._grid_count = 0
elif self._grid_count < 3 and close >= self._entry_price + atr_val:
self._entry_price = (self._entry_price + close) / 2.0
self._grid_count += 1
self.SellMarket()
if self.Position == 0:
if close <= lower:
self._entry_price = close
self._grid_count = 0
self.BuyMarket()
elif close >= upper:
self._entry_price = close
self._grid_count = 0
self.SellMarket()
def CreateClone(self):
return rubber_bands_grid_strategy()