Ver no GitHub

Estratégia do Trader de Notícias

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. 10
NewsYear, NewsMonth, NewsDay, NewsHour, NewsMinute 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;
	}
}