Ver no GitHub

Estratégia de Ordens Pendentes em Notícias

Esta estratégia coloca um par de ordens stop pendentes ao redor do preço atual e as gerencia conforme o mercado evolui. É destinada à negociação durante divulgações de notícias onde movimentos bruscos são esperados.

Como funciona

  • Quando sem posição, a estratégia coloca:
    • Uma ordem de buy stop em Ask + Step.
    • Uma ordem de sell stop em Bid - Step.
  • Ordens pendentes são reprecificadas a cada TimeModify segundos se o mercado se mover pelo menos StepTrail.
  • Quando uma ordem é executada, a ordem pendente oposta é cancelada.
  • Um stop loss protetor e um take profit opcional são criados com base no preço de entrada.
  • O stop loss pode ser movido para o ponto de equilíbrio após um lucro definido e depois seguir o preço à medida que avança.

A estratégia opera com dados de Nível1 e não depende de nenhum indicador.

Parâmetros

Parâmetro Padrão Descrição
Step 10 Distância em ticks para colocar as ordens stop pendentes.
StopLoss 10 Stop loss inicial em ticks.
TakeProfit 50 Take profit em ticks (0 desativa).
TrailingStop 10 Distância do trailing stop em ticks.
TrailingStart 0 Lucro em ticks antes de ativar o trailing.
StepTrail 2 Mudança mínima no preço do stop (em ticks) para enviar uma nova ordem stop.
BreakEven false Mover o stop para a entrada ao atingir MinProfitBreakEven.
MinProfitBreakEven 0 Lucro em ticks necessário para mover o stop ao ponto de equilíbrio.
TimeModify 30 Segundos entre tentativas de reprecificação de ordens pendentes.

Notas

  • Ordens são gerenciadas usando a API de alto nível do StockSharp.
  • A estratégia cancela ordens protetoras quando a posição é fechada.
  • Apenas a versão em C# é fornecida; nenhuma implementação em Python está incluída.
using System;
using System.Collections.Generic;

using Ecng.Common;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// News-style volatility breakout strategy.
/// Enters on ATR expansion with momentum confirmation via EMA.
/// </summary>
public class NewsPendingOrdersStrategy : Strategy
{
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<decimal> _atrMult;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevAtr;
	private decimal _entryPrice;

	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
	public decimal AtrMult { get => _atrMult.Value; set => _atrMult.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public NewsPendingOrdersStrategy()
	{
		_emaPeriod = Param(nameof(EmaPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("EMA Period", "EMA trend period", "Indicators");
		_atrPeriod = Param(nameof(AtrPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("ATR Period", "ATR period", "Indicators");
		_atrMult = Param(nameof(AtrMult), 1.5m)
			.SetDisplay("ATR Mult", "ATR expansion multiplier", "Indicators");
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles", "General");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
		=> [(Security, CandleType)];

	protected override void OnReseted()
	{
		base.OnReseted();
		_prevAtr = 0;
		_entryPrice = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var atr = new StandardDeviation { Length = AtrPeriod };

		SubscribeCandles(CandleType).Bind(ema, atr, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal ema, decimal atr)
	{
		if (candle.State != CandleStates.Finished) return;

		if (_prevAtr <= 0) { _prevAtr = atr; return; }

		var close = candle.ClosePrice;
		var bodySize = Math.Abs(candle.ClosePrice - candle.OpenPrice);

		// Volatility expansion: big body candle relative to stddev
		var expansion = bodySize > atr * 0.5m;

		if (expansion && close > ema && Position <= 0)
		{
			if (Position < 0) BuyMarket();
			BuyMarket();
			_entryPrice = close;
		}
		else if (expansion && close < ema && Position >= 0)
		{
			if (Position > 0) SellMarket();
			SellMarket();
			_entryPrice = close;
		}
		// Exit long
		else if (Position > 0)
		{
			if (close < ema || (_entryPrice > 0 && close <= _entryPrice - atr * 2))
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		// Exit short
		else if (Position < 0)
		{
			if (close > ema || (_entryPrice > 0 && close >= _entryPrice + atr * 2))
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		_prevAtr = atr;
	}
}