Die LBS-Strategie ist eine direkte Konvertierung des MetaTrader 5-Expertenberaters "LBS (barabashkakvn's edition)". Das Originalsystem beobachtet Ausbrüche der vorherigen Kerze während eines konfigurierbaren Handelsfensters und platziert Stop-Orders an beiden Extrema. Der StockSharp-Port behält dieselben Trade-Management-Regeln bei und verwendet die High-Level-API (SubscribeCandles, SubscribeLevel1, BuyStop/SellStop) für Klarheit und Zuverlässigkeit.
Handelslogik
Die Strategie überwacht abgeschlossene Kerzen des ausgewählten Zeitrahmens (CandleType).
Wenn die Schlusszeit der Kerze mit einer der aktivierten Handelsstunden (Hour1, Hour2, Hour3) übereinstimmt, berechnet der Algorithmus Ausbruchniveaus:
Der Buy-Stop wird beim Höchstwert aus Kerzenhoch und aktuellem Ask plus Freeze-Puffer platziert.
Der Sell-Stop wird beim Mindestwert aus Kerzentief und aktuellem Bid minus demselben Puffer platziert.
Der Puffer reproduziert den MetaTrader-SYMBOL_TRADE_FREEZE_LEVEL-Fallback (drei Spreads, aber nie weniger als zehn Pips).
Wenn eine Position eröffnet wird, wird die entgegengesetzte ausstehende Order sofort storniert, genau wie die DeleteAllPendingOrders-Routine des MQL-Experten.
Anfängliche Stop-Loss-Preise werden gemäß StopLossPips angehängt. Optionale Trailing-Logik (TrailingStopPips und TrailingStepPips) verschiebt den Stop, sobald der schwebende Gewinn die konfigurierten Schwellenwerte übersteigt.
Orders werden nur gesendet, wenn die Strategie online ist, keine Position offen ist und gültige Level1-Kurse verfügbar sind.
Geldmanagement
MoneyMode spiegelt den Lot/Risk-Schalter des ursprünglichen Experten:
FixedLot – der Parameter VolumeOrRisk wird als absolutes Handelsvolumen interpretiert.
RiskPercent – die Strategie konvertiert VolumeOrRisk in einen Anteil des Portfoliowerts. Der Risikobetrag wird durch den Abstand zwischen dem Einstiegspreis und dem Schutz-Stop (in Preisschritten) geteilt, um das Ordervolumen zu erhalten. Wenn dieser Modus verwendet wird, muss der Stop-Loss aktiviert sein; andernfalls wird die Order übersprungen.
Alle Volumina werden auf die Mindest-, Maximal- und Schrittbeschränkungen des Instruments normalisiert, um Broker-Ablehnungen zu vermeiden.
Parameter
Name
Standard
Beschreibung
StopLossPips
50
Abstand zum festen Stop in Pips. Null deaktiviert sowohl den anfänglichen Stop als auch das Trailing-Modul.
TrailingStopPips
5
Trailing-Stop-Abstand in Pips. Null deaktiviert Trailing.
TrailingStepPips
15
Zusätzlicher Gewinn (in Pips) erforderlich bevor der Trailing-Stop bewegt wird. Muss positiv sein wenn Trailing aktiviert ist.
MoneyMode
FixedLot
Wählt zwischen festem Volumen und risikoprozentualem Sizing.
VolumeOrRisk
1.0
Lotgröße im FixedLot-Modus oder Risikoprozentsatz im RiskPercent-Modus.
Hour1
10
Erste Handelsstunde. Auf 0 setzen zum Deaktivieren.
Hour2
11
Zweite Handelsstunde. Auf 0 setzen zum Deaktivieren.
Hour3
12
Dritte Handelsstunde. Auf 0 setzen zum Deaktivieren.
CandleType
1-Stunden-Zeitrahmen
Kerzenserie für die Ausbrucherkennung; anpassen um den Chartrahmen aus MetaTrader zu spiegeln.
Hinweise
Stundenvergleiche verwenden die Kerzenschlusszeit, die dem Moment entspricht, wenn MetaTrader's TimeCurrent() dem Beginn der nächsten Bar entspricht.
Die Freeze/Stop-Level-Approximation garantiert, dass Stop-Orders nie näher als zehn Pips am aktuellen Bid/Ask liegen, wodurch die häufigsten MetaTrader-Fehler vermieden werden.
Trailing-Stops werden bei jedem Level1-Tick aktualisiert, was ein Verhalten nahe dem tick-gesteuerten OnTick-Handler des ursprünglichen Experten sicherstellt.
Risikobasiertes Sizing verwendet Portfolio.CurrentValue wenn verfügbar und fällt sonst auf Portfolio.BeginValue zurück.
Verwendungshinweise
Hängen Sie die Strategie an ein Instrument und wählen Sie denselben Zeitrahmen wie in MetaTrader.
Konfigurieren Sie die Handelsstunden entsprechend der Session, die Sie handeln möchten (Setzen auf 0 deaktiviert diesen Slot).
Wählen Sie den RiskPercent-Modus für automatisches Scaling; stellen Sie sicher, dass StopLossPips positiv ist.
Für Fixed-Lot-Trading behalten Sie MoneyMode bei FixedLot und setzen Sie VolumeOrRisk auf die gewünschte Größe.
Starten Sie die Strategie. Sie platziert zwei ausstehende Orders bei der nächsten konfigurierten Stunde und pflegt den Schutz-Stop automatisch.
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;
/// <summary>
/// London Breakout Strategy using EMA crossover as breakout direction filter.
/// Buys when fast EMA crosses above slow EMA, sells on reverse.
/// </summary>
public class LbsStrategy : 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;
/// <summary>
/// Fast EMA period.
/// </summary>
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
/// <summary>
/// Slow EMA period.
/// </summary>
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
/// <summary>
/// Stop-loss distance in price steps.
/// </summary>
public int StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take-profit distance in price steps.
/// </summary>
public int TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="LbsStrategy"/> class.
/// </summary>
public LbsStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 100)
.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");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fast = null;
_slow = null;
_prevFast = 0;
_prevSlow = 0;
_entryPrice = 0;
_cooldown = 0;
}
/// <inheritdoc />
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;
// Check SL/TP
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
// EMA crossover
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_entryPrice = close;
_cooldown = 80;
}
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_entryPrice = close;
_cooldown = 80;
}
_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 lbs_strategy(Strategy):
def __init__(self):
super(lbs_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 20) \
.SetDisplay("Fast Period", "Fast EMA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 100) \
.SetDisplay("Slow Period", "Slow EMA 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(lbs_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(lbs_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 = 80
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 = 80
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 = 80
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 = 80
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 = 80
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 = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return lbs_strategy()