A Estratégia Expert NEWS é uma conversão direta do robô MQL5 "Expert_NEWS". A estratégia coloca continuamente ordens stop simétricas acima e abaixo do preço de mercado atual e gerencia as posições resultantes com proteção de break-even, trailing stops e atualizações programadas de ordens pendentes. A implementação depende de cotações Level1 e mantém o volume de negociação padrão em 0.1 lotes.
Lógica de negociação
Assinatura de cotações – a estratégia escuta atualizações de melhor bid/ask e calcula preços de ordens a partir dos valores mais recentes.
Ordens stop iniciais – quando não existe posição comprada ou buy stop ativo, um novo buy stop é colocado em ask + EntryOffsetTicks * PriceStep. Quando não existe posição vendida ou sell stop ativo, um sell stop é colocado em bid - EntryOffsetTicks * PriceStep.
Atualização de ordens – a cada OrderRefreshSeconds, a estratégia cancela e recria um stop pendente se o preço requerido desviar mais de TrailingStepTicks ticks.
Proteção de posição – após uma execução, a estratégia abre ordens stop de proteção e take-profit se as distâncias solicitadas atenderem à restrição MinimumStopTicks.
Controle de break-even – quando UseBreakEven está habilitado, o stop é puxado para entrada ± BreakEvenProfitTicks assim que o mercado se move o suficiente e o novo stop respeita a distância mínima da cotação atual.
Trailing stop – uma vez que o preço avança TrailingStartTicks, o stop segue usando TrailingStopTicks como distância e TrailingStepTicks como passo mínimo de melhoria.
Limpeza – fechar a posição cancela todas as ordens de proteção restantes.
Parâmetros
Parâmetro
Descrição
StopLossTicks
Distância inicial do stop de proteção (ticks). Defina como zero para desativar a ordem stop inicial.
TakeProfitTicks
Distância inicial do take-profit (ticks). Defina como zero para desativar a ordem alvo.
TrailingStopTicks
Distância do trailing stop (ticks).
TrailingStartTicks
Lucro em ticks necessário antes de a lógica de trailing ser ativada.
TrailingStepTicks
Melhoria mínima ao atualizar o trailing stop ou as ordens de entrada pendentes.
UseBreakEven
Ativa o deslocamento do stop para break-even assim que houver lucro suficiente.
BreakEvenProfitTicks
Margem de lucro adicional ao mover o stop para break-even.
EntryOffsetTicks
Distância entre a cotação atual e cada nova ordem stop de entrada.
OrderRefreshSeconds
Intervalo de tempo entre tentativas automáticas de atualização de ordens stop pendentes.
MinimumStopTicks
Fallback manual para o requisito de nível de stop do corretor. Stops mais próximos que esta distância não são submetidos.
Gestão de posição
As ordens de proteção sempre correspondem ao volume da posição líquida. Execuções parciais redimensionam automaticamente as ordens stop e take-profit.
A lógica de break-even e trailing funciona mesmo quando o stop inicial está desativado; o stop será criado dinamicamente assim que as regras forem satisfeitas.
A estratégia mantém o preço do stop mais recente em memória para que as atualizações de trailing preservem um comportamento monótono.
Notas de uso
Certifique-se de que Security.PriceStep esteja configurado; cada parâmetro de distância em ticks é multiplicado por este valor.
O volume padrão é 0.1 para espelhar o robô original. Ajuste a propriedade Volume se outro tamanho for necessário.
MinimumStopTicks deve ser definido conforme o requisito de nível de stop da plataforma de negociação, se esta exigir um. Deixe em zero para permitir os stops mais ajustados possíveis.
O algoritmo não depende de barras históricas e pode operar apenas com cotações em tempo 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()