Auf GitHub ansehen

LBS-Strategie

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

  1. Die Strategie überwacht abgeschlossene Kerzen des ausgewählten Zeitrahmens (CandleType).
  2. 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).
  3. Wenn eine Position eröffnet wird, wird die entgegengesetzte ausstehende Order sofort storniert, genau wie die DeleteAllPendingOrders-Routine des MQL-Experten.
  4. 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.
  5. 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

  1. Hängen Sie die Strategie an ein Instrument und wählen Sie denselben Zeitrahmen wie in MetaTrader.
  2. Konfigurieren Sie die Handelsstunden entsprechend der Session, die Sie handeln möchten (Setzen auf 0 deaktiviert diesen Slot).
  3. Wählen Sie den RiskPercent-Modus für automatisches Scaling; stellen Sie sicher, dass StopLossPips positiv ist.
  4. Für Fixed-Lot-Trading behalten Sie MoneyMode bei FixedLot und setzen Sie VolumeOrRisk auf die gewünschte Größe.
  5. 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;
	}
}