Die Envelope Limit Ladder Strategy ist eine C#-Portierung des MetaTrader Expert Advisors E_2_12_5min.mq4 (ID 7671). Es baut die ursprüngliche Leiter der Limit-Orders rund um einen EMA-Umschlag auf 5-Minuten-Kerzen neu auf und behält dabei das Multi-Target- und Trailing-Management-Modell des alten Roboters bei.
Konzept
Hüllkurvenfilter – eine gleitende durchschnittliche Hüllkurve (Standardwert EMA 144 mit einer Abweichung von 0,05 %), berechnet auf dem konfigurierbaren Zeitrahmen EnvelopeCandleType, liefert die Mittellinie und die oberen/unteren Bänder.
Signalkerze – Handelssignale werden im CandleType-Abonnement ausgewertet (Standard 5 Minuten). Wenn die vorherige Kerze zwischen der Mittellinie und dem nächsten Band schließt, begrenzt die Strategie die Aufträge auf der Mittellinie.
Auftragsleiter – bis zu drei Kauflimits und drei Verkaufslimits werden gleichzeitig platziert:
Einstiegspreis: ausgerichteter Mittellinienwert.
Stop-Loss: entgegengesetztes Hüllkurvenband.
Take-Profit: Band ± benutzerdefinierte Offsets (standardmäßig 8, 13 und 21 Punkte).
Handelsfenster – ausstehende Aufträge werden nur erstellt, wenn TradingStartHour < Hour < TradingEndHour. Alle verbleibenden Limits werden aufgehoben, sobald die Öffnungszeit TradingEndHour erreicht.
Positionsverwaltung – jede ausgeführte Limit-Order platziert sofort ihre eigene Stop- und Take-Profit-Order. Ein optionaler Trailing-Modus verschiebt den Stop auf den gleitenden Durchschnitt (oder hält ihn auf dem gegenüberliegenden Band), wenn der Preis über die Hülle hinaus ausbricht.
Parameter
Parameter
Standard
Beschreibung
CandleType
5 Minuten
Kerzentyp zur Signalerkennung.
EnvelopeCandleType
5 Minuten
Kerzentyp, der zur Berechnung der Hülle verwendet wird. Verwenden Sie einen längeren Zeitrahmen, um die MT4-Eingabe EnvTimeFrame nachzuahmen.
EnvelopePeriod
144
Gleitende durchschnittliche Länge des Umschlags.
MaMethod
EMA
Methode des gleitenden Durchschnitts (SMA, EMA, SMMA, LWMA).
EnvelopeDeviation
0,05
Umschlagbreite in Prozent (0,05 = 0,05 %).
TradingStartHour
0
Erste Stunde, in der ausstehende Aufträge angezeigt werden können (exklusive Prüfung, entspricht MT4-Verhalten).
TradingEndHour
17
Stunde, in der alle ausstehenden Aufträge entfernt werden (exklusive Obergrenze).
FirstTakeProfitPoints
8
Versatz in Punkten, der über die Hüllkurve hinaus für die erste Leitersprosse hinzugefügt wird.
SecondTakeProfitPoints
13
Versatz in Punkten für die zweite Sprosse.
ThirdTakeProfitPoints
21
Versatz in Punkten für die dritte Sprosse.
UseOppositeEnvelopeTrailing
true
Hält den Stop auf dem gegenüberliegenden Band (true) oder verfolgt ihn zum gleitenden Durchschnitt (false). Spiegelt das MT4-Flag MaElineTSL wider.
OrderVolume
0,1
Volumen pro ausstehender Bestellung (ersetzt die adaptive Losgröße von MT4).
Verhaltenshinweise
Die Strategie verwaltet für jede ausgeführte Limit-Order ein separates Stop/Take-Paar. Ausgänge behindern nicht die übrigen Sprossen der Leiter.
Das Trailing wird erst nach einem Ausbruch über die Hülle hinaus aktiviert und verschärft den Stop nur in die profitable Richtung.
Wenn EnvelopeCandleType von CandleType abweicht, werden die aktuellsten Umschlagwerte des sekundären Abonnements für Signalkerzen wiederverwendet, was weitgehend der MT4-Umschlagsuche für höhere Zeitrahmen entspricht.
Die ursprüngliche MT4-Geldverwaltungsroutine (LotsOptimized) wird durch den expliziten Parameter OrderVolume ersetzt, um den Port innerhalb von StockSharp deterministisch zu halten.
Nutzungstipps
Passen Sie den Hüllkurven-Zeitrahmen an die MT4-Eingaben an, um das ursprüngliche Verhalten zu reproduzieren (z. B. EnvelopeCandleType bei 5 Minuten belassen oder je nach Bedarf auf 1 Stunde/4 Stunden wechseln).
Setzen Sie UseOppositeEnvelopeTrailing auf false, wenn Sie möchten, dass der Trailing Stop zum gleitenden Durchschnitt und nicht zum entgegengesetzten Band springt, sobald der Preis den Umschlag verlässt.
Optimieren Sie die Take-Profit-Offsets und die Umschlagsabweichung gemeinsam. Die Leiterabstände hängen von der durch die Hülle erfassten Volatilität ab.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Envelope Limit Ladder strategy - Bollinger band mean reversion.
/// Buys when price touches lower band, sells when it touches upper band.
/// Exits at the middle band.
/// </summary>
public class EnvelopeLimitLadderStrategy : Strategy
{
private readonly StrategyParam<int> _bbPeriod;
private readonly StrategyParam<decimal> _bbWidth;
private readonly StrategyParam<DataType> _candleType;
public int BbPeriod { get => _bbPeriod.Value; set => _bbPeriod.Value = value; }
public decimal BbWidth { get => _bbWidth.Value; set => _bbWidth.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public EnvelopeLimitLadderStrategy()
{
_bbPeriod = Param(nameof(BbPeriod), 20)
.SetDisplay("BB Period", "Bollinger bands period", "Indicators");
_bbWidth = Param(nameof(BbWidth), 1.5m)
.SetDisplay("BB Width", "Bollinger bands deviation", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
protected override void OnReseted() { base.OnReseted(); }
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var bb = new BollingerBands { Length = BbPeriod, Width = BbWidth };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(bb, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue value)
{
if (candle.State != CandleStates.Finished)
return;
if (!value.IsFinal || value.IsEmpty)
return;
var bbVal = value as BollingerBandsValue;
if (bbVal == null)
return;
var upper = bbVal.UpBand;
var lower = bbVal.LowBand;
var middle = bbVal.MovingAverage;
if (upper == null || lower == null || middle == null)
return;
var close = candle.ClosePrice;
// Mean reversion: buy at lower band
if (close < lower.Value && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Mean reversion: sell at upper band
else if (close > upper.Value && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
// Exit long at middle
else if (Position > 0 && close > middle.Value)
{
SellMarket();
}
// Exit short at middle
else if (Position < 0 && close < middle.Value)
{
BuyMarket();
}
}
}
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 BollingerBands
from StockSharp.Algo.Strategies import Strategy
class envelope_limit_ladder_strategy(Strategy):
def __init__(self):
super(envelope_limit_ladder_strategy, self).__init__()
self._bb_period = self.Param("BbPeriod", 20) \
.SetDisplay("BB Period", "Bollinger bands period", "Indicators")
self._bb_width = self.Param("BbWidth", 1.5) \
.SetDisplay("BB Width", "Bollinger bands deviation", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
@property
def bb_period(self):
return self._bb_period.Value
@property
def bb_width(self):
return self._bb_width.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(envelope_limit_ladder_strategy, self).OnReseted()
def OnStarted2(self, time):
super(envelope_limit_ladder_strategy, self).OnStarted2(time)
bb = BollingerBands()
bb.Length = self.bb_period
bb.Width = self.bb_width
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(bb, self.process_candle).Start()
def process_candle(self, candle, bb_value):
if candle.State != CandleStates.Finished:
return
if not bb_value.IsFinal or bb_value.IsEmpty:
return
if bb_value.UpBand is None or bb_value.LowBand is None or bb_value.MovingAverage is None:
return
upper = float(bb_value.UpBand)
lower = float(bb_value.LowBand)
middle = float(bb_value.MovingAverage)
close = float(candle.ClosePrice)
if close < lower and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif close > upper and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
elif self.Position > 0 and close > middle:
self.SellMarket()
elif self.Position < 0 and close < middle:
self.BuyMarket()
def CreateClone(self):
return envelope_limit_ladder_strategy()