La Estrategia de Three Breaky es una conversión completa del experto asesor MetaTrader 4 ThreeBreaky_v1.mq4. La versión de StockSharp conserva el trío original de subsistemas de ruptura, traduce su lógica basada en velas a la API de alto nivel y añade una gestión clara de posiciones para cada módulo. La estrategia trabaja en un único marco temporal configurable y puede habilitar o deshabilitar cualquier subsistema sin afectar a los otros.
Módulos de trading
Sistema 1 – Ruptura de expansión ATR
Usa solo la vela anterior.
Va largo cuando la vela anterior es alcista y su rango alto-bajo supera cuatro veces el rango verdadero promedio de 72 períodos.
Va corto cuando la vela anterior es bajista y se cumple la misma condición de rango.
Sistema 2 – Cambio de nube Ichimoku
Observa los límites de la nube (Senkou Span A y Senkou Span B) con períodos predeterminados 9/26/52.
Una señal larga se activa cuando hace dos velas el cierre fue por debajo de ambos spans y el último cerró por encima de ambos (un cambio alcista a través de la nube).
Una señal corta se activa cuando hace dos velas el cierre fue por encima de ambos spans y el último cerró por debajo de ambos.
Sistema 3 – Ruptura de cuerpo excepcional
Registra el tamaño del cuerpo de las últimas 20 velas completadas.
Una configuración larga requiere que la vela anterior sea alcista y su cuerpo sea más de tres veces el cuerpo máximo observado en esa historia de 20 velas.
Una configuración corta refleja la condición para cuerpos bajistas.
Cada subsistema opera una posición virtual dedicada. Las marcas de tiempo de las órdenes se almacenan para garantizar que un módulo pueda abrir como máximo una operación por vela, igual que la lógica original buyTag y sellTag.
Lógica de salida
Reversión de SAR Parabólico: Todas las posiciones abiertas comparten una salida de SAR Parabólico (0.005/0.2). Cuando el precio cruza el SAR entre las dos últimas velas, la posición afectada se cierra.
Gestión de riesgo: Las distancias opcionales de stop-loss y take-profit (en pips) se evalúan en cada vela completada. Si se superan los umbrales configurados, la posición relevante se cierra inmediatamente.
Indicadores utilizados
Rango verdadero promedio (período 72) para la línea base de volatilidad promedio.
Ichimoku Kinko Hyo (9, 26, 52) para el filtro de cambio de nube.
SAR Parabólico (aceleración 0.005, máximo 0.2) para salidas y lógica de trailing.
Buffer de tamaño de cuerpo rodante (20 velas) para reproducir la comparación de cuerpo máximo MQL.
Parámetros
Parámetro
Descripción
UseSystem1
Habilita el módulo de ruptura de expansión ATR.
UseSystem2
Habilita el módulo de cambio de nube Ichimoku.
UseSystem3
Habilita el módulo de ruptura de cuerpo grande.
OrderVolume
Volumen usado para cada orden de mercado generada por cualquier módulo.
StopLossPips
Distancia protectora del stop en pips. Establecer en cero para deshabilitar.
TakeProfitPips
Distancia del take-profit en pips. Establecer en cero para deshabilitar.
CandleType
Marco temporal para las velas de trabajo (predeterminado 1 hora).
Resumen del flujo de trabajo
Suscribirse a la serie de velas configurada y procesar solo las velas finalizadas.
Actualizar los indicadores ATR, Ichimoku y SAR Parabólico junto con el historial de cuerpos rodante.
Cerrar posiciones que alcancen stops, objetivos o reversiones de SAR Parabólico.
Si el trading está permitido, evaluar cada subsistema de forma independiente y emitir órdenes de mercado cuando se cumplan todas las condiciones respectivas.
Almacenar las últimas salidas de los indicadores para que la siguiente vela pueda acceder a los mismos valores históricos que en la implementación MQL original.
Notas
La estrategia asume un valor de pip basado en el paso de precio del instrumento; las cotizaciones FX de cinco y tres dígitos se normalizan a tamaños de pip de cuatro y dos decimales respectivamente.
Los subsistemas pueden ejecutarse simultáneamente. Cada uno mantiene su propio precio de entrada, dirección de posición y últimas marcas de tiempo de señal para reflejar la separación MagicNumber+N del EA fuente.
La implementación de StockSharp retiene el patrón de ejecución "una vez por barra" usando los tiempos de apertura de velas para bloquear órdenes duplicadas dentro de una sola barra.
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;
public class ThreeBreakyStrategy : 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;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public ThreeBreakyStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 15).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 60).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");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
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;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_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 three_breaky_strategy(Strategy):
def __init__(self):
super(three_breaky_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 15) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 60) \
.SetDisplay("Slow Period", "Slow MA 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(three_breaky_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(three_breaky_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 = 100
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 = 100
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 = 100
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 = 100
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 = 100
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 = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return three_breaky_strategy()