Ver no GitHub

Estratégia de Reversão de Gap

Visão geral

A Estratégia de Reversão de Gap é uma porta direta do MetaTrader 4 consultor especialista gaps.mq4. O sistema monitora velas e loo de 15 minutos ks para abertura de gaps que ocorrem fora da faixa máxima/baixa da vela anterior. Quando tal lacuna aparece, a estratégia imediatamente muda. entra no mercado na direção do movimento de reversão à média esperado.

A versão StockSharp segue a lógica original enquanto depende da assinatura de vela de alto nível API. Toda a gestão comercial é feito com ordens de mercado e nenhuma ordem de proteção fixa é colocada, refletindo o comportamento encontrado no código MQL.

Regras de negociação

  1. Assine velas de 15 minutos (configuráveis através do parâmetro CandleType).
  2. Mantenha os valores máximos e mínimos da vela concluída anterior.
  3. Quando uma nova vela começa:
    • Calcule o buffer de lacuna: (MinGapSize + spreadInSteps) * pointValue.
    • Se o preço de abertura estiver acima previousHigh + gapBuffer, abra uma posição curta.
    • Se o preço de abertura estiver abaixo de previousLow - gapBuffer, abra uma posição longa.
  4. Apenas uma negociação por vela é permitida. Uma vez colocada uma ordem, a estratégia espera pela próxima vela antes de gerar uma nova. sinal.

O componente de spread usa o melhor lance/venda atual, se disponível. Quando nenhum dado de cotação é fornecido, a estratégia cai em pecado passo de preço como um buffer conservador.

Parâmetros

Parâmetro Padrão Descrição
MinGapSize 1 Tamanho mínimo do intervalo nas etapas de preço que deve ser excedido antes de enviar um pedido.
GapVolume 0.1 Volume de pedidos para entradas no mercado desencadeadas por lacunas.
CandleType 15m TimeFrame Tipo de vela usado para cálculos (o padrão é velas de 15 minutos).

Todos os parâmetros são registrados como StrategyParam<T> e suportam otimização dentro do StockSharp Designer ou outras ferramentas.

Notas de implementação

  • Usa SubscribeCandles com Bind para processar apenas velas concluídas.
  • Lembra o intervalo da vela anterior para evitar o recálculo da série de dados.
  • Bloqueia pedidos duplicados na mesma vela rastreando o tempo de abertura da barra que acionou a negociação.
  • A saída do gráfico desenha as velas subscritas e as negociações estratégicas para uma rápida inspeção visual.

Diferenças da versão MQL

  • Os níveis de take-profit e stop-loss não foram definidos corretamente no EA original (o código MQL passou valores para os parâmetros errados) . A porta StockSharp, portanto, mantém o comportamento de execução sem ordens de proteção.
  • O gerenciamento de spread agora verifica cotações de compra/venda em tempo real quando disponíveis, fornecendo um buffer mais adaptável.

Requisitos

  • StockSharp API com acesso aos dados da vela para o instrumento selecionado.
  • As cotações de nível 1 são opcionais, mas melhoram a detecção de spread.

Momento no replay: o gap é verificado na primeira atualização de cada candle (Active, quando disponível). Se a fonte fornecer apenas candles Finished, o gap de abertura só será identificado no fechamento; isso não comprova execução no preço de abertura.

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;
        }
    }
}