Esta estratégia reproduz o comportamento do script NewsTrader.mq4 original, armando ambos os lados do mercado pouco antes de um lançamento macroeconômico programado. Dez minutos antes do carimbo de data/hora da notícia configurado, o bot envia um par de ordens de stop breakout e anexa imediatamente saídas de proteção quando um lado é acionado.
Lógica principal
Usa uma assinatura de vela de 1 minuto (configurável) apenas como fonte de tempo.
Calcula o momento de ativação como news time - LeadMinutes e espera até a primeira vela concluída cujo tempo de abertura esteja nesse ponto ou além dele.
Coloca um stop de venda abaixo do preço atual e um stop de compra acima dele, compensado por BiasPips convertido por meio de Security.PriceStep (espelha a lógica bias * Point em MQL4).
Assim que uma ordem pendente for preenchida, a ordem pendente oposta será cancelada; ordens dedicadas de stop-loss e take-profit são colocadas usando as distâncias de pip configuradas.
Os preenchimentos de stop-loss ou take-profit cancelam a ordem de proteção restante e nivelam a estratégia.
Chama StartProtection() no início para que a estratégia coopere com proteções StockSharp de nível superior.
Parâmetros
Nome
Descrição
Padrão
TradeVolume
Contratos enviados com cada pedido pendente.
1
StopLossPips
Distância de stop-loss em pips (0 desativa a ordem de stop).
10
TakeProfitPips
Distância de lucro em pips (0 desativa a ordem alvo).
10
BiasPips
Distância do preço de referência às ordens de breakout stop.
20
LeadMinutes
Minutos antes do carimbo de data/hora da notícia, quando as ordens de fuga são armadas.
Componentes do horário programado das notícias (relógio da plataforma).
2010, 3, 8, 1, 30
CandleType
Tipo de dados Candle usado para rastrear a progressão do tempo.
1 Minute
Notas de implementação
A estratégia define Volume como TradeVolume durante OnStarted, garantindo que métodos auxiliares como BuyStop e SellStop usem o tamanho esperado.
Security.PriceStep deve ser definido; caso contrário, a lógica lança uma exceção porque as distâncias baseadas em pip não podem ser traduzidas em preços.
Os preços de fechamento das velas são usados como um proxy para o lance/venda mais recente ao calcular os níveis de stop – correspondendo à lógica MQL4 original que dependia da cotação mais recente no momento do acionamento.
Os pedidos pendentes são feitos apenas uma vez; o algoritmo não se rearma após a passagem do evento de notícias configurado.
As ordens de proteção são ignoradas quando a respectiva distância do pip é zero, o que mantém o comportamento configurável para intervenção manual.
Arquivos
CS/NewsTraderStrategy.cs — Implementação da estratégia em C#.
A versão Python foi omitida intencionalmente conforme solicitado.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// News Trader: RSI breakout with EMA filter and ATR stops.
/// </summary>
public class NewsTraderStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevRsi;
private decimal _entryPrice;
public NewsTraderStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_rsiLength = Param(nameof(RsiLength), 14)
.SetDisplay("RSI Length", "RSI period.", "Indicators");
_emaLength = Param(nameof(EmaLength), 30)
.SetDisplay("EMA Length", "Trend filter.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
public int EmaLength { get => _emaLength.Value; set => _emaLength.Value = value; }
public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRsi = 0; _entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevRsi = 0; _entryPrice = 0;
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var ema = new ExponentialMovingAverage { Length = EmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(rsi, ema, atr, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, ema); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal rsiVal, decimal emaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished) return;
if (_prevRsi == 0 || atrVal <= 0) { _prevRsi = rsiVal; return; }
var close = candle.ClosePrice;
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 2.5m || close <= _entryPrice - atrVal * 1.5m || rsiVal > 70) { SellMarket(); _entryPrice = 0; }
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 2.5m || close >= _entryPrice + atrVal * 1.5m || rsiVal < 30) { BuyMarket(); _entryPrice = 0; }
}
if (Position == 0)
{
if (rsiVal > 55 && _prevRsi <= 55 && close > emaVal) { _entryPrice = close; BuyMarket(); }
else if (rsiVal < 45 && _prevRsi >= 45 && close < emaVal) { _entryPrice = close; SellMarket(); }
}
_prevRsi = rsiVal;
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import RelativeStrengthIndex, ExponentialMovingAverage, AverageTrueRange
class news_trader_strategy(Strategy):
def __init__(self):
super(news_trader_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(8))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period.", "Indicators")
self._ema_length = self.Param("EmaLength", 30) \
.SetDisplay("EMA Length", "Trend filter.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._prev_rsi = 0.0
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def RsiLength(self):
return self._rsi_length.Value
@property
def EmaLength(self):
return self._ema_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(news_trader_strategy, self).OnStarted2(time)
self._prev_rsi = 0.0
self._entry_price = 0.0
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.RsiLength
self._ema = ExponentialMovingAverage()
self._ema.Length = self.EmaLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._rsi, self._ema, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, rsi_val, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
rv = float(rsi_val)
ev = float(ema_val)
av = float(atr_val)
if self._prev_rsi == 0 or av <= 0:
self._prev_rsi = rv
return
close = float(candle.ClosePrice)
if self.Position > 0:
if close >= self._entry_price + av * 2.5 or close <= self._entry_price - av * 1.5 or rv > 70:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if close <= self._entry_price - av * 2.5 or close >= self._entry_price + av * 1.5 or rv < 30:
self.BuyMarket()
self._entry_price = 0.0
if self.Position == 0:
if rv > 55 and self._prev_rsi <= 55 and close > ev:
self._entry_price = close
self.BuyMarket()
elif rv < 45 and self._prev_rsi >= 45 and close < ev:
self._entry_price = close
self.SellMarket()
self._prev_rsi = rv
def OnReseted(self):
super(news_trader_strategy, self).OnReseted()
self._prev_rsi = 0.0
self._entry_price = 0.0
def CreateClone(self):
return news_trader_strategy()