Estrategia Gap Fill Reversal
La estrategia Reversión por Relleno de Brecha aprovecha los gaps nocturnos que retroceden rápidamente durante la siguiente sesión. Cuando el precio forma un gap alejándose del cierre anterior pero inmediatamente regresa para llenar ese vacío, a menudo señala el agotamiento del movimiento inicial.
Las pruebas indican un rendimiento anual promedio de aproximadamente 181%. Funciona mejor en el mercado de criptomonedas.
La estrategia entra una vez que el gap está completamente cerrado y busca una reversión en la dirección opuesta a la apertura. Su objetivo es capturar el rebote que ocurre cuando los traders atrapados salen de sus posiciones.
Un stop basado en porcentaje define el riesgo, y las posiciones se cierran cuando el impulso se desvanece o se alcanza el stop.
Detalles
- Criterios de entrada: coincidencia de patrón
- Largo/Corto: Ambos
- Criterios de salida: stop-loss o señal opuesta
- Stops: Sí, basado en porcentaje
- Valores predeterminados:
CandleType= 15 minutosStopLoss= 2%
- Filtros:
- Categoría: Patrón
- Dirección: Ambos
- Indicadores: Gap
- Stops: Sí
- Complejidad: Intermedio
- Marco temporal: Intradía
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Gap Fill Reversal strategy.
/// Enters when a gap between candles is followed by a reversal candle.
/// Gap up + bearish candle = short, gap down + bullish candle = long.
/// Uses SMA for exit confirmation.
/// Uses cooldown to control trade frequency.
/// </summary>
public class GapFillReversalStrategy : Strategy
{
private readonly StrategyParam<decimal> _minGapPercent;
private readonly StrategyParam<int> _maLength;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private ICandleMessage _prevCandle;
private int _cooldown;
/// <summary>
/// Minimum gap size as percentage.
/// </summary>
public decimal MinGapPercent
{
get => _minGapPercent.Value;
set => _minGapPercent.Value = value;
}
/// <summary>
/// MA period for exit.
/// </summary>
public int MaLength
{
get => _maLength.Value;
set => _maLength.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Cooldown bars.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public GapFillReversalStrategy()
{
_minGapPercent = Param(nameof(MinGapPercent), 0.02m)
.SetRange(0.01m, 1m)
.SetDisplay("Min Gap %", "Minimum gap size percentage", "Trading");
_maLength = Param(nameof(MaLength), 20)
.SetRange(10, 50)
.SetDisplay("MA Length", "Period of SMA for exit", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_cooldownBars = Param(nameof(CooldownBars), 500)
.SetRange(1, 1000)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevCandle = null;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevCandle = null;
_cooldown = 0;
var sma = new SimpleMovingAverage { Length = MaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_prevCandle == null)
{
_prevCandle = candle;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevCandle = candle;
return;
}
var prevClose = _prevCandle.ClosePrice;
// Gap detection
var gapUp = candle.OpenPrice > prevClose;
var gapDown = candle.OpenPrice < prevClose;
decimal gapPercent = 0;
if (gapUp)
gapPercent = (candle.OpenPrice - prevClose) / prevClose * 100;
else if (gapDown)
gapPercent = (prevClose - candle.OpenPrice) / prevClose * 100;
var isBearishCandle = candle.ClosePrice < candle.OpenPrice;
var isBullishCandle = candle.ClosePrice > candle.OpenPrice;
if (gapPercent >= MinGapPercent)
{
// Gap down + bullish reversal = long
if (Position == 0 && gapDown && isBullishCandle)
{
BuyMarket();
_cooldown = CooldownBars;
}
// Gap up + bearish reversal = short
else if (Position == 0 && gapUp && isBearishCandle)
{
SellMarket();
_cooldown = CooldownBars;
}
}
// Exit on SMA cross
if (Position > 0 && candle.ClosePrice < smaValue)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position < 0 && candle.ClosePrice > smaValue)
{
BuyMarket();
_cooldown = CooldownBars;
}
_prevCandle = candle;
}
}
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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class gap_fill_reversal_strategy(Strategy):
"""
Gap Fill Reversal strategy.
Enters when a gap between candles is followed by a reversal candle.
Gap up + bearish candle = short, gap down + bullish candle = long.
Uses SMA for exit confirmation.
"""
def __init__(self):
super(gap_fill_reversal_strategy, self).__init__()
self._min_gap_percent = self.Param("MinGapPercent", 0.02).SetDisplay("Min Gap %", "Minimum gap size percentage", "Trading")
self._ma_length = self.Param("MaLength", 20).SetDisplay("MA Length", "Period of SMA for exit", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._cooldown_bars = self.Param("CooldownBars", 500).SetDisplay("Cooldown Bars", "Bars to wait between trades", "General")
self._prev_candle = None
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(gap_fill_reversal_strategy, self).OnReseted()
self._prev_candle = None
self._cooldown = 0
def OnStarted2(self, time):
super(gap_fill_reversal_strategy, self).OnStarted2(time)
self._prev_candle = None
self._cooldown = 0
sma = SimpleMovingAverage()
sma.Length = self._ma_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, sma_val):
if candle.State != CandleStates.Finished:
return
if self._prev_candle is None:
self._prev_candle = candle
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_candle = candle
return
prev_close = float(self._prev_candle.ClosePrice)
open_price = float(candle.OpenPrice)
# Gap detection
gap_up = open_price > prev_close
gap_down = open_price < prev_close
gap_percent = 0.0
if gap_up and prev_close > 0:
gap_percent = (open_price - prev_close) / prev_close * 100.0
elif gap_down and prev_close > 0:
gap_percent = (prev_close - open_price) / prev_close * 100.0
is_bearish = candle.ClosePrice < candle.OpenPrice
is_bullish = candle.ClosePrice > candle.OpenPrice
sv = float(sma_val)
cd = self._cooldown_bars.Value
min_gap = self._min_gap_percent.Value
if gap_percent >= min_gap:
# Gap down + bullish reversal = long
if self.Position == 0 and gap_down and is_bullish:
self.BuyMarket()
self._cooldown = cd
# Gap up + bearish reversal = short
elif self.Position == 0 and gap_up and is_bearish:
self.SellMarket()
self._cooldown = cd
# Exit on SMA cross
if self.Position > 0 and float(candle.ClosePrice) < sv:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0 and float(candle.ClosePrice) > sv:
self.BuyMarket()
self._cooldown = cd
self._prev_candle = candle
def CreateClone(self):
return gap_fill_reversal_strategy()