La Estrategia de ruptura de horas sincronizadas es una versión StockSharp del MetaTrader 4 asesor experto JK_sinkhro1. Analiza el saldo de velas alcistas y bajistas durante la ventana de negociación reciente y solo opera durante dos horas de sincronización cuidadosamente seleccionadas (por defecto, 19:00 y 22:00 más un desplazamiento). La estrategia se centra en capturar rupturas direccionales y al mismo tiempo hacer cumplir reglas conservadoras de gestión de riesgos similares a las del EA original.
Lógica de trading
Funciona en la serie de velas seleccionada por el parámetro Candle Type (predeterminado: velas de 1 hora).
Mantiene una ventana deslizante de las últimas Analysis Period velas completadas y cuenta cuántas cerraron alcistas y bajistas.
Cuando el conteo alcista excede el conteo bajista, la estrategia se prepara para una ruptura larga durante la primera hora de sincronización (22 + Hour Offset).
Cuando el conteo bajista excede el conteo alcista, se prepara para una breve ruptura durante la segunda hora de sincronización (19 + Hour Offset).
Las señales solo son válidas dentro de los primeros cinco minutos de la hora para que la orden se sincronice con la nueva barra abierta, como en el MQL original.
Las nuevas operaciones se ignoran si ya hay Max Active Orders registrado o si hay una posición abierta.
Gestión de Riesgos y Gestión Comercial
Las posiciones se abren con un tamaño de lote fijo (Fixed Volume) o un tamaño basado en el riesgo utilizando el parámetro efectivo de la cuenta y Risk %. El modelo de riesgo divide el riesgo de efectivo permitido por la distancia de parada en pasos de precio para aproximarse al comportamiento de la fuente EA.
Cada posición utiliza tres capas de lógica de salida:
Una toma de ganancias primaria a Take Profit (pts) del precio de entrada.
Una toma de ganancias secundaria más rápida en Secondary TP (pts) para imitar el cierre manual inicial en el código original.
Un stop-loss estricto en Stop Loss (pts) por debajo/por encima del precio de entrada.
Trailing stop opcional: una vez que el precio avanza más de Trailing Stop (pts), el umbral de seguimiento sigue el extremo favorable y cierra la posición si el precio retrocede más allá de la distancia de seguimiento.
El estado de posición se restablece después de cada salida completa para prepararse para la siguiente ventana de sincronización.
Parámetros
Parámetro
Descripción
Take Profit (pts)
Distancia de obtención de beneficios primaria en los escalones del precio de los valores.
Secondary TP (pts)
Distancia de toma de ganancias más rápida activada antes del objetivo principal.
Stop Loss (pts)
Distancia de stop-loss medida en pasos de precio.
Trailing Stop (pts)
Distancia del trailing stop; configúrelo en 0 para desactivarlo.
Analysis Period
Número de velas recientes inspeccionadas al contar los cierres alcistas/bajistas.
Hour Offset
Compensación agregada al horario comercial original de 19:00 y 22:00.
Max Active Orders
Número máximo de órdenes activas simultáneamente permitidas antes de que se bloqueen nuevas entradas.
Fixed Volume
Volumen comercial utilizado cuando el tamaño basado en riesgo está deshabilitado.
Use Risk Volume
Permite dimensionar posiciones dinámicamente en función del efectivo de la cartera y la distancia de parada.
Risk %
Porcentaje de efectivo de la cartera arriesgado por operación en modo basado en riesgo.
Candle Type
Tipo de vela/período de tiempo utilizado para los cálculos y la generación de señales.
Notas de uso
La configuración predeterminada emula la versión MetaTrader que negoció EURUSD durante la sesión de Nueva York; ajuste el desplazamiento horario para que coincida con la zona horaria de su agente/servidor.
Asegúrese de que la definición de seguridad proporcione valores PriceStep, VolumeStep y MinVolume precisos para que el tamaño de la posición basado en el riesgo pueda alinear los volúmenes con los incrementos de los lotes de intercambio.
Debido a que la estrategia se basa en datos de cierre de velas, conéctela a un proveedor de historial o a una fuente de datos en vivo que pueda entregar la serie de velas seleccionada con un retraso mínimo.
La salida final utiliza precios de cierre de velas terminadas, que coinciden estrechamente con la lógica de seguimiento basada en ticks de la fuente EA sin dejar de ser compatible con el nivel alto de StockSharp API.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class SynchronizedHourBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<int> _cooldownCandles;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose;
private decimal _prevEma;
private bool _hasPrev;
private int _cooldownRemaining;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public SynchronizedHourBreakoutStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 50).SetDisplay("EMA Period", "EMA lookback", "Indicators");
_atrPeriod = Param(nameof(AtrPeriod), 14).SetDisplay("ATR Period", "ATR lookback", "Indicators");
_cooldownCandles = Param(nameof(CooldownCandles), 200).SetDisplay("Cooldown", "Candles between signals", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = default;
_prevEma = default;
_hasPrev = default;
_cooldownRemaining = default;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = 0;
_prevEma = 0;
_hasPrev = false;
_cooldownRemaining = 0;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, atr, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema, decimal atr)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
if (!_hasPrev) { _prevClose = close; _prevEma = ema; _hasPrev = true; return; }
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevClose = close;
_prevEma = ema;
return;
}
if (_prevClose <= _prevEma && close > ema && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownCandles;
}
else if (_prevClose >= _prevEma && close < ema && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_cooldownRemaining = CooldownCandles;
}
_prevClose = close;
_prevEma = ema;
}
}
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, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class synchronized_hour_breakout_strategy(Strategy):
def __init__(self):
super(synchronized_hour_breakout_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 50) \
.SetDisplay("EMA Period", "EMA lookback", "Indicators")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "ATR lookback", "Indicators")
self._cooldown_candles = self.Param("CooldownCandles", 200) \
.SetDisplay("Cooldown", "Candles between signals", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
self._cooldown_remaining = 0
@property
def ema_period(self):
return self._ema_period.Value
@property
def atr_period(self):
return self._atr_period.Value
@property
def cooldown_candles(self):
return self._cooldown_candles.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(synchronized_hour_breakout_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(synchronized_hour_breakout_strategy, self).OnStarted2(time)
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
self._cooldown_remaining = 0
ema = ExponentialMovingAverage()
ema.Length = self.ema_period
atr = AverageTrueRange()
atr.Length = self.atr_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, atr, self.process_candle).Start()
def process_candle(self, candle, ema, atr):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
ema_val = float(ema)
if not self._has_prev:
self._prev_close = close
self._prev_ema = ema_val
self._has_prev = True
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_close = close
self._prev_ema = ema_val
return
if self._prev_close <= self._prev_ema and close > ema_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self.cooldown_candles
elif self._prev_close >= self._prev_ema and close < ema_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self.cooldown_candles
self._prev_close = close
self._prev_ema = ema_val
def CreateClone(self):
return synchronized_hour_breakout_strategy()