La Estrategia E-News Lucky es un puerto de StockSharp del asesor experto de MetaTrader e-News-Lucky. El sistema automatiza el clásico enfoque de ruptura por noticias:
En un PlacementTime configurable, envía órdenes tanto de compra stop como de venta stop alrededor del precio actual, desplazadas por DistancePips.
Cuando se ejecuta cualquier orden pendiente, la orden opuesta se cancela inmediatamente. Los niveles iniciales de protección de stop-loss y take-profit se adjuntan según los desplazamientos en pips configurados.
Se puede habilitar un trailing stop mediante TrailingStopPips y TrailingStepPips para asegurar ganancias a medida que la operación se mueve en la dirección favorable.
En el CancelTime configurado, todas las órdenes pendientes restantes se eliminan y cualquier posición abierta se cierra para evitar mantener riesgo fuera de la ventana de trading.
La estrategia usa datos de velas (CandleType, 1 minuto por defecto) solo para rastrear los tiempos programados y actualizar el trailing stop. No depende de cálculos de indicadores.
Parámetros
Nombre
Descripción
Volume
Volumen de orden para cada entrada pendiente. La estrategia envía órdenes simétricas de compra stop y venta stop con este volumen.
StopLossPips
Distancia entre el precio de entrada y el stop-loss de protección, expresada en pips. Establecer en cero para deshabilitar el stop.
TakeProfitPips
Distancia entre el precio de entrada y el objetivo de beneficio en pips. Establecer en cero para deshabilitar el objetivo.
TrailingStopPips
Distancia del trailing stop en pips. El motor de trailing se activa solo cuando este valor es mayor que cero.
TrailingStepPips
Ganancia mínima en pips requerida antes de que el trailing stop se mueva nuevamente. Previene actualizaciones excesivas del stop en mercados laterales.
DistancePips
Desplazamiento (en pips) del precio actual usado para colocar las órdenes stop.
PlacementTime
Hora del día (tiempo del broker/servidor) en que se colocan las órdenes pendientes. Por defecto: 10:30.
CancelTime
Hora del día en que se cancelan las órdenes pendientes y se cierran las posiciones abiertas. Por defecto: 22:30.
CandleType
Serie de velas usada para programación y trailing. Por defecto: marco temporal de 1 minuto.
Notas de implementación
El tamaño de pip sigue la lógica de MetaTrader: si el símbolo tiene 3 o 5 dígitos, la estrategia multiplica el paso de precio por 10 para trabajar en unidades de pip.
Todos los precios se normalizan al paso de precio del instrumento antes de enviar órdenes.
Los trailing stops comparan el último cierre contra PositionPrice y solo mueven el stop de protección cuando la ganancia supera tanto TrailingStopPips como TrailingStepPips.
Las órdenes pendientes se recrean cada día de trading cuando se alcanza el tiempo de colocación. Las verificaciones del tiempo de cancelación garantizan que toda la exposición esté plana al final de la ventana.
Consejos de uso
Adjunte la estrategia a un instrumento líquido con spreads ajustados; las distancias de ruptura asumen un comportamiento de precio similar al de las noticias.
Establezca PlacementTime y CancelTime de acuerdo con el calendario económico de interés.
Ajuste las distancias en pips para coincidir con la volatilidad del instrumento. Los valores más grandes reducen la posibilidad de falsos disparadores, mientras que los valores más pequeños pueden capturar movimientos más tempranos pero aumentan el riesgo de whipsaw.
Deshabilite el trailing manteniendo TrailingStopPips en cero si se prefieren stops fijos.
Monitoree el deslizamiento y el spread durante noticias de alto impacto para asegurar que las órdenes pendientes se ejecuten como se espera.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Scheduled breakout strategy that monitors price around a reference level and enters on breakout.
/// Converted from the original pending-order version to use market orders.
/// </summary>
public class ENewsLuckyStrategy : Strategy
{
private readonly StrategyParam<decimal> _stopLossPips;
private readonly StrategyParam<decimal> _takeProfitPips;
private readonly StrategyParam<decimal> _trailingStopPips;
private readonly StrategyParam<decimal> _trailingStepPips;
private readonly StrategyParam<decimal> _distancePips;
private readonly StrategyParam<int> _placementHour;
private readonly StrategyParam<int> _cancelHour;
private readonly StrategyParam<DataType> _candleType;
private decimal _pipSize;
private decimal? _buyLevel;
private decimal? _sellLevel;
private decimal _entryPrice;
private decimal? _stopPrice;
private decimal? _takePrice;
private bool _pendingActive;
private bool _lastWasPlacementDay;
public decimal StopLossPips
{
get => _stopLossPips.Value;
set => _stopLossPips.Value = value;
}
public decimal TakeProfitPips
{
get => _takeProfitPips.Value;
set => _takeProfitPips.Value = value;
}
public decimal TrailingStopPips
{
get => _trailingStopPips.Value;
set => _trailingStopPips.Value = value;
}
public decimal TrailingStepPips
{
get => _trailingStepPips.Value;
set => _trailingStepPips.Value = value;
}
public decimal DistancePips
{
get => _distancePips.Value;
set => _distancePips.Value = value;
}
public int PlacementHour
{
get => _placementHour.Value;
set => _placementHour.Value = value;
}
public int CancelHour
{
get => _cancelHour.Value;
set => _cancelHour.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public ENewsLuckyStrategy()
{
_stopLossPips = Param(nameof(StopLossPips), 50m)
.SetDisplay("Stop Loss", "Stop loss in pips", "Trading");
_takeProfitPips = Param(nameof(TakeProfitPips), 150m)
.SetDisplay("Take Profit", "Take profit in pips", "Trading");
_trailingStopPips = Param(nameof(TrailingStopPips), 5m)
.SetDisplay("Trailing Stop", "Trailing distance in pips", "Risk");
_trailingStepPips = Param(nameof(TrailingStepPips), 5m)
.SetDisplay("Trailing Step", "Minimum trailing step in pips", "Risk");
_distancePips = Param(nameof(DistancePips), 20m)
.SetGreaterThanZero()
.SetDisplay("Entry Distance", "Distance from market in pips", "Trading");
_placementHour = Param(nameof(PlacementHour), 2)
.SetDisplay("Placement Hour", "Hour to set breakout levels", "General");
_cancelHour = Param(nameof(CancelHour), 22)
.SetDisplay("Cancel Hour", "Hour to cancel and close", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Working candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
protected override void OnReseted()
{
base.OnReseted();
_pipSize = 0m;
_buyLevel = null;
_sellLevel = null;
_entryPrice = 0m;
_stopPrice = null;
_takePrice = null;
_pendingActive = false;
_lastWasPlacementDay = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var step = Security?.PriceStep ?? 0m;
_pipSize = step > 0 ? step : 1m;
SubscribeCandles(CandleType)
.Bind(ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var hour = candle.CloseTime.Hour;
var price = candle.ClosePrice;
// Set breakout levels at placement hour
if (hour == PlacementHour && !_lastWasPlacementDay && Position == 0)
{
var distance = DistancePips * _pipSize;
_buyLevel = price + distance;
_sellLevel = price - distance;
_pendingActive = true;
_lastWasPlacementDay = true;
}
if (hour != PlacementHour)
_lastWasPlacementDay = false;
// Cancel at cancel hour
if (hour == CancelHour && _pendingActive)
{
_pendingActive = false;
_buyLevel = null;
_sellLevel = null;
if (Position > 0)
SellMarket(Position);
else if (Position < 0)
BuyMarket(-Position);
_entryPrice = 0m;
_stopPrice = null;
_takePrice = null;
return;
}
// Check breakout triggers
if (_pendingActive && Position == 0)
{
if (_buyLevel.HasValue && candle.HighPrice >= _buyLevel.Value)
{
var buyLevel = _buyLevel.Value;
BuyMarket(Volume);
_entryPrice = buyLevel;
_stopPrice = StopLossPips > 0 ? _entryPrice - StopLossPips * _pipSize : null;
_takePrice = TakeProfitPips > 0 ? _entryPrice + TakeProfitPips * _pipSize : null;
_pendingActive = false;
_buyLevel = null;
_sellLevel = null;
}
else if (_sellLevel.HasValue && candle.LowPrice <= _sellLevel.Value)
{
var sellLevel = _sellLevel.Value;
SellMarket(Volume);
_entryPrice = sellLevel;
_stopPrice = StopLossPips > 0 ? _entryPrice + StopLossPips * _pipSize : null;
_takePrice = TakeProfitPips > 0 ? _entryPrice - TakeProfitPips * _pipSize : null;
_pendingActive = false;
_buyLevel = null;
_sellLevel = null;
}
}
// Manage open position
if (Position > 0)
{
// Trailing stop
if (TrailingStopPips > 0 && _entryPrice > 0)
{
var trailDist = TrailingStopPips * _pipSize;
var stepDist = TrailingStepPips * _pipSize;
if (price - _entryPrice > trailDist + stepDist)
{
var newStop = price - trailDist;
if (!_stopPrice.HasValue || newStop > _stopPrice.Value)
_stopPrice = newStop;
}
}
if (_stopPrice.HasValue && candle.LowPrice <= _stopPrice.Value)
{
SellMarket(Position);
ResetPosition();
return;
}
if (_takePrice.HasValue && candle.HighPrice >= _takePrice.Value)
{
SellMarket(Position);
ResetPosition();
}
}
else if (Position < 0)
{
// Trailing stop
if (TrailingStopPips > 0 && _entryPrice > 0)
{
var trailDist = TrailingStopPips * _pipSize;
var stepDist = TrailingStepPips * _pipSize;
if (_entryPrice - price > trailDist + stepDist)
{
var newStop = price + trailDist;
if (!_stopPrice.HasValue || newStop < _stopPrice.Value)
_stopPrice = newStop;
}
}
if (_stopPrice.HasValue && candle.HighPrice >= _stopPrice.Value)
{
BuyMarket(-Position);
ResetPosition();
return;
}
if (_takePrice.HasValue && candle.LowPrice <= _takePrice.Value)
{
BuyMarket(-Position);
ResetPosition();
}
}
}
private void ResetPosition()
{
_entryPrice = 0m;
_stopPrice = null;
_takePrice = null;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from StockSharp.Algo.Strategies import Strategy
from StockSharp.Messages import DataType, CandleStates
from System import TimeSpan, Math
class e_news_lucky_strategy(Strategy):
def __init__(self):
super(e_news_lucky_strategy, self).__init__()
self._stop_loss_pips = self.Param("StopLossPips", 50.0)
self._take_profit_pips = self.Param("TakeProfitPips", 150.0)
self._trailing_stop_pips = self.Param("TrailingStopPips", 5.0)
self._trailing_step_pips = self.Param("TrailingStepPips", 5.0)
self._distance_pips = self.Param("DistancePips", 20.0)
self._placement_hour = self.Param("PlacementHour", 2)
self._cancel_hour = self.Param("CancelHour", 22)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._pip_size = 0.0
self._buy_level = None
self._sell_level = None
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
self._pending_active = False
self._last_was_placement_day = False
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(e_news_lucky_strategy, self).OnStarted2(time)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 0.0
self._pip_size = step if step > 0 else 1.0
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
hour = candle.CloseTime.Hour
price = float(candle.ClosePrice)
# Set breakout levels at placement hour
if hour == self._placement_hour.Value and not self._last_was_placement_day and self.Position == 0:
distance = self._distance_pips.Value * self._pip_size
self._buy_level = price + distance
self._sell_level = price - distance
self._pending_active = True
self._last_was_placement_day = True
if hour != self._placement_hour.Value:
self._last_was_placement_day = False
# Cancel at cancel hour
if hour == self._cancel_hour.Value and self._pending_active:
self._pending_active = False
self._buy_level = None
self._sell_level = None
if self.Position > 0:
self.SellMarket(self.Position)
elif self.Position < 0:
self.BuyMarket(abs(self.Position))
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
return
# Check breakout triggers
if self._pending_active and self.Position == 0:
if self._buy_level is not None and float(candle.HighPrice) >= self._buy_level:
buy_level = self._buy_level
self.BuyMarket(self.Volume)
self._entry_price = buy_level
self._stop_price = self._entry_price - self._stop_loss_pips.Value * self._pip_size if self._stop_loss_pips.Value > 0 else None
self._take_price = self._entry_price + self._take_profit_pips.Value * self._pip_size if self._take_profit_pips.Value > 0 else None
self._pending_active = False
self._buy_level = None
self._sell_level = None
elif self._sell_level is not None and float(candle.LowPrice) <= self._sell_level:
sell_level = self._sell_level
self.SellMarket(self.Volume)
self._entry_price = sell_level
self._stop_price = self._entry_price + self._stop_loss_pips.Value * self._pip_size if self._stop_loss_pips.Value > 0 else None
self._take_price = self._entry_price - self._take_profit_pips.Value * self._pip_size if self._take_profit_pips.Value > 0 else None
self._pending_active = False
self._buy_level = None
self._sell_level = None
# Manage open position
if self.Position > 0:
# Trailing stop for long
if self._trailing_stop_pips.Value > 0 and self._entry_price > 0:
trail_dist = self._trailing_stop_pips.Value * self._pip_size
step_dist = self._trailing_step_pips.Value * self._pip_size
if price - self._entry_price > trail_dist + step_dist:
new_stop = price - trail_dist
if self._stop_price is None or new_stop > self._stop_price:
self._stop_price = new_stop
if self._stop_price is not None and float(candle.LowPrice) <= self._stop_price:
self.SellMarket(self.Position)
self._reset_position()
return
if self._take_price is not None and float(candle.HighPrice) >= self._take_price:
self.SellMarket(self.Position)
self._reset_position()
elif self.Position < 0:
# Trailing stop for short
if self._trailing_stop_pips.Value > 0 and self._entry_price > 0:
trail_dist = self._trailing_stop_pips.Value * self._pip_size
step_dist = self._trailing_step_pips.Value * self._pip_size
if self._entry_price - price > trail_dist + step_dist:
new_stop = price + trail_dist
if self._stop_price is None or new_stop < self._stop_price:
self._stop_price = new_stop
if self._stop_price is not None and float(candle.HighPrice) >= self._stop_price:
self.BuyMarket(abs(self.Position))
self._reset_position()
return
if self._take_price is not None and float(candle.LowPrice) <= self._take_price:
self.BuyMarket(abs(self.Position))
self._reset_position()
def _reset_position(self):
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
def OnReseted(self):
super(e_news_lucky_strategy, self).OnReseted()
self._pip_size = 0.0
self._buy_level = None
self._sell_level = None
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
self._pending_active = False
self._last_was_placement_day = False
def CreateClone(self):
return e_news_lucky_strategy()