Lilith Goes To Hollywood-Strategie
Überblick
Diese Strategie stellt das Verhalten der MetaTrader-Expertin „Lilith geht nach Hollywood“ innerhalb der StockSharp hohen Ebene API nach. Es implementiert ein Absicherungsnetz, das in zwei sehr unterschiedlichen Modi arbeiten kann:
- Automatisierter Modus – Parabolic SAR löst sofortige Markteintritte aus, sobald der Preis den Stop-and-Reverse-Wert überschreitet.
- Manueller Modus – Ausstehende Stop-/Limit-Orders werden um benutzerdefinierte Referenzpreise herum geparkt und zur Ausführung überlassen.
In beiden Fällen verfolgt die Strategie das Long- und Short-Engagement getrennt, berechnet den schwebenden PnL des offenen Netzes und nutzt diese Informationen, um zu entscheiden, wann zusätzliche Wiederherstellungsanordnungen eingesetzt werden sollen.
Betriebsarten
- Automatisiert – Wenn keine Position offen ist, abonniert die Strategie den Indikator Parabolic SAR (0,02/0,2 Faktoren). Liegt der Schlusskurs der Kerze über dem Indikator, wird zum Marktwert gekauft, liegt er darunter, wird verkauft. Der ausgeführte Preis wird zum neuen Fokus und Erholungsstopps werden in einem konfigurierbaren Ankerabstand um ihn herum aktiviert.
- Manuell – Wenn keine Position offen ist, übermittelt die Strategie eine einzelne ausstehende Order pro Seite. Wenn der Markt unterhalb des Kaufniveaus handelt, wird ein Kaufstopp erstellt, andernfalls wird ein Kauflimit übermittelt. Die Verkaufsseite spiegelt die gleiche Logik rund um das
PriceDown-Niveau wider. Sobald eine der Orders ausgeführt wird, bleibt die andere Seite aktiv, bis sie manuell oder durch die Strategie storniert wird.
Logik der Auftragsverwaltung
- Das Raster führt weiterhin die Gesamtsumme der erfüllten Long-/Short-Volumina und ausstehenden Kauf-/Verkaufsaufträge aus. Dies ermöglicht es der Strategie, Ungleichgewichte zwischen beiden Seiten des Buches zu messen.
- Immer wenn der variable Gewinn das dynamische Ziel (
account value / 1000) erreicht, schließt die Strategie jede Position und storniert alle ausstehenden Aufträge.
- Wenn der variable PnL unter
-AccountValue * RiskPercent / 100 fällt, wird eine Notfallabsicherung eingesetzt, indem Marktaufträge eröffnet werden, die den Netto-Short- oder Long-Überschuss abdecken.
- Wiederherstellungsaufträge werden als Stop-Orders ausgedrückt, die um den Fokuspreis (automatischer Modus) oder um die konfigurierten manuellen Preise herum platziert werden. Ihre Größe wird als
(opposite exposure * XFactor) - current exposure berechnet und ahmt die MT4-Logik nach, die nächste Bestellung zu überdimensionieren, um das Raster wieder auszugleichen.
Parameter
| Name |
Beschreibung |
Automated |
Ermöglicht Parabolic SAR gesteuerte Markteintritte. Deaktivieren Sie die Funktion, um im manuellen Pending-Order-Modus zu arbeiten. |
PriceUp |
Referenzpreis, der zum Erstellen von Kauf-Stop/Limit-Orders im manuellen Modus verwendet wird. |
PriceDown |
Referenzpreis, der zum Erstellen von Verkaufs-Stopp-/Limit-Orders im manuellen Modus verwendet wird. |
AnchorSteps |
Abstand, ausgedrückt in Preisschritten, der zum Ausgleich von Wiederherstellungsaufträgen vom Fokuspreis verwendet wird. |
ManualVolume |
Basislosgröße bei manueller Bedienung oder wenn die dynamische Positionsgrößenbestimmung Null ergibt. |
XFactor |
Bei der Dimensionierung von Einziehungsanordnungen wird ein Multiplikator auf das gegnerische Risiko angewendet. |
RiskPercent |
Maximaler variabler Verlust (Prozentsatz des Kontowerts), der toleriert wird, bevor die Strategie eine Notfallabsicherung einsetzt. |
CandleType |
Zeitrahmen, der zur Steuerung der Parabolic SAR und der allgemeinen Verwaltungslogik verwendet wird. |
Risk controls
- Die Gewinnmitnahme ist dynamisch und skaliert mit dem Kontowert, sodass das Ziel automatisch erhöht werden kann, wenn das Konto wächst.
- Eine Notfallabsicherung kann extreme Verluste neutralisieren, indem die am stärksten gefährdete Seite des Netzes abgeflacht wird, sobald der schwebende Verlust den Schwellenwert
RiskPercent überschreitet.
- Alle ausstehenden Aufträge werden auf die Tick-Größe des Instruments gerundet und die Volumina werden angepasst, um die Wechselkurslimits einzuhalten, die den typischen Schutzmaßnahmen des ursprünglichen MetaTrader-Experten entsprechen.
Konvertierungshinweise
- MetaTrader Häkchen werden durch fertige Kerzen ersetzt. Der standardmäßige Zeitrahmen von einer Minute hält die Strategie reaktiv, kann jedoch über den Parameter
CandleType angepasst werden.
- Die Einstellung
Anchor aus der Quelle MQL drückte die Entfernung in Punkten aus. Hier ist es in mehreren Preisschritten konfiguriert, sodass es sich automatisch an die Tick-Größe des Instruments anpasst.
- Die ursprüngliche „Kommentar“-Ausgabe wurde in Strategieprotokollmeldungen (
LogInfo) umgewandelt, sodass das Plattformjournal dasselbe Feedback enthält, ohne auf Diagrammanmerkungen angewiesen zu sein.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Lilith Goes To Hollywood: SMA crossover with RSI filter and ATR stops.
/// </summary>
public class LilithGoesToHollywoodStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastSmaLength;
private readonly StrategyParam<int> _slowSmaLength;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
public LilithGoesToHollywoodStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_fastSmaLength = Param(nameof(FastSmaLength), 10)
.SetDisplay("Fast SMA", "Fast SMA period.", "Indicators");
_slowSmaLength = Param(nameof(SlowSmaLength), 25)
.SetDisplay("Slow SMA", "Slow SMA 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 FastSmaLength
{
get => _fastSmaLength.Value;
set => _fastSmaLength.Value = value;
}
public int SlowSmaLength
{
get => _slowSmaLength.Value;
set => _slowSmaLength.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 = FastSmaLength };
var slowEma = new ExponentialMovingAverage { Length = SlowSmaLength };
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)
{
SellMarket();
_entryPrice = 0;
}
else if (close <= _entryPrice - atrVal * 2m)
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (fastVal > slowVal && _prevFast <= _prevSlow)
{
BuyMarket();
_entryPrice = 0;
}
else if (close >= _entryPrice + atrVal * 2m)
{
BuyMarket();
_entryPrice = 0;
}
}
if (Position == 0)
{
if (fastVal > slowVal && _prevFast <= _prevSlow && rsiVal > 45)
{
_entryPrice = close;
BuyMarket();
}
else if (fastVal < slowVal && _prevFast >= _prevSlow && rsiVal < 55)
{
_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 lilith_goes_to_hollywood_strategy(Strategy):
def __init__(self):
super(lilith_goes_to_hollywood_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._fast_sma_length = self.Param("FastSmaLength", 10) \
.SetDisplay("Fast SMA", "Fast SMA period.", "Indicators")
self._slow_sma_length = self.Param("SlowSmaLength", 25) \
.SetDisplay("Slow SMA", "Slow SMA 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 FastSmaLength(self):
return self._fast_sma_length.Value
@property
def SlowSmaLength(self):
return self._slow_sma_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(lilith_goes_to_hollywood_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.FastSmaLength
self._slow_ema = ExponentialMovingAverage()
self._slow_ema.Length = self.SlowSmaLength
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:
self.SellMarket()
self._entry_price = 0.0
elif 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:
self.BuyMarket()
self._entry_price = 0.0
elif 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 > 45:
self._entry_price = close
self.BuyMarket()
elif fv < sv and self._prev_fast >= self._prev_slow and rv < 55:
self._entry_price = close
self.SellMarket()
self._prev_fast = fv
self._prev_slow = sv
def OnReseted(self):
super(lilith_goes_to_hollywood_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
def CreateClone(self):
return lilith_goes_to_hollywood_strategy()