Ver en GitHub

Estrategia de ventana diaria de tiempo abierto

Descripción general

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

  1. Cada vela terminada del período de tiempo seleccionado activa una verificación de la hora del día.
  2. 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.
  3. Mientras la ventana de cierre está activa, la estrategia llama a ClosePosition() una vez para reducir cualquier exposición pendiente.
  4. 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;
	}
}