La estrategia reproduce el comportamiento del experto MetaTrader "OpenTime". Coloca órdenes de mercado a una hora del día configurable, opcionalmente cierra todas las exposiciones durante una ventana de salida dedicada y aplica reglas simples de administración de dinero, como stop-loss fijo, toma de ganancias y protección de seguimiento. El puerto utiliza StockSharp Strategy API de alto nivel, por lo que la estrategia se puede combinar con otros componentes dentro del marco.
como funciona
Cada vela terminada del período de tiempo seleccionado activa una verificación de la hora del día.
Cuando la hora actual cae dentro de la ventana de negociación, la estrategia envía órdenes de mercado para cada dirección habilitada:
Si solo un lado está habilitado, la posición neta actual se extiende o invierte hasta alcanzar el volumen solicitado.
Cuando ambos lados están habilitados, las órdenes de compra y venta se emiten en la misma ventana. Debido a que StockSharp cuenta la exposición compensada en paralelo, abrir la segunda dirección compensa automáticamente la exposición opuesta antes de establecer la nueva.
Mientras la ventana de cierre está activa, la estrategia llama a ClosePosition() una vez para reducir cualquier exposición pendiente.
Las distancias opcionales de stop-loss, take-profit y trailing stop se delegan a StartProtection, que gestiona las órdenes de protección utilizando salidas de mercado.
Parámetros
Habilitar cerrar ventana: refleja la bandera TimeClose. Cuando están habilitados, Close Position Time y Window Length definen cuándo se cierran las operaciones existentes.
Hora de posición de cierre: hora diaria en la que comienza la ventana de salida (predeterminado 20:50).
Hora de negociación: hora diaria en la que se permiten nuevas transacciones (predeterminado 18:50).
Duración de la ventana: duración de las ventanas de negociación y de cierre (5 minutos predeterminados, correspondientes a la entrada original Duration).
Permitir entradas de venta: corresponde al interruptor MQL Sell; permite entradas breves (el valor predeterminado es verdadero).
Permitir entradas de compra: corresponde al interruptor MQL Buy; permite entradas largas (falso predeterminado).
Volumen de pedido: volumen neto objetivo para cada nueva operación (por defecto, 0,1 lotes). La estrategia agrega el valor absoluto de la posición actual cuando aparece una señal opuesta, por lo que las reversiones ocurren en una única orden de mercado.
Puntos Stop-Loss – distancia en puntos para la parada de protección (el valor predeterminado 0 desactiva la parada).
Puntos Take-Profit: distancia en puntos para el objetivo de ganancias (el valor predeterminado 0 desactiva el objetivo).
Usar Trailing Stop: habilita la lógica de trailing stop del asistente original SimpleTrailing.
Puntos de parada de seguimiento: distancia de seguimiento expresada en puntos (predeterminado 300).
Puntos de paso final: se requiere progreso adicional antes de avanzar hasta el punto final (predeterminado 3).
Tipo de vela: período de tiempo utilizado para las comprobaciones de tiempo (velas predeterminadas de 1 minuto).
Notas
El tamaño en puntos se deriva del paso del precio del valor. Para cotizaciones de tres y cinco decimales, el paso se multiplica por 10, reproduciendo el manejo de pips utilizado por el script MQL.
StartProtection coloca topes de protección solo cuando al menos una de las distancias es mayor que cero. Si el seguimiento está activo sin un stop-loss fijo, la distancia de seguimiento se proporciona como valor de protección inicial.
La estrategia no gestiona intencionalmente órdenes pendientes ni reintentos repetidos, porque StockSharp ya proporciona manejo automático de errores para órdenes de mercado.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Open Time Daily Window: trades during a specific time window using
/// EMA direction for entry. Closes position at the end of window.
/// </summary>
public class OpenTimeDailyWindowStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _tradeHour;
private readonly StrategyParam<int> _windowMinutes;
private readonly StrategyParam<int> _closeHour;
private decimal _prevEma;
private decimal _entryPrice;
public OpenTimeDailyWindowStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_emaLength = Param(nameof(EmaLength), 20)
.SetDisplay("EMA Length", "EMA period for direction.", "Indicators");
_tradeHour = Param(nameof(TradeHour), 10)
.SetDisplay("Trade Hour", "Hour when trading window opens (UTC).", "Schedule");
_windowMinutes = Param(nameof(WindowMinutes), 120)
.SetDisplay("Window Minutes", "Duration of trading window.", "Schedule");
_closeHour = Param(nameof(CloseHour), 20)
.SetDisplay("Close Hour", "Hour to close positions (UTC).", "Schedule");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public int TradeHour
{
get => _tradeHour.Value;
set => _tradeHour.Value = value;
}
public int WindowMinutes
{
get => _windowMinutes.Value;
set => _windowMinutes.Value = value;
}
public int CloseHour
{
get => _closeHour.Value;
set => _closeHour.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevEma = 0;
_entryPrice = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ema = new ExponentialMovingAverage { Length = EmaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal emaVal)
{
if (candle.State != CandleStates.Finished)
return;
var hour = candle.OpenTime.Hour;
var minute = candle.OpenTime.Minute;
var totalMinutes = hour * 60 + minute;
var tradeStart = TradeHour * 60;
var tradeEnd = tradeStart + WindowMinutes;
var closeStart = CloseHour * 60;
var close = candle.ClosePrice;
// Close position at close hour
if (Position != 0 && totalMinutes >= closeStart && totalMinutes < closeStart + 30)
{
if (Position > 0)
SellMarket();
else
BuyMarket();
_entryPrice = 0;
}
if (_prevEma == 0)
{
_prevEma = emaVal;
return;
}
// Trade within window
var inWindow = totalMinutes >= tradeStart && totalMinutes < tradeEnd;
if (Position == 0 && inWindow)
{
var emaRising = emaVal > _prevEma;
var emaFalling = emaVal < _prevEma;
if (emaRising && close > emaVal)
{
_entryPrice = close;
BuyMarket();
}
else if (emaFalling && close < emaVal)
{
_entryPrice = close;
SellMarket();
}
}
_prevEma = emaVal;
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import ExponentialMovingAverage
class open_time_daily_window_strategy(Strategy):
def __init__(self):
super(open_time_daily_window_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._ema_length = self.Param("EmaLength", 20) \
.SetDisplay("EMA Length", "EMA period for direction", "Indicators")
self._trade_hour = self.Param("TradeHour", 10) \
.SetDisplay("Trade Hour", "Hour when trading window opens", "Schedule")
self._window_minutes = self.Param("WindowMinutes", 120) \
.SetDisplay("Window Minutes", "Duration of trading window", "Schedule")
self._close_hour = self.Param("CloseHour", 20) \
.SetDisplay("Close Hour", "Hour to close positions", "Schedule")
self._prev_ema = 0.0
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def EmaLength(self):
return self._ema_length.Value
@property
def TradeHour(self):
return self._trade_hour.Value
@property
def WindowMinutes(self):
return self._window_minutes.Value
@property
def CloseHour(self):
return self._close_hour.Value
def OnStarted2(self, time):
super(open_time_daily_window_strategy, self).OnStarted2(time)
self._prev_ema = 0.0
self._entry_price = 0.0
self._ema = ExponentialMovingAverage()
self._ema.Length = self.EmaLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._ema, self.ProcessCandle).Start()
def ProcessCandle(self, candle, ema_val):
if candle.State != CandleStates.Finished:
return
ev = float(ema_val)
hour = candle.OpenTime.Hour
minute = candle.OpenTime.Minute
total_minutes = hour * 60 + minute
trade_start = self.TradeHour * 60
trade_end = trade_start + self.WindowMinutes
close_start = self.CloseHour * 60
close = float(candle.ClosePrice)
# Close position at close hour
if self.Position != 0 and total_minutes >= close_start and total_minutes < close_start + 30:
if self.Position > 0:
self.SellMarket()
else:
self.BuyMarket()
self._entry_price = 0.0
if self._prev_ema == 0:
self._prev_ema = ev
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_ema = ev
return
# Trade within window
in_window = total_minutes >= trade_start and total_minutes < trade_end
if self.Position == 0 and in_window:
ema_rising = ev > self._prev_ema
ema_falling = ev < self._prev_ema
if ema_rising and close > ev:
self._entry_price = close
self.BuyMarket()
elif ema_falling and close < ev:
self._entry_price = close
self.SellMarket()
self._prev_ema = ev
def OnReseted(self):
super(open_time_daily_window_strategy, self).OnReseted()
self._prev_ema = 0.0
self._entry_price = 0.0
def CreateClone(self):
return open_time_daily_window_strategy()