Ver no GitHub

Estratégia Expert NEWS

Visão geral

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

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. 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;
	}
}