La Estrategia LBS es una conversión directa del asesor experto de MetaTrader 5 "LBS (barabashkakvn's edition)". El sistema original monitorea rupturas de la vela anterior durante una ventana de trading configurable y coloca órdenes stop en ambos extremos. El puerto de StockSharp mantiene las mismas reglas de gestión de operaciones mientras usa la API de alto nivel (SubscribeCandles, SubscribeLevel1, BuyStop/SellStop) para mayor claridad y fiabilidad.
Lógica de trading
La estrategia monitorea velas completadas del marco temporal seleccionado (CandleType).
Cuando el tiempo de cierre de la vela coincide con cualquiera de las horas de trading habilitadas (Hour1, Hour2, Hour3), el algoritmo calcula los niveles de ruptura:
El buy stop se coloca en el máximo más alto de la vela y el ask actual más un buffer de congelación.
El sell stop se coloca en el mínimo más bajo de la vela y el bid actual menos el mismo buffer.
El buffer reproduce el fallback SYMBOL_TRADE_FREEZE_LEVEL de MetaTrader (tres spreads, pero nunca menos de diez pips).
Si se abre una posición, la orden pendiente opuesta se cancela inmediatamente, igual que la rutina DeleteAllPendingOrders del experto MQL.
Los precios iniciales de stop-loss se adjuntan según StopLossPips. La lógica de trailing opcional (TrailingStopPips y TrailingStepPips) desplaza el stop una vez que el beneficio flotante supera los umbrales configurados.
Las órdenes solo se envían cuando la estrategia está en línea, no hay ninguna posición abierta y hay cotizaciones válidas de Level1 disponibles.
Gestión del dinero
MoneyMode espeja el interruptor Lot/Risk del experto original:
FixedLot – el parámetro VolumeOrRisk se interpreta como un volumen de trading absoluto.
RiskPercent – la estrategia convierte VolumeOrRisk en una fracción del valor de la cartera. El importe del riesgo se divide por la distancia entre el precio de entrada y el stop de protección (en pasos de precio) para obtener el volumen de la orden. Cuando se usa este modo, el stop-loss debe estar habilitado; de lo contrario, la orden se omite.
Todos los volúmenes se normalizan a los límites de mínimo, máximo y paso del instrumento para evitar rechazos del broker.
Parámetros
Nombre
Predeterminado
Descripción
StopLossPips
50
Distancia al stop fijo en pips. Cero deshabilita tanto el stop inicial como el módulo de trailing.
TrailingStopPips
5
Distancia del trailing-stop en pips. Cero deshabilita el trailing.
TrailingStepPips
15
Beneficio adicional (en pips) requerido antes de mover el trailing stop. Debe ser positivo cuando el trailing está habilitado.
MoneyMode
FixedLot
Selecciona entre volumen fijo y dimensionamiento por porcentaje de riesgo.
VolumeOrRisk
1.0
Tamaño del lote en modo FixedLot o porcentaje de riesgo en modo RiskPercent.
Hour1
10
Primera hora de trading. Establezca en 0 para deshabilitar.
Hour2
11
Segunda hora de trading. Establezca en 0 para deshabilitar.
Hour3
12
Tercera hora de trading. Establezca en 0 para deshabilitar.
CandleType
Marco temporal de 1 hora
Serie de velas usada para detectar rupturas; ajuste para reflejar el marco temporal del gráfico de MetaTrader.
Notas
Las comparaciones de horas usan el tiempo de cierre de la vela, que corresponde al momento en que TimeCurrent() de MetaTrader es igual al inicio de la siguiente barra.
La aproximación del nivel de congelación/stop garantiza que las órdenes stop nunca estén más cerca de diez pips del bid/ask actual, evitando los errores más comunes de MetaTrader.
Los trailing stops se actualizan en cada tick de Level1, asegurando un comportamiento cercano al manejador OnTick basado en ticks del experto original.
El dimensionamiento basado en riesgo usa Portfolio.CurrentValue cuando está disponible y recurre a Portfolio.BeginValue en caso contrario.
Consejos de uso
Adjunte la estrategia a un instrumento y elija el mismo marco temporal que se usó en MetaTrader.
Configure las horas de trading según la sesión que desea operar (establecerlas en 0 deshabilita ese slot).
Seleccione el modo RiskPercent si desea escalado automático; asegúrese de que StopLossPips sea positivo.
Para trading de lote fijo, mantenga MoneyMode en FixedLot y establezca VolumeOrRisk al tamaño deseado.
Inicie la estrategia. Colocará dos órdenes pendientes en la próxima hora configurada y mantendrá el stop de protección automáticamente.
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()