A estratégia 21horas reproduz o comportamento do MQL4 consultor especialista 21hour.mq4. Ele opera em torno de uma janela de tempo diária: as ordens de breakout pendentes são criadas em uma hora de início configurável e toda a exposição é removida em uma hora de término configurável. A versão StockSharp mantém a mesma ideia de "duas ordens stop em torno do preço", ao mesmo tempo em que aproveita o API de alto nível para gerenciamento de pedidos, assinaturas de dados de mercado e tratamento protetor de take-profit.
Lógica de negociação
No início de cada dia de negociação, quando o horário do servidor corresponde a StartHour:00, a estratégia lê as últimas cotações de compra/venda e coloca uma ordem de compra e venda.
A distância do preço de venda atual até o gatilho de parada de compra é StepPoints * PriceStep.
A distância do preço de compra atual até o gatilho do stop de venda é o mesmo valor abaixo do mercado.
TakeProfitPoints é convertido em distância de preço por meio da etapa de preço do instrumento e passado para StartProtection, portanto, tanto as posições longas quanto as curtas recebem um take-profit protetor logo após a execução.
Somente uma configuração pendente é permitida por dia. Se apenas uma das duas ordens stop permanecer ativa (por exemplo, após um lado ter sido preenchido), a estratégia cancela a ordem pendente sobrevivente para espelhar a lógica EA original.
Quando o relógio chega a StopHour:00, a estratégia fecha qualquer posição aberta no mercado e cancela todas as ordens pendentes pendentes. Isto se aplica mesmo que não tenha ocorrido nenhuma ruptura.
O fluxo de velas padrão são dados de um minuto. Ele é usado exclusivamente para acionar verificações horárias em velas concluídas, o que imita a proteção prevtime da versão MQL.
Parâmetros
Parâmetro
Descrição
Padrão
Volume
Volume de pedidos em lotes para ambos os pedidos pendentes.
0.1
StartHour
Hora (0–23) em que o par de ordens pendentes é criado.
10
StopHour
Hora (0–23) em que a estratégia fecha posições e remove todas as ordens pendentes.
22
StepPoints
Distância em pontos do instrumento entre o preço atual de compra/venda e cada entrada de stop. Convertido em preço multiplicando por PriceStep.
15
TakeProfitPoints
Distância em pontos do preço de entrada até a meta de lucro gerenciada por StartProtection. Defina como 0 para desativar o alvo.
200
CandleType
Tipo de dados Candle usado para rastreamento de tempo. O padrão é o período de um minuto (TimeSpan.FromMinutes(1).TimeFrame()).
1 minute
Notas de implementação
Usa SubscribeCandles para obter velas concluídas e avaliar a programação horária apenas uma vez por minuto.
Assina cotações de nível 1 via SubscribeLevel1() para manter os valores de compra/venda mais recentes para posicionamento de stop preciso.
Depende de StartProtection com uma unidade de lucro para emular o lucro da ordem pendente do EA original em vez de anexar pedidos manualmente.
Mantém o controle das ordens stop de compra e venda ativas e chama CancelOrder se apenas um lado permanecer, garantindo que o sistema nunca seja executado com uma ordem pendente não emparelhada.
Invoca auxiliares BuyMarket / SellMarket para posições planas na hora de parada, usando estritamente a estratégia de alto nível API.
Notas de comportamento
A estratégia espera que a conexão do corretor forneça informações sobre as etapas do preço. Se PriceStep estiver ausente, os preços não serão arredondados.
Os pedidos pendentes são gerados apenas uma vez por dia corrido. Eles serão recriados no próximo dia de negociação na hora de início configurada, mesmo que o rompimento do dia anterior não tenha sido acionado.
Quando TakeProfitPoints é zero, a estratégia ainda coloca ordens pendentes, mas nenhum lucro protetor é gerenciado.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Time-based breakout strategy. At start hour, detects breakout direction from previous candle range.
/// At stop hour, closes all positions.
/// </summary>
public class TwentyOneHourStrategy : Strategy
{
private readonly StrategyParam<int> _startHour;
private readonly StrategyParam<int> _stopHour;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevHigh;
private decimal _prevLow;
private bool _hasPrev;
private bool _tradedToday;
private int _lastTradeDay;
public TwentyOneHourStrategy()
{
_startHour = Param(nameof(StartHour), 10)
.SetDisplay("Start Hour", "Hour to look for breakout entries.", "Schedule");
_stopHour = Param(nameof(StopHour), 22)
.SetDisplay("Stop Hour", "Hour to close positions.", "Schedule");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candles used for time tracking.", "General");
}
public int StartHour
{
get => _startHour.Value;
set => _startHour.Value = value;
}
public int StopHour
{
get => _stopHour.Value;
set => _stopHour.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <inheritdoc />
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevHigh = 0;
_prevLow = 0;
_hasPrev = false;
_tradedToday = false;
_lastTradeDay = -1;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevHigh = 0;
_prevLow = 0;
_hasPrev = false;
_tradedToday = false;
_lastTradeDay = -1;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var hour = candle.OpenTime.Hour;
var day = candle.OpenTime.DayOfYear;
// Reset daily flag
if (day != _lastTradeDay)
{
_tradedToday = false;
_lastTradeDay = day;
}
// Close at stop hour
if (hour >= StopHour && Position != 0)
{
if (Position > 0)
SellMarket();
else
BuyMarket();
}
// Entry at start hour window
if (hour >= StartHour && hour < StopHour && !_tradedToday && _hasPrev && Position == 0)
{
if (candle.ClosePrice > _prevHigh)
{
BuyMarket();
_tradedToday = true;
}
else if (candle.ClosePrice < _prevLow)
{
SellMarket();
_tradedToday = true;
}
}
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
_hasPrev = true;
}
}
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
class twenty_one_hour_strategy(Strategy):
"""Time-based breakout: enter on breakout from previous candle range, close at stop hour."""
def __init__(self):
super(twenty_one_hour_strategy, self).__init__()
self._start_hour = self.Param("StartHour", 10).SetDisplay("Start Hour", "Hour to look for breakout entries", "Schedule")
self._stop_hour = self.Param("StopHour", 22).SetDisplay("Stop Hour", "Hour to close positions", "Schedule")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Timeframe", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(twenty_one_hour_strategy, self).OnReseted()
self._prev_high = 0
self._prev_low = 0
self._has_prev = False
self._traded_today = False
self._last_trade_day = -1
def OnStarted2(self, time):
super(twenty_one_hour_strategy, self).OnStarted2(time)
self._prev_high = 0
self._prev_low = 0
self._has_prev = False
self._traded_today = False
self._last_trade_day = -1
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawOwnTrades(area)
def OnProcess(self, candle):
if candle.State != CandleStates.Finished:
return
hour = candle.OpenTime.Hour
day = candle.OpenTime.DayOfYear
# Reset daily flag
if day != self._last_trade_day:
self._traded_today = False
self._last_trade_day = day
# Close at stop hour
start_h = self._start_hour.Value
stop_h = self._stop_hour.Value
if hour >= stop_h and self.Position != 0:
if self.Position > 0:
self.SellMarket()
else:
self.BuyMarket()
# Entry at start hour window
if hour >= start_h and hour < stop_h and not self._traded_today and self._has_prev and self.Position == 0:
close = float(candle.ClosePrice)
if close > self._prev_high:
self.BuyMarket()
self._traded_today = True
elif close < self._prev_low:
self.SellMarket()
self._traded_today = True
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._has_prev = True
def CreateClone(self):
return twenty_one_hour_strategy()