Estrategia de ruptura que replica el experto MetaTrader "Previous Candle Breakdown 2". El algoritmo observa la vela completada más recientemente en un marco temporal configurable y activa operaciones cuando el precio supera su máximo o mínimo por un desplazamiento de pips definido por el usuario. Filtrado opcional por medias móviles, horas de negociación estrictas, dimensionamiento de posición por volumen fijo o riesgo porcentual, y salidas de protección en capas replican el comportamiento de la versión MQL original dentro de StockSharp.
Descripción general
Lógica de entrada: Entrar largo cuando el precio supera el máximo de la vela anterior más un desplazamiento. Entrar corto cuando el precio rompe por debajo del mínimo de la vela anterior menos el mismo desplazamiento.
Filtros: Medias móviles rápidas/lentas opcionales con parámetros de desplazamiento requieren confirmación direccional antes de operar. La negociación también está limitada a una ventana de tiempo de inicio/fin.
Dimensionamiento de posición: Elegir entre un volumen de orden fijo o dimensionamiento dinámico basado en el valor del portafolio y la distancia del stop-loss.
Controles de riesgo: Niveles de stop-loss y take-profit estáticos en pips, trailing stop con un filtro de paso, y un objetivo de beneficio global que cierra todas las posiciones.
Escala: El límite MaxPositions limita el tamaño absoluto de la posición neta para cada dirección.
ProfitClose = 100 (unidades de moneda de PnL realizado + no realizado)
MaxPositions = 10 (recuento absoluto de contratos/lotes por lado)
OrderVolume = 0 (deshabilitado), RiskPercent = 5 (usado cuando OrderVolume es cero y el stop-loss está activo)
StartTime = 09:09, EndTime = 19:19
CandleType = marco temporal de 4 horas
Reglas de negociación
Suscribirse a la serie de velas configurada y registrar cada vela finalizada.
Comprobar si la hora actual cae dentro de la sesión de negociación permitida. Si se alcanza ProfitClose, aplanar inmediatamente.
Calcular los niveles de ruptura añadiendo/restando el desplazamiento en pips del máximo y mínimo de la vela anterior.
Cuando el precio rompe esos niveles y se satisfacen las condiciones de MA (si están habilitadas), abrir operaciones respetando el límite MaxPositions.
Establecer distancias iniciales de stop-loss y take-profit desde el precio de entrada y activar trailing stops una vez que el precio se haya movido a favor de la operación al menos la distancia de trailing más el paso.
Monitorear continuamente las velas: activar salidas de stop-loss/take-profit cuando se toquen, arrastrar stops a medida que avanza el precio y restablecer niveles de protección una vez que las posiciones estén cerradas.
Notas
Los cálculos de pips se ajustan automáticamente para instrumentos de 3 o 5 decimales para imitar la conversión de punto a pip de MetaTrader.
Al usar dimensionamiento de riesgo porcentual, el algoritmo estima el volumen a partir del valor actual del portafolio y el stop-loss configurado.
La comprobación de ruptura usa velas finalizadas, por lo que los picos dentro de la barra se evalúan a los niveles de cierre/máximo/mínimo de la vela.
MaxPositions trabaja con la posición neta de la estrategia. Si se usan volúmenes fraccionarios, el parámetro representa el tamaño neto absoluto máximo permitido por dirección.
Los gráficos muestran velas, las medias móviles activas cuando están habilitadas, y las operaciones ejecutadas para confirmación visual.
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>
/// Previous candle breakdown strategy v2.
/// Trades breakout of previous candle high/low filtered by EMA crossover.
/// </summary>
public class PreviousCandleBreakdown2Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal? _prevHigh;
private decimal? _prevLow;
private decimal? _prevFast;
private decimal? _prevSlow;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public PreviousCandleBreakdown2Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 10).SetGreaterThanZero().SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 30).SetGreaterThanZero().SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevHigh = null;
_prevLow = null;
_prevFast = null;
_prevSlow = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevHigh = null; _prevLow = null; _prevFast = null; _prevSlow = null;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fast, slow, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fast); DrawIndicator(area, slow); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
_prevFast = fast;
_prevSlow = slow;
return;
}
if (_prevHigh == null || _prevLow == null || _prevFast == null || _prevSlow == null)
{
_prevHigh = candle.HighPrice; _prevLow = candle.LowPrice; _prevFast = fast; _prevSlow = slow;
return;
}
var close = candle.ClosePrice;
var bullTrend = fast > slow;
var bearTrend = fast < slow;
if (bullTrend && close > _prevHigh.Value && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (bearTrend && close < _prevLow.Value && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
_prevHigh = candle.HighPrice; _prevLow = candle.LowPrice; _prevFast = fast; _prevSlow = slow;
}
}
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 previous_candle_breakdown2_strategy(Strategy):
def __init__(self):
super(previous_candle_breakdown2_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_period = self.Param("FastPeriod", 10) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 30) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._prev_high = None
self._prev_low = None
self._prev_fast = None
self._prev_slow = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
def OnReseted(self):
super(previous_candle_breakdown2_strategy, self).OnReseted()
self._prev_high = None
self._prev_low = None
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(previous_candle_breakdown2_strategy, self).OnStarted2(time)
self._prev_high = None
self._prev_low = None
self._prev_fast = None
self._prev_slow = None
fast = ExponentialMovingAverage()
fast.Length = self.FastPeriod
slow = ExponentialMovingAverage()
slow.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast, slow, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._prev_fast = fv
self._prev_slow = sv
return
if self._prev_high is None or self._prev_low is None or self._prev_fast is None or self._prev_slow is None:
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._prev_fast = fv
self._prev_slow = sv
return
close = float(candle.ClosePrice)
bull_trend = fv > sv
bear_trend = fv < sv
if bull_trend and close > self._prev_high and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif bear_trend and close < self._prev_low and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._prev_fast = fv
self._prev_slow = sv
def CreateClone(self):
return previous_candle_breakdown2_strategy()