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
Cada vela concluída no período selecionado aciona uma verificação da hora do dia.
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.
Enquanto a janela de fechamento estiver ativa, a estratégia chama ClosePosition() uma vez para nivelar qualquer exposição pendente.
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;
}
}
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()