Ver en GitHub

Estrategia del sistema Otkat

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

  1. 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.
  2. 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.
  3. 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.
  4. Ó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.
  5. 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;
	}
}