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
Suscríbete a velas de 15 minutos (configurable a través del parámetro CandleType).
Mantenga el máximo y el mínimo de la vela completada anteriormente.
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.
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;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Level1Fields
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Strategies import Strategy
class gap_reversion_strategy(Strategy):
"""Trades against opening gaps beyond the previous candle's high/low and spread."""
def __init__(self):
super(gap_reversion_strategy, self).__init__()
self._min_gap_size = self.Param("MinGapSize", 1.0).SetGreaterThanZero().SetDisplay(
"Minimum Gap", "Minimum open gap in price steps", "Signals")
self._gap_volume = self.Param("GapVolume", 0.1).SetGreaterThanZero().SetDisplay(
"Gap Volume", "Volume of each market gap entry", "Trading")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay(
"Candle Type", "Candle timeframe", "General")
self._clear_state()
@property
def min_gap_size(self):
return self._min_gap_size.Value
@property
def gap_volume(self):
return self._gap_volume.Value
@property
def candle_type(self):
return self._candle_type.Value
def _clear_state(self):
self._previous_high = None
self._previous_low = None
self._observed_bar = None
self._best_bid = None
self._best_ask = None
def OnReseted(self):
super(gap_reversion_strategy, self).OnReseted()
self._clear_state()
def OnStarted2(self, time):
super(gap_reversion_strategy, self).OnStarted2(time)
self._clear_state()
self.Volume = Decimal(self.gap_volume)
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
quotes = Subscription(DataType.Level1, self.Security)
quotes.MarketData.BuildField = field
self.SubscribeLevel1(quotes).Bind(self._process_quote).Start()
self.SubscribeCandles(self.candle_type).Bind(self._process_candle).Start()
def _process_quote(self, quote):
bid = quote.Changes[Level1Fields.BestBidPrice] if quote.Changes.ContainsKey(Level1Fields.BestBidPrice) else None
ask = quote.Changes[Level1Fields.BestAskPrice] if quote.Changes.ContainsKey(Level1Fields.BestAskPrice) else None
if bid is not None:
self._best_bid = float(bid)
if ask is not None:
self._best_ask = float(ask)
def _process_candle(self, candle):
if candle.State != CandleStates.Active and candle.State != CandleStates.Finished:
return
# Active updates allow execution at the open. Finished-only feeds necessarily
# discover the opening gap at close; this is not equivalent to open execution.
if self._observed_bar != candle.OpenTime:
self._observed_bar = candle.OpenTime
if self._previous_high is not None and self._previous_low is not None:
step = float(self.Security.PriceStep)
if step > 0.0:
spread = (self._best_ask - self._best_bid
if self._best_ask is not None and self._best_bid is not None
and self._best_ask > self._best_bid else step)
buffer = float(self.min_gap_size) * step + spread
op = float(candle.OpenPrice)
if op > self._previous_high + buffer:
self.SellMarket(Decimal(self.gap_volume))
elif op < self._previous_low - buffer:
self.BuyMarket(Decimal(self.gap_volume))
if candle.State == CandleStates.Finished:
self._previous_high = float(candle.HighPrice)
self._previous_low = float(candle.LowPrice)
def CreateClone(self):
return gap_reversion_strategy()