A estratégia reproduz o consultor especialista MetaTrader 1_Otkat_Sys. Ele monitora a abertura, fechamento, máxima e
e baixo para decidir se deseja entrar em uma posição durante os primeiros três minutos após a meia-noite (horário da corretora) de terça a
Quinta-feira.
Lógica de negociação
Estatísticas diárias – a última vela diária concluída é armazenada em cache para calcular:
Open - Close e Close - Open para detectar se a sessão anterior foi de baixa ou alta.
Close - Low e High - Close para medir o quão profundamente o preço recuou dos extremos.
Janela de entrada – novas negociações são avaliadas quando a vela de entrada abre entre 00h00 e 00h03. Segunda e sexta são
ignorado, correspondendo aos filtros DayOfWeek do robô original.
Filtros direcionais – quatro condições mutuamente exclusivas espelham as regras MQL:
Dia anterior de baixa (Open - Close acima do limite do corredor) combinado com uma retração superficial (Close - Low
abaixo de Pullback - Tolerance) abre um longo.
A alta do dia anterior com uma retração ascendente estendida (High - Close acima de Pullback + Tolerance) também abre uma posição longa.
A alta do dia anterior com uma retração ascendente fraca (High - Close abaixo de Pullback - Tolerance) abre uma venda.
O dia anterior de baixa com uma retração negativa estendida (Close - Low acima de Pullback + Tolerance) abre uma venda.
Pedidos – as entradas são ordens de mercado colocadas com o tamanho de lote configurado. As negociações de compra usam uma distância de lucro igual a
TakeProfit + 3 pontos (como no EA original); shorts usam exatamente TakeProfit pontos. Ambos os lados aplicam o mesmo stop-loss
distância.
Saída baseada em tempo – qualquer posição aberta é estabilizada após 22h45, replicando a limpeza noturna implementada em MetaTrader
roteiro.
Todos os parâmetros de limite são expressos em pontos e traduzidos em distâncias de preço com o PriceStep do instrumento.
Parâmetros
Nome
Descrição
EntryCandleType
Período utilizado para a janela de negociação (padrão: 1 minuto).
DailyCandleType
Prazo que fornece as estatísticas diárias (padrão: 1 dia).
TakeProfit
Meta de lucro em pontos. As negociações longas adicionam um buffer de 3 pontos.
StopLoss
Distância de parada protetora em pontos.
PullbackThreshold
Limite de retrocesso básico ("Otkat") em pontos.
CorridorThreshold
Limite do corredor direcional (KoridorOC).
ToleranceThreshold
Tolerância de retrocesso (KoridorOt).
TradeVolume
Tamanho do lote para cada entrada.
A estratégia redefine automaticamente seus valores armazenados em cache em Reset, inscreve-se em fluxos de velas de entrada e diários e sorteia
velas mais marcadores comerciais quando uma área do gráfico estiver disponível.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class OtkatSysStrategy : Strategy
{
private readonly StrategyParam<int> _channelPeriod;
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _cooldownCandles;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose;
private decimal _prevMid;
private bool _hasPrev;
private int _cooldownRemaining;
public int ChannelPeriod { get => _channelPeriod.Value; set => _channelPeriod.Value = value; }
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public OtkatSysStrategy()
{
_channelPeriod = Param(nameof(ChannelPeriod), 48).SetDisplay("Channel Period", "Channel lookback", "Indicators");
_emaPeriod = Param(nameof(EmaPeriod), 20).SetDisplay("EMA Period", "EMA filter", "Indicators");
_cooldownCandles = Param(nameof(CooldownCandles), 150).SetDisplay("Cooldown", "Candles between signals", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = default;
_prevMid = default;
_hasPrev = default;
_cooldownRemaining = default;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = 0;
_prevMid = 0;
_hasPrev = false;
_cooldownRemaining = 0;
var highest = new Highest { Length = ChannelPeriod };
var lowest = new Lowest { Length = ChannelPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(highest, lowest, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal highest, decimal lowest)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
var mid = (highest + lowest) / 2;
if (!_hasPrev) { _prevClose = close; _prevMid = mid; _hasPrev = true; return; }
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevClose = close;
_prevMid = mid;
return;
}
if (_prevClose <= _prevMid && close > mid && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownCandles;
}
else if (_prevClose >= _prevMid && close < mid && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_cooldownRemaining = CooldownCandles;
}
_prevClose = close;
_prevMid = mid;
}
}
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.Indicators import Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class otkat_sys_strategy(Strategy):
def __init__(self):
super(otkat_sys_strategy, self).__init__()
self._channel_period = self.Param("ChannelPeriod", 48) \
.SetDisplay("Channel Period", "Channel lookback", "Indicators")
self._cooldown_candles = self.Param("CooldownCandles", 150) \
.SetDisplay("Cooldown", "Candles between signals", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_close = 0.0
self._prev_mid = 0.0
self._has_prev = False
self._cooldown_remaining = 0
@property
def channel_period(self):
return self._channel_period.Value
@property
def cooldown_candles(self):
return self._cooldown_candles.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(otkat_sys_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_mid = 0.0
self._has_prev = False
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(otkat_sys_strategy, self).OnStarted2(time)
self._prev_close = 0.0
self._prev_mid = 0.0
self._has_prev = False
self._cooldown_remaining = 0
highest = Highest()
highest.Length = self.channel_period
lowest = Lowest()
lowest.Length = self.channel_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(highest, lowest, self.process_candle).Start()
def process_candle(self, candle, highest, lowest):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
mid = (float(highest) + float(lowest)) / 2.0
if not self._has_prev:
self._prev_close = close
self._prev_mid = mid
self._has_prev = True
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_close = close
self._prev_mid = mid
return
if self._prev_close <= self._prev_mid and close > mid and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self.cooldown_candles
elif self._prev_close >= self._prev_mid and close < mid and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self.cooldown_candles
self._prev_close = close
self._prev_mid = mid
def CreateClone(self):
return otkat_sys_strategy()