La estrategia reproduce el asesor experto MetaTrader 1_Otkat_Sys. Supervisa la apertura, el cierre, el máximo y el máximo del día de negociación anterior.
y bajo para decidir si ingresar una posición durante los primeros tres minutos después de la medianoche (hora del corredor) del martes al
Jueves.
Lógica de trading
Estadísticas diarias: la última vela diaria completada se almacena en caché para calcular:
Open - Close y Close - Open para detectar si la sesión anterior fue bajista o alcista.
Close - Low y High - Close para medir en qué medida el precio retrocedió desde los extremos.
Ventana de entrada: las nuevas operaciones se evalúan cuando la vela de entrada se abre entre las 00:00 y las 00:03. lunes y viernes son
omitido, coincidiendo con los filtros DayOfWeek del robot original.
Filtros direccionales: cuatro condiciones mutuamente excluyentes reflejan las reglas MQL:
Día anterior bajista (Open - Close por encima del umbral del corredor) combinado con un retroceso superficial (Close - Low
debajo de Pullback - Tolerance) abre un largo.
El día anterior alcista con un retroceso alcista extendido (High - Close por encima de Pullback + Tolerance) también abre una posición larga.
El día anterior alcista con un retroceso alcista débil (High - Close por debajo de Pullback - Tolerance) abre una venta corta.
El día anterior bajista con un retroceso bajista extendido (Close - Low por encima de Pullback + Tolerance) abre una posición corta.
Órdenes: las entradas son órdenes de mercado realizadas con el tamaño de lote configurado. Las operaciones de compra utilizan una distancia de obtención de beneficios igual a
TakeProfit + 3 puntos (como en el EA original); Los pantalones cortos usan exactamente TakeProfit puntos. Ambas partes aplican el mismo stop-loss
distancia.
Salida basada en tiempo: cualquier posición abierta se aplana después de las 22:45, replicando la limpieza nocturna implementada en el MetaTrader
guión.
Todos los parámetros de umbral se expresan en puntos y se traducen a distancias de precios con el PriceStep del instrumento.
Parámetros
Nombre
Descripción
EntryCandleType
Marco de tiempo utilizado para la ventana de negociación (predeterminado: 1 minuto).
DailyCandleType
Plazo que proporciona las estadísticas diarias (predeterminado: 1 día).
TakeProfit
Objetivo de beneficio en puntos. Las operaciones largas añaden un colchón de 3 puntos.
StopLoss
Distancia de parada de protección en puntos.
PullbackThreshold
Umbral de retroceso base ("Otkat") en puntos.
CorridorThreshold
Umbral del corredor direccional (KoridorOC).
ToleranceThreshold
Tolerancia de retroceso (KoridorOt).
TradeVolume
Tamaño del lote para cada entrada.
La estrategia restablece automáticamente sus valores almacenados en caché el Reset, se suscribe a flujos de velas tanto de entrada como diarios, y extrae
velas más marcadores comerciales cuando un área del gráfico esté disponible.
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()