Ver en GitHub

Estrategia de reversión de brechas

Descripción general

La Estrategia de reversión de brechas es una adaptación directa del MetaTrader4 asesor experto gaps.mq4. El sistema monitorea velas de 15 minutos y baño. ks para abrir brechas que ocurren fuera del rango máximo/bajo de la vela anterior. Cuando aparece tal brecha, la estrategia inmediatamente e entra en el mercado en la dirección del movimiento de reversión a la media esperado.

La versión StockSharp sigue la lógica original y depende de la suscripción de vela de alto nivel API. Toda la gestión comercial es se realiza con órdenes de mercado y no se colocan órdenes de protección fijas, lo que refleja el comportamiento encontrado en el código MQL.

Reglas de trading

  1. Suscríbete a velas de 15 minutos (configurable a través del parámetro CandleType).
  2. Mantenga el máximo y el mínimo de la vela completada anteriormente.
  3. Cuando comienza una nueva vela:
    • Calcule el buffer de brecha: (MinGapSize + spreadInSteps) * pointValue.
    • Si el precio de apertura está por encima de previousHigh + gapBuffer, abra una posición corta.
    • Si el precio de apertura está por debajo de previousLow - gapBuffer, abra una posición larga.
  4. Sólo se permite una operación por vela. Una vez que se realiza una orden, la estrategia espera la siguiente vela antes de generar una nueva vela. señal.

El componente de diferencial utiliza la mejor oferta/demanda actual, si está disponible. Cuando no se proporcionan datos de cotización, la estrategia vuelve a caer en pecado. Un pequeño paso en el precio como amortiguador conservador.

Parámetros

Parámetro Predeterminado Descripción
MinGapSize 1 Tamaño mínimo de brecha en los escalones de precios que se debe exceder antes de enviar un pedido.
GapVolume 0.1 Volumen de pedidos para entradas al mercado provocadas por brechas.
CandleType 15m TimeFrame Tipo de vela utilizado para los cálculos (el valor predeterminado es velas de 15 minutos).

Todos los parámetros están registrados como StrategyParam<T> y admiten la optimización dentro de StockSharp Designer u otras herramientas.

Notas de implementación

  • Utiliza SubscribeCandles con Bind para procesar velas terminadas únicamente.
  • Recuerda el rango de la vela anterior para evitar volver a calcular la serie de datos.
  • Bloquea órdenes duplicadas en la misma vela rastreando el tiempo de apertura de la barra que desencadenó la operación.
  • La salida del gráfico dibuja las velas suscritas y las operaciones de estrategia para una inspección visual rápida.

Diferencias con la versión MQL

  • Los niveles de toma de ganancias y límite de pérdidas no se establecieron correctamente en el EA original (el código MQL pasó valores a los parámetros incorrectos) . Por tanto, el puerto StockSharp mantiene el comportamiento de funcionamiento sin órdenes de protección.
  • El manejo de diferenciales ahora verifica las cotizaciones de oferta y demanda en tiempo real cuando están disponibles, lo que proporciona un margen más adaptable.

Requisitos

  • StockSharp API con acceso a los datos de velas para el instrumento seleccionado.
  • Las cotizaciones de nivel 1 son opcionales pero mejoran la detección de diferenciales.

Momento en la reproducción: el hueco se comprueba en la primera actualización de cada vela (Active si está disponible). Si la fuente solo emite velas Finished, el hueco de apertura se detecta al cierre; esto no demuestra una ejecución al precio de apertura.

namespace StockSharp.Samples.Strategies;

using System;
using System.Collections.Generic;

using StockSharp.Algo;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

/// <summary>
/// Enters against gaps between a new candle's open and the previous completed range.
/// No EMA condition or automatic protection is part of the original gap rules.
/// </summary>
public class GapReversionStrategy : Strategy
{
    private readonly StrategyParam<decimal> _minGapSize;
    private readonly StrategyParam<decimal> _gapVolume;
    private readonly StrategyParam<DataType> _candleType;

    private decimal? _previousHigh;
    private decimal? _previousLow;
    private DateTimeOffset? _observedBar;
    private decimal? _bid;
    private decimal? _ask;

    public decimal MinGapSize { get => _minGapSize.Value; set => _minGapSize.Value = value; }
    public decimal GapVolume { get => _gapVolume.Value; set => _gapVolume.Value = value; }
    public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

    public GapReversionStrategy()
    {
        _minGapSize = Param(nameof(MinGapSize), 1m)
            .SetGreaterThanZero()
            .SetDisplay("Minimum Gap", "Minimum open gap in price steps", "Signals");
        _gapVolume = Param(nameof(GapVolume), 0.1m)
            .SetGreaterThanZero()
            .SetDisplay("Gap Volume", "Volume of each market gap entry", "Trading");
        _candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
            .SetDisplay("Candle Type", "Candle timeframe", "General");
    }

    public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
        => [(Security, CandleType)];

    protected override void OnReseted()
    {
        base.OnReseted();
        ClearState();
    }

    private void ClearState()
    {
        _previousHigh = null;
        _previousLow = null;
        _observedBar = null;
        _bid = null;
        _ask = null;
    }

    protected override void OnStarted2(DateTime time)
    {
        base.OnStarted2(time);
        ClearState();
        Volume = GapVolume;

        // Quote subscriptions are optional: one price step is used as spread
        // if the replay or provider does not expose both best prices.
        foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
        {
            var quotes = new Subscription(DataType.Level1, Security);
            quotes.MarketData.BuildField = field;
            SubscribeLevel1(quotes).Bind(ProcessQuote).Start();
        }

        var candles = SubscribeCandles(CandleType);
        candles.Bind(ProcessCandle).Start();
    }

    private void ProcessQuote(Level1ChangeMessage quote)
    {
        if (quote.TryGetDecimal(Level1Fields.BestBidPrice) is decimal bid)
            _bid = bid;
        if (quote.TryGetDecimal(Level1Fields.BestAskPrice) is decimal ask)
            _ask = ask;
    }

    private void ProcessCandle(ICandleMessage candle)
    {
        if (candle.State is not (CandleStates.Active or CandleStates.Finished))
            return;

        // Process a bar just once, at its first update (Active if available).
        // Finished-only feeds can only evaluate the opening gap retrospectively.
        if (_observedBar != candle.OpenTime)
        {
            _observedBar = candle.OpenTime;
            if (_previousHigh is decimal high && _previousLow is decimal low)
            {
                var step = Security?.PriceStep ?? 0m;
                if (step > 0m)
                {
                    var spread = _bid is decimal bid && _ask is decimal ask && ask > bid
                        ? ask - bid
                        : step;
                    var buffer = MinGapSize * step + spread;
                    if (candle.OpenPrice > high + buffer)
                        SellMarket(GapVolume);
                    else if (candle.OpenPrice < low - buffer)
                        BuyMarket(GapVolume);
                }
            }
        }

        if (candle.State == CandleStates.Finished)
        {
            _previousHigh = candle.HighPrice;
            _previousLow = candle.LowPrice;
        }
    }
}