La Estrategia Expert NEWS es una conversión directa del robot MQL5 "Expert_NEWS". La estrategia coloca continuamente órdenes stop simétricas por encima y por debajo del precio de mercado actual, gestionando las posiciones resultantes con protección de punto de equilibrio, stops de seguimiento y actualizaciones programadas de órdenes pendientes. La implementación se basa en cotizaciones Level1 y mantiene el volumen de negociación predeterminado en 0.1 lotes.
Lógica de negociación
Suscripción a cotizaciones – la estrategia escucha las actualizaciones de mejor bid/ask y calcula los precios de las órdenes a partir de los últimos valores.
Órdenes stop iniciales – cuando no existe posición larga ni buy stop activo, se coloca un nuevo buy stop en ask + EntryOffsetTicks * PriceStep. Cuando no existe posición corta ni sell stop activo, se coloca un sell stop en bid - EntryOffsetTicks * PriceStep.
Actualización de órdenes – cada OrderRefreshSeconds, la estrategia cancela y recrea un stop pendiente si el precio requerido se desvía más de TrailingStepTicks ticks.
Protección de posición – tras una ejecución, la estrategia abre órdenes stop de protección y take-profit si las distancias solicitadas cumplen la restricción MinimumStopTicks.
Control de punto de equilibrio – cuando UseBreakEven está activado, el stop se mueve a entrada ± BreakEvenProfitTicks una vez que el mercado se desplaza suficientemente y el nuevo stop respeta la distancia mínima de la cotización actual.
Stop de seguimiento – una vez que el precio avanza TrailingStartTicks, el stop sigue usando TrailingStopTicks como distancia y TrailingStepTicks como paso de mejora mínima.
Limpieza – cerrar la posición cancela todas las órdenes de protección restantes.
Parámetros
Parámetro
Descripción
StopLossTicks
Distancia inicial del stop de protección (ticks). Establezca en cero para desactivar la orden stop inicial.
TakeProfitTicks
Distancia inicial del take-profit (ticks). Establezca en cero para desactivar la orden objetivo.
TrailingStopTicks
Distancia del stop de seguimiento (ticks).
TrailingStartTicks
Beneficio en ticks requerido antes de que se active la lógica de seguimiento.
TrailingStepTicks
Mejora mínima al actualizar el stop de seguimiento o las órdenes de entrada pendientes.
UseBreakEven
Activa el desplazamiento del stop al punto de equilibrio una vez que hay suficiente beneficio.
BreakEvenProfitTicks
Margen de beneficio adicional al mover el stop al punto de equilibrio.
EntryOffsetTicks
Distancia entre la cotización actual y cada nueva orden stop de entrada.
OrderRefreshSeconds
Intervalo de tiempo entre intentos automáticos de actualización de órdenes stop pendientes.
MinimumStopTicks
Respaldo manual para el requisito de nivel de stop del bróker. Los stops más cercanos que esta distancia no se envían.
Gestión de posición
Las órdenes de protección siempre coinciden con el volumen de la posición neta. Las ejecuciones parciales redimensionan automáticamente las órdenes stop y take-profit.
La lógica de punto de equilibrio y de seguimiento funciona incluso cuando el stop inicial está desactivado; el stop se creará dinámicamente una vez que se cumplan las reglas.
La estrategia mantiene el precio del stop más reciente en memoria para que las actualizaciones de seguimiento preserven un comportamiento monótono.
Notas de uso
Asegúrese de que Security.PriceStep esté configurado; cada parámetro de distancia en ticks se multiplica por este valor.
El volumen predeterminado es 0.1 para reflejar el robot original. Ajuste la propiedad Volume si se requiere otro tamaño.
MinimumStopTicks debe establecerse al requisito de nivel de stop de la plataforma de negociación si ésta lo exige. Déjelo en cero para permitir los stops más ajustados posibles.
El algoritmo no depende de barras históricas y puede operar solo con cotizaciones en tiempo real.
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>
/// Breakout strategy converted from the MQL Expert NEWS robot.
/// Detects breakouts above/below a reference price range and enters positions.
/// Uses stop loss and take profit for position management.
/// </summary>
public class ExpertNewsStrategy : Strategy
{
private readonly StrategyParam<decimal> _entryOffset;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<int> _lookbackPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly List<decimal> _highs = new();
private readonly List<decimal> _lows = new();
private decimal _entryPrice;
private int _lastSignal;
/// <summary>
/// Entry offset from the high/low range.
/// </summary>
public decimal EntryOffset
{
get => _entryOffset.Value;
set => _entryOffset.Value = value;
}
/// <summary>
/// Stop loss distance in price units.
/// </summary>
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Take profit distance in price units.
/// </summary>
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Number of bars to determine high/low range.
/// </summary>
public int LookbackPeriod
{
get => _lookbackPeriod.Value;
set => _lookbackPeriod.Value = value;
}
/// <summary>
/// Candle type for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public ExpertNewsStrategy()
{
_entryOffset = Param(nameof(EntryOffset), 200m)
.SetGreaterThanZero()
.SetDisplay("Entry Offset", "Offset from range high/low for entry", "Parameters");
_stopLoss = Param(nameof(StopLoss), 1000m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss", "Stop loss in price units", "Risk");
_takeProfit = Param(nameof(TakeProfit), 2000m)
.SetGreaterThanZero()
.SetDisplay("Take Profit", "Take profit in price units", "Risk");
_lookbackPeriod = Param(nameof(LookbackPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Lookback Period", "Bars for range calculation", "Parameters");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_highs.Clear();
_lows.Clear();
_entryPrice = 0m;
_lastSignal = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
SubscribeCandles(CandleType)
.Bind(ProcessCandle)
.Start();
StartProtection(
new Unit(TakeProfit, UnitTypes.Absolute),
new Unit(StopLoss, UnitTypes.Absolute));
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_highs.Add(candle.HighPrice);
_lows.Add(candle.LowPrice);
if (_highs.Count > LookbackPeriod + 1)
_highs.RemoveAt(0);
if (_lows.Count > LookbackPeriod + 1)
_lows.RemoveAt(0);
if (_highs.Count <= LookbackPeriod)
return;
// Compute range from prior bars (excluding current)
var rangeHigh = decimal.MinValue;
var rangeLow = decimal.MaxValue;
for (int i = 0; i < _highs.Count - 1; i++)
{
if (_highs[i] > rangeHigh) rangeHigh = _highs[i];
if (_lows[i] < rangeLow) rangeLow = _lows[i];
}
var close = candle.ClosePrice;
var breakoutUp = close > rangeHigh + EntryOffset;
var breakoutDown = close < rangeLow - EntryOffset;
if (breakoutUp && _lastSignal != 1 && Position <= 0)
{
BuyMarket();
_entryPrice = close;
_lastSignal = 1;
}
else if (breakoutDown && _lastSignal != -1 && Position >= 0)
{
SellMarket();
_entryPrice = close;
_lastSignal = -1;
}
else if (!breakoutUp && !breakoutDown)
_lastSignal = 0;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
class expert_news_strategy(Strategy):
def __init__(self):
super(expert_news_strategy, self).__init__()
self._entry_offset = self.Param("EntryOffset", 200.0)
self._stop_loss = self.Param("StopLoss", 1000.0)
self._take_profit = self.Param("TakeProfit", 2000.0)
self._lookback_period = self.Param("LookbackPeriod", 20)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._highs = []
self._lows = []
self._entry_price = 0.0
self._last_signal = 0
@property
def EntryOffset(self):
return self._entry_offset.Value
@EntryOffset.setter
def EntryOffset(self, value):
self._entry_offset.Value = value
@property
def StopLoss(self):
return self._stop_loss.Value
@StopLoss.setter
def StopLoss(self, value):
self._stop_loss.Value = value
@property
def TakeProfit(self):
return self._take_profit.Value
@TakeProfit.setter
def TakeProfit(self, value):
self._take_profit.Value = value
@property
def LookbackPeriod(self):
return self._lookback_period.Value
@LookbackPeriod.setter
def LookbackPeriod(self, value):
self._lookback_period.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(expert_news_strategy, self).OnStarted2(time)
self._highs = []
self._lows = []
self._entry_price = 0.0
self._last_signal = 0
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
self.StartProtection(
Unit(self.TakeProfit, UnitTypes.Absolute),
Unit(self.StopLoss, UnitTypes.Absolute))
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
h = float(candle.HighPrice)
l = float(candle.LowPrice)
close = float(candle.ClosePrice)
self._highs.append(h)
self._lows.append(l)
period = int(self.LookbackPeriod)
if len(self._highs) > period + 1:
self._highs.pop(0)
if len(self._lows) > period + 1:
self._lows.pop(0)
if len(self._highs) <= period:
return
range_high = -1e18
range_low = 1e18
for i in range(len(self._highs) - 1):
if self._highs[i] > range_high:
range_high = self._highs[i]
if self._lows[i] < range_low:
range_low = self._lows[i]
offset = float(self.EntryOffset)
breakout_up = close > range_high + offset
breakout_down = close < range_low - offset
if breakout_up and self._last_signal != 1 and self.Position <= 0:
self.BuyMarket()
self._entry_price = close
self._last_signal = 1
elif breakout_down and self._last_signal != -1 and self.Position >= 0:
self.SellMarket()
self._entry_price = close
self._last_signal = -1
elif not breakout_up and not breakout_down:
self._last_signal = 0
def OnReseted(self):
super(expert_news_strategy, self).OnReseted()
self._highs = []
self._lows = []
self._entry_price = 0.0
self._last_signal = 0
def CreateClone(self):
return expert_news_strategy()