Ver no GitHub

Estratégia de janela diária de tempo aberto

Visão geral

A estratégia reproduz o comportamento do especialista MetaTrader "OpenTime". Ele coloca ordens de mercado em um horário do dia configurável, opcionalmente fecha todas as exposições durante uma janela de saída dedicada e aplica regras simples de gerenciamento de dinheiro, como stop-loss fixo, take-profit e proteção móvel. A porta usa o StockSharp Strategy API de alto nível, para que a estratégia possa ser combinada com outros componentes dentro da estrutura.

Como funciona

  1. Cada vela concluída no período selecionado aciona uma verificação da hora do dia.
  2. Quando a hora atual cai dentro da janela de negociação, a estratégia envia ordens de mercado para todas as direções habilitadas:
    • Se apenas um lado estiver habilitado, a posição líquida atual será estendida ou invertida até que o volume solicitado seja alcançado.
    • Quando ambos os lados estão habilitados, as ordens de compra e venda são emitidas na mesma janela. Como StockSharp contabiliza a exposição líquida lado a lado, a abertura da segunda direção compensa automaticamente a exposição oposta antes de estabelecer a nova.
  3. Enquanto a janela de fechamento estiver ativa, a estratégia chama ClosePosition() uma vez para nivelar qualquer exposição pendente.
  4. As distâncias opcionais de stop-loss, take-profit e trailing stop são delegadas a StartProtection, que gerencia as ordens de proteção usando saídas de mercado.

Parâmetros

  • Ativar Fechar Janela – espelha o sinalizador TimeClose. Quando ativados, Close Position Time e Window Length definem quando as negociações existentes são fechadas.
  • Horário de posição fechada – horário diário de início da janela de saída (padrão 20:50).
  • Horário de negociação – horário diário em que novas negociações são permitidas (padrão 18h50).
  • Duração da janela – duração das janelas de negociação e fechamento (padrão 5 minutos, correspondendo à entrada original Duration).
  • Permitir entradas de venda – corresponde à opção MQL Sell; habilita entradas curtas (padrão verdadeiro).
  • Permitir entradas de compra – corresponde à opção MQL Buy; permite entradas longas (padrão falso).
  • Volume do pedido – volume líquido alvo para cada nova negociação (padrão 0,1 lote). A estratégia soma o valor absoluto da posição atual quando aparece um sinal oposto, de forma que as reversões ocorram em uma única ordem de mercado.
  • Stop-Loss Points – distância em pontos para a parada de proteção (padrão 0 desabilita a parada).
  • Pontos Take-Profit – distância em pontos para a meta de lucro (o padrão 0 desativa a meta).
  • Use Trailing Stop – ativa a lógica de trailing stop do auxiliar SimpleTrailing original.
  • Pontos de parada final – distância final expressa em pontos (padrão 300).
  • Trailing Step Points – progresso adicional necessário antes de avançar o trailing stop (padrão 3).
  • Tipo de vela – período de tempo usado para as verificações de tempo (velas padrão de 1 minuto).

Notas

  • O tamanho do ponto é derivado da etapa do preço do título. Para aspas de três e cinco decimais, a etapa é multiplicada por 10, reproduzindo o tratamento de pip usado pelo script MQL.
  • StartProtection anexa paradas de proteção somente quando pelo menos uma das distâncias é maior que zero. Se o rastreamento estiver ativo sem um stop loss fixo, a distância de rastreamento será fornecida como o valor de proteção inicial.
  • A estratégia intencionalmente não gerencia ordens pendentes ou tentativas repetidas, porque StockSharp já fornece tratamento automático de erros para ordens 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;
	}
}