Esta estrategia reproduce el comportamiento central del asesor experto original NewsReleaseEA, preparando un bracket de órdenes pendientes alrededor de una noticia programada y gestionando activamente la posición resultante.
Ideas clave
Cinco entradas (hora de noticia, ventanas antes/después, distancias de órdenes y separación) definen cuándo y dónde se colocan las órdenes stop.
Un conjunto simétrico de órdenes buy stop y sell stop se envía poco antes de la hora configurada. El primer par se coloca a DistancePips del ask/bid actual y los pares adicionales se desplazan por StepPips.
Las órdenes pendientes permanecen activas hasta PostNewsMinutes minutos después del evento. Al final de la ventana, la estrategia cancela todas las órdenes activas y, si se solicita, cierra cualquier posición abierta.
Cuando una orden se ejecuta, las órdenes pendientes opuestas se cancelan automáticamente y la posición abierta se gestiona mediante reglas de stop-loss, take-profit, break-even y trailing expresadas en pips.
La protección break-even se arma después de que el precio se mueve BreakEvenTriggerPips a favor de la posición y fuerza una salida si el precio vuelve al precio de entrada más BreakEvenOffsetPips (largos) o menos ese offset (cortos).
La gestión trailing sigue el mejor precio alcanzado tras la entrada. Cuando la distancia entre el precio actual y el extremo supera TrailingPips, la posición se cierra para proteger el beneficio acumulado.
La bandera TradeOnce replica el comportamiento "trade one time per news" del programa MQL impidiendo una segunda activación tras completarse la primera operación.
Parámetros
NewsTime: hora programada de la noticia.
PreNewsMinutes: cuántos minutos antes de la noticia se colocan órdenes pendientes.
PostNewsMinutes: cuántos minutos después de la noticia se mantienen vivas las órdenes pendientes antes de cancelarlas.
OrderPairs: número de pares buy stop/sell stop que forman el bracket.
DistancePips: distancia en pips del primer par respecto al mejor ask/bid en el momento de colocación.
StepPips: separación adicional en pips entre pares consecutivos.
OrderVolume: volumen enviado con cada orden pendiente.
TradeOnce: si está activado, la estrategia solo puede operar una vez por ventana de evento.
UseStopLoss / StopLossPips: activa y configura la distancia de stop-loss en pips.
UseTakeProfit / TakeProfitPips: activa y configura la distancia de take-profit en pips.
UseBreakEven, BreakEvenTriggerPips, BreakEvenOffsetPips: configura el módulo break-even.
UseTrailing / TrailingPips: activa la lógica de salida trailing y define la distancia en pips.
CloseAfterEvent: cierra cualquier posición abierta al finalizar la ventana posterior a la noticia.
Notas
La estrategia trabaja exclusivamente con datos level1 (SubscribeLevel1) para reaccionar a los últimos precios bid/ask sin esperar velas.
Las distancias expresadas en pips se convierten a precios absolutos usando PriceStep del instrumento. Si PriceStep no está disponible, se usa 1 como fallback seguro.
Las condiciones de stop-loss, take-profit, break-even y trailing cierran la posición a mercado llamando a ClosePosition(). Esto replica la gestión reactiva del experto original manteniendo compacta la implementación.
No se proporciona versión Python, tal como se solicitó.
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// News Release strategy: Volatility breakout using Highest/Lowest channel.
/// Buys when close >= highest. Sells when close <= lowest.
/// </summary>
public class NewsReleaseStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _channelPeriod;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int ChannelPeriod
{
get => _channelPeriod.Value;
set => _channelPeriod.Value = value;
}
public NewsReleaseStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_channelPeriod = Param(nameof(ChannelPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("Channel Period", "Highest/Lowest lookback", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var high = new Highest { Length = ChannelPeriod };
var low = new Lowest { Length = ChannelPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(high, low, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal high, decimal low)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (candle.ClosePrice >= high && Position <= 0)
{
BuyMarket();
}
else if (candle.ClosePrice <= low && Position >= 0)
{
SellMarket();
}
}
}
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 Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class news_release_strategy(Strategy):
def __init__(self):
super(news_release_strategy, self).__init__()
self._channel_period = self.Param("ChannelPeriod", 10) \
.SetDisplay("Channel Period", "Highest/Lowest lookback", "Indicators")
self._highest = None
self._lowest = None
@property
def channel_period(self):
return self._channel_period.Value
def OnReseted(self):
super(news_release_strategy, self).OnReseted()
self._highest = None
self._lowest = None
def OnStarted2(self, time):
super(news_release_strategy, self).OnStarted2(time)
self._highest = Highest()
self._highest.Length = self.channel_period
self._lowest = Lowest()
self._lowest.Length = self.channel_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._highest, self._lowest, self._process_candle)
subscription.Start()
def _process_candle(self, candle, high_val, low_val):
if candle.State != CandleStates.Finished:
return
if not self._highest.IsFormed or not self._lowest.IsFormed:
return
close = float(candle.ClosePrice)
h = float(high_val)
l = float(low_val)
if close >= h and self.Position <= 0:
self.BuyMarket()
elif close <= l and self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return news_release_strategy()