Esta estrategia replica el asesor experto clásico "BreakOut" de MetaTrader de Soubra2003. Monitorea el máximo y el mínimo de la
vela completada más reciente y reacciona cuando el cierre actual rompe esos niveles de referencia. El enfoque es totalmente
simétrico: las posiciones largas se abren en rupturas alcistas, y las posiciones cortas se abren en roturas bajistas. Buffers
opcionales de stop-loss y take-profit expresados en unidades de precio permiten al usuario limitar el riesgo o bloquear ganancias.
Descripción General
Se suscribe a una única serie de velas (marco temporal de 1 hora por defecto).
Almacena el máximo y el mínimo de la vela anterior para actuar como disparadores de ruptura.
Opera solo al cierre de la vela para reflejar la lógica basada en ticks del original sin depender de datos dentro de la barra.
Soporta tanto operaciones largas como cortas y siempre permanece plano cuando no hay condición de ruptura activa.
Reglas de Trading
Entrada por ruptura / reversión
Cuando el cierre de la vela actual terminada es estrictamente superior al máximo de la vela anterior:
Cualquier posición corta abierta se cierra a mercado.
Se abre inmediatamente una nueva posición larga (la reversión ocurre dentro del mismo paso de procesamiento de la vela).
Cuando el cierre es estrictamente inferior al mínimo de la vela anterior:
Cualquier posición larga abierta se cierra a mercado.
Se abre posteriormente una nueva posición corta.
Salidas de protección (opcional)
Si se configura un offset de stop-loss (> 0), la estrategia sale de un largo cuando el cierre cae offset unidades por
debajo del precio de entrada, o sale de un corto cuando el cierre sube offset unidades por encima del precio de entrada.
Si se configura un offset de take-profit (> 0), la estrategia sale de un largo cuando el cierre sube offset unidades por
encima del precio de entrada, o sale de un corto cuando el cierre cae offset unidades por debajo del precio de entrada.
Reinicio del estado
Después de que cada vela es procesada, el máximo y mínimo más recientes se convierten en los nuevos niveles de referencia de ruptura.
Parámetros
Candle Type – tipo de datos usado para la suscripción (por defecto marco temporal horario). Establézcalo al tamaño de barra
que coincide con el gráfico usado en MetaTrader para el asesor experto original.
Stop Loss – distancia en unidades de precio absolutas entre el precio de entrada y el stop de protección. Mantener en 0
para deshabilitar el manejo del stop-loss.
Take Profit – distancia en unidades de precio absolutas entre el precio de entrada y el objetivo de beneficio. Mantener en
0 para deshabilitar el manejo del take-profit.
Notas
Los cálculos de stop-loss y take-profit se realizan en precios de cierre de velas. La versión MQL4 original adjuntaba niveles
de SL/TP estáticos a las órdenes; en StockSharp las salidas se simulan enviando órdenes a mercado una vez que se cumplen los
umbrales.
Use incrementos de precio específicos del instrumento al configurar los offsets. Por ejemplo, si el instrumento opera con un
tamaño de tick de 0.01 y desea un stop de 20 ticks, configure el parámetro de stop-loss en 0.20.
Debido a que la lógica siempre referencia la vela inmediatamente anterior, la estrategia funciona mejor en instrumentos en
tendencia o durante sesiones de alta volatilidad donde las rupturas son significativas.
Origen
Fuente: MQL/17306/BreakOut.mq4 (asesor experto BreakOut de Soubra2003)
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Breakout strategy that trades when the close crosses the previous candle's high or low.
/// Ported from the BreakOut.mq4 expert by Soubra2003.
/// </summary>
public class PreviousCandleBreakoutStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _stopLossOffset;
private readonly StrategyParam<decimal> _takeProfitOffset;
private decimal? _previousHigh;
private decimal? _previousLow;
private decimal _entryPrice;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public decimal StopLossOffset { get => _stopLossOffset.Value; set => _stopLossOffset.Value = value; }
public decimal TakeProfitOffset { get => _takeProfitOffset.Value; set => _takeProfitOffset.Value = value; }
public PreviousCandleBreakoutStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromDays(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for candle subscription", "General");
_stopLossOffset = Param(nameof(StopLossOffset), 1000m)
.SetDisplay("Stop Loss", "Price distance for the stop-loss. Set 0 to disable.", "Risk")
;
_takeProfitOffset = Param(nameof(TakeProfitOffset), 1500m)
.SetDisplay("Take Profit", "Price distance for the take-profit. Set 0 to disable.", "Risk")
;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_previousHigh = null;
_previousLow = null;
_entryPrice = 0m;
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, CandleType);
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (_previousHigh is null || _previousLow is null)
{
// Store the first finished candle to obtain reference high/low levels.
_previousHigh = candle.HighPrice;
_previousLow = candle.LowPrice;
return;
}
var previousHigh = _previousHigh.Value;
var previousLow = _previousLow.Value;
var close = candle.ClosePrice;
var breakoutAbove = close > previousHigh;
var breakoutBelow = close < previousLow;
// Manage protective exits while a position is open.
if (Position > 0)
{
if (StopLossOffset > 0m && close <= _entryPrice - StopLossOffset)
{
if (Position > 0) SellMarket(); else if (Position < 0) BuyMarket();
_entryPrice = 0m;
}
else if (TakeProfitOffset > 0m && close >= _entryPrice + TakeProfitOffset)
{
if (Position > 0) SellMarket(); else if (Position < 0) BuyMarket();
_entryPrice = 0m;
}
}
else if (Position < 0)
{
if (StopLossOffset > 0m && close >= _entryPrice + StopLossOffset)
{
if (Position > 0) SellMarket(); else if (Position < 0) BuyMarket();
_entryPrice = 0m;
}
else if (TakeProfitOffset > 0m && close <= _entryPrice - TakeProfitOffset)
{
if (Position > 0) SellMarket(); else if (Position < 0) BuyMarket();
_entryPrice = 0m;
}
}
// Breakout above the previous high opens or reverses into a long position.
if (breakoutAbove)
{
if (Position < 0)
{
if (Position > 0) SellMarket(); else if (Position < 0) BuyMarket();
_entryPrice = 0m;
}
if (Position <= 0)
{
BuyMarket();
_entryPrice = close;
}
}
else if (breakoutBelow)
{
// Breakout below the previous low opens or reverses into a short position.
if (Position > 0)
{
if (Position > 0) SellMarket(); else if (Position < 0) BuyMarket();
_entryPrice = 0m;
}
if (Position >= 0)
{
SellMarket();
_entryPrice = close;
}
}
_previousHigh = candle.HighPrice;
_previousLow = candle.LowPrice;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
class previous_candle_breakout_strategy(Strategy):
def __init__(self):
super(previous_candle_breakout_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromDays(1)))
self._stop_loss_offset = self.Param("StopLossOffset", 1000.0)
self._take_profit_offset = self.Param("TakeProfitOffset", 1500.0)
self._previous_high = None
self._previous_low = None
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def StopLossOffset(self):
return self._stop_loss_offset.Value
@StopLossOffset.setter
def StopLossOffset(self, value):
self._stop_loss_offset.Value = value
@property
def TakeProfitOffset(self):
return self._take_profit_offset.Value
@TakeProfitOffset.setter
def TakeProfitOffset(self, value):
self._take_profit_offset.Value = value
def OnStarted2(self, time):
super(previous_candle_breakout_strategy, self).OnStarted2(time)
self._previous_high = None
self._previous_low = None
self._entry_price = 0.0
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
if self._previous_high is None or self._previous_low is None:
self._previous_high = high
self._previous_low = low
return
previous_high = self._previous_high
previous_low = self._previous_low
breakout_above = close > previous_high
breakout_below = close < previous_low
sl = float(self.StopLossOffset)
tp = float(self.TakeProfitOffset)
if self.Position > 0:
if sl > 0.0 and close <= self._entry_price - sl:
self.SellMarket()
self._entry_price = 0.0
elif tp > 0.0 and close >= self._entry_price + tp:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if sl > 0.0 and close >= self._entry_price + sl:
self.BuyMarket()
self._entry_price = 0.0
elif tp > 0.0 and close <= self._entry_price - tp:
self.BuyMarket()
self._entry_price = 0.0
if breakout_above:
if self.Position < 0:
self.BuyMarket()
self._entry_price = 0.0
if self.Position <= 0:
self.BuyMarket()
self._entry_price = close
elif breakout_below:
if self.Position > 0:
self.SellMarket()
self._entry_price = 0.0
if self.Position >= 0:
self.SellMarket()
self._entry_price = close
self._previous_high = high
self._previous_low = low
def OnReseted(self):
super(previous_candle_breakout_strategy, self).OnReseted()
self._previous_high = None
self._previous_low = None
self._entry_price = 0.0
def CreateClone(self):
return previous_candle_breakout_strategy()