La Brecha Nocturna opera en la apertura cuando el precio presenta un gap significativo respecto al cierre anterior debido a noticias o actividad fuera de horario.
Los grandes gaps suelen retraerse parcialmente a medida que los operadores digieren el movimiento.
Las pruebas indican un retorno anual promedio de aproximadamente el 124%. Funciona mejor en el mercado de forex.
La estrategia va en contra de los gaps excesivos, entrando en dirección opuesta poco después de la apertura y cerrando antes de que termine la sesión.
Los stops se basan en un porcentaje más allá de los extremos del gap para gestionar el riesgo si el movimiento continúa.
Detalles
Criterios de entrada: señal de indicador
Largo/Corto: Ambos
Criterios de salida: stop-loss o señal opuesta
Stops: Sí, basado en porcentaje
Valores predeterminados:
CandleType = 15 minute
StopLossPercent = 2
MinGapPercent = 0.01
El mercado opera las 24 horas, por lo que la sesión es el día UTC. El gap es la distancia entre el último cierre del día anterior y la primera apertura del día. Un gap de al menos MinGapPercent se opera en contra al cierre de la primera vela; el stop se sitúa StopLossPercent más allá del máximo (corto) o del mínimo (largo) de esa vela y se comprueba en los cierres, y la posición se cierra en la última vela del día.
Filtros:
Categoría: Gap
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 System.Linq;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Overnight Gap strategy.
/// The market trades around the clock, so the session is the UTC day.
/// The gap is the distance between the previous UTC day's last close and the new day's first open. A gap of at least MinGapPercent
/// is faded at the close of the day's first candle; the stop lies StopLossPercent beyond the first candle's extreme in the gap's direction,
/// and the position closes at the day's last candle.
/// </summary>
public class OvernightGapStrategy : Strategy
{
private readonly StrategyParam<decimal> _minGapPercent;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private DateTime? _day;
private decimal? _prevClose;
private decimal _stopPrice;
/// <summary>
/// Smallest gap to fade, in percent.
/// </summary>
public decimal MinGapPercent
{
get => _minGapPercent.Value;
set => _minGapPercent.Value = value;
}
/// <summary>
/// Distance of the stop beyond the first candle's extreme, in percent.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public OvernightGapStrategy()
{
_minGapPercent = Param(nameof(MinGapPercent), 0.01m)
.SetGreaterThanZero()
.SetDisplay("Min Gap %", "Smallest gap to fade, in percent", "Session");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Distance of the stop beyond the first candle's extreme, in percent", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_day = null;
_prevClose = null;
_stopPrice = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_day = null;
_prevClose = null;
_stopPrice = default;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ClosePosition()
{
if (Position > 0)
SellMarket(Position);
else if (Position < 0)
BuyMarket(-Position);
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var day = candle.OpenTime.Date;
var firstOfDay = _day != day;
if (firstOfDay)
{
_day = day;
}
var frame = CandleType.Arg is TimeSpan tf ? tf : TimeSpan.Zero;
var lastOfDay = (candle.OpenTime + frame).Date != day;
var prevClose = _prevClose;
_prevClose = candle.ClosePrice;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
if (Position != 0)
{
if (lastOfDay || (Position > 0 ? close <= _stopPrice : close >= _stopPrice))
ClosePosition();
return;
}
if (!firstOfDay || prevClose is not decimal last || last <= 0)
return;
var gap = (candle.OpenPrice - last) / last * 100m;
if (gap >= MinGapPercent)
{
SellMarket(Volume);
_stopPrice = candle.HighPrice * (1 + StopLossPercent / 100m);
}
else if (-gap >= MinGapPercent)
{
BuyMarket(Volume);
_stopPrice = candle.LowPrice * (1 - StopLossPercent / 100m);
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes, Level1Fields
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Strategies import Strategy
class overnight_gap_strategy(Strategy):
"""
Overnight Gap strategy.
The market trades around the clock, so the session is the UTC day.
The gap is the distance between the previous UTC day's last close and the new day's first open. A gap of at least MinGapPercent
is faded at the close of the day's first candle; the stop lies StopLossPercent beyond the first candle's extreme in the gap's direction,
and the position closes at the day's last candle.
"""
def __init__(self):
super(overnight_gap_strategy, self).__init__()
self._min_gap_percent = self.Param("MinGapPercent", 0.01).SetGreaterThanZero().SetDisplay("Min Gap %", "Smallest gap to fade, in percent", "Session")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss %", "Distance of the stop beyond the first candle's extreme, in percent", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._day = None
self._prev_close = None
self._stop_price = Decimal(0)
def OnReseted(self):
super(overnight_gap_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(overnight_gap_strategy, self).OnStarted2(time)
self._reset_state()
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _close_position(self):
if self.Position > 0:
self.SellMarket(self.Position)
elif self.Position < 0:
self.BuyMarket(-self.Position)
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
day = candle.OpenTime.Date
first_of_day = self._day is None or self._day != day
if first_of_day:
self._day = day
frame = self.candle_type.Arg
last_of_day = (candle.OpenTime + frame).Date != day
prev_close = self._prev_close
self._prev_close = candle.ClosePrice
if not self.IsFormedAndOnlineAndAllowTrading():
return
close = candle.ClosePrice
if self.Position != 0:
stopped = close <= self._stop_price if self.Position > 0 else close >= self._stop_price
if last_of_day or stopped:
self._close_position()
return
if not first_of_day or prev_close is None or prev_close <= 0:
return
gap = (candle.OpenPrice - prev_close) / prev_close * Decimal(100)
min_gap = Decimal(self._min_gap_percent.Value)
percent = Decimal(self._stop_loss_percent.Value) / Decimal(100)
if gap >= min_gap:
self.SellMarket(self.Volume)
self._stop_price = candle.HighPrice * (Decimal(1) + percent)
elif -gap >= min_gap:
self.BuyMarket(self.Volume)
self._stop_price = candle.LowPrice * (Decimal(1) - percent)
def CreateClone(self):
return overnight_gap_strategy()