Ver no GitHub

Estratégia do Sistema Indicador Vortex

Visão geral

  • Fonte: Convertido do consultor especializado MetaTrader 5 "Vortex Indicator System" (MQL ID 19137).
  • Conceito: Usa o indicador Vortex para detectar cruzamentos de alta ou baixa e então arma gatilhos de rompimento no máximo/mínimo da vela do cruzamento.
  • Estilo de execução: Seguimento de rompimento; as operações são iniciadas somente após o preço confirmar o cruzamento ao superar o nível do gatilho.
  • Regime de mercado: Funciona em qualquer instrumento e período que suporte o indicador Vortex e dados de velas; nenhuma característica específica do corretor é necessária.
  • Tipos de ordens: Ordens a mercado via BuyMarket e SellMarket. A estratégia fecha automaticamente posições opostas antes de enfileirar um novo gatilho.

Lógica de trading

  1. Inscrever-se no tipo de vela configurado e calcular o indicador Vortex com o comprimento especificado.
  2. Detectar um cruzamento de alta quando VI+ se move acima de VI- depois de estar abaixo na vela anterior:
    • Fechar qualquer posição vendida existente usando ClosePosition().
    • Armazenar o máximo da vela do cruzamento como o preço de gatilho comprado.
    • Cancelar qualquer gatilho vendido pendente.
  3. Detectar um cruzamento de baixa quando VI- se move acima de VI+ depois de estar abaixo na vela anterior:
    • Fechar qualquer posição comprada existente.
    • Armazenar o mínimo da vela do cruzamento como o preço de gatilho vendido.
    • Cancelar qualquer gatilho comprado pendente.
  4. Enquanto um gatilho está ativo, monitorar as velas subsequentes:
    • Se o preço máximo romper o gatilho comprado armazenado e a posição atual for flat ou vendida, enviar uma compra a mercado dimensionada para reverter qualquer exposição vendida.
    • Se o preço mínimo romper o gatilho vendido armazenado e a posição atual for flat ou comprada, enviar uma venda a mercado dimensionada para reverter qualquer exposição comprada.
  5. Cada operação executada limpa o gatilho correspondente. Gatilhos opostos são mutuamente exclusivos.

Parâmetros

Parâmetro Padrão Descrição
Length 14 Período do indicador Vortex. Corresponde à entrada MQL original VI_Length.
CandleType Período de 60 minutos Tipo de vela usado para o cálculo do indicador e a avaliação do gatilho. Pode ser ajustado para qualquer período suportado pela fonte de dados conectada.
Volume Retirado da propriedade base Strategy Volume de operação usado para ordens a mercado. Configure antes de iniciar a estratégia se for necessário um valor diferente de 1 contrato/lote.

Como os parâmetros afetam o comportamento

  • Aumentar Length suaviza as linhas Vortex, reduzindo o número de cruzamentos, mas melhorando sua confiabilidade.
  • Diminuir Length torna o sistema mais reativo, gerando mais gatilhos e operações potenciais.
  • O CandleType deve ser alinhado com a granularidade de dados na configuração MQL original (tipicamente o período do gráfico). Velas mais curtas fornecem sinais mais rápidos, enquanto velas mais longas se concentram em tendências mais amplas.

Notas de gestão de risco

  • O consultor especializado original não define níveis de stop-loss ou take-profit. Esta conversão mantém esse comportamento; a gestão de risco deve ser tratada externamente ou estendendo a estratégia.
  • A reversão de posição é imediata: quando ocorre um sinal oposto, a estratégia emite ClosePosition() e aguarda um rompimento além do gatilho antes de entrar na nova direção.
  • Apenas um gatilho (comprado ou vendido) pode estar ativo de cada vez. Os gatilhos são limpos se o preço os romper ou quando ocorre um cruzamento oposto.

Instruções de uso

  1. Adicione a estratégia ao seu projeto StockSharp e certifique-se de que o pacote StockSharp.Algo.Indicators esteja disponível.
  2. Configure o instrumento desejado e o conector na aplicação hospedeira.
  3. Defina o parâmetro CandleType para o período que deseja operar. Deve corresponder a uma assinatura de velas disponível para o instrumento selecionado.
  4. Opcionalmente ajuste Length e Volume antes de iniciar a estratégia ou através de otimização.
  5. Inicie a estratégia. As ordens serão geradas assim que o indicador estiver formado e os dados em tempo real estiverem disponíveis.

Destaques de implementação

  • Usa a API de alto nível SubscribeCandles com vinculação de indicador (Bind) para processamento limpo baseado em eventos.
  • Armazena os valores anteriores do Vortex para detectar cruzamentos exatamente como a implementação MQL faz (comparações de VI+ e VI- entre duas velas consecutivas).
  • Os gatilhos de entrada são implementados como campos decimal anuláveis para imitar o mecanismo original de "armar e romper".
  • Comentários em inglês na linha do arquivo C# descrevem cada passo de decisão e ajudam a manter o código.

Possíveis extensões

  • Adicionar regras de stop-loss e take-profit (p. ex., saídas baseadas em ATR) se um controle de risco mais rígido for necessário.
  • Introduzir um período de resfriamento ou tempo máximo de manutenção para evitar períodos planos prolongados quando os gatilhos não executam.
  • Combinar com um filtro de volatilidade para operar apenas quando os intervalos de preço forem suficientemente amplos para justificar tentativas de rompimento.
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 based on the Vortex indicator crossover system.
/// Replicates the logic of the original MQL expert by arming entry triggers
/// on the candle where VI+ and VI- lines cross and executing when price breaks the trigger.
/// </summary>
public class VortexIndicatorSystemStrategy : Strategy
{
	private readonly StrategyParam<int> _length;
	private readonly StrategyParam<DataType> _candleType;

	private VortexIndicator _vortex = null!;
	private decimal _previousPlus;
	private decimal _previousMinus;
	private bool _hasPrevious;
	private decimal? _pendingBuyTrigger;
	private decimal? _pendingSellTrigger;

	/// <summary>
	/// Length of the Vortex indicator.
	/// </summary>
	public int Length
	{
		get => _length.Value;
		set => _length.Value = value;
	}

	/// <summary>
	/// Candle type used for indicator calculations.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes parameters for the strategy.
	/// </summary>
	public VortexIndicatorSystemStrategy()
	{
		_length = Param(nameof(Length), 14)
			.SetDisplay("Vortex Length", "Period for the Vortex indicator", "General")
			.SetGreaterThanZero()
			
			.SetOptimize(7, 28, 7);

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe used for analysis", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_previousPlus = 0m;
		_previousMinus = 0m;
		_hasPrevious = false;
		_pendingBuyTrigger = null;
		_pendingSellTrigger = null;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_vortex = new VortexIndicator
		{
			Length = Length
		};

		var subscription = SubscribeCandles(CandleType);

		subscription
			.BindEx(_vortex, ProcessCandle)
			.Start();
	}

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue vortexValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!_vortex.IsFormed)
			return;

		if (vortexValue is not VortexIndicatorValue typed)
			return;

		var viPlusN = typed.PlusVi;
		var viMinusN = typed.MinusVi;
		if (viPlusN is not decimal viPlus || viMinusN is not decimal viMinus)
			return;

		if (_pendingBuyTrigger is decimal buyTrigger && candle.HighPrice > buyTrigger)
		{
			if (Position <= 0)
			{
				// Reverse existing short if present and open a new long position when price breaks the trigger.
				BuyMarket();
			}

			_pendingBuyTrigger = null;
		}
		else if (_pendingSellTrigger is decimal sellTrigger && candle.LowPrice < sellTrigger)
		{
			if (Position >= 0)
			{
				// Reverse existing long if present and open a new short position when price breaks the trigger.
				SellMarket();
			}

			_pendingSellTrigger = null;
		}

		if (!_hasPrevious)
		{
			_previousPlus = viPlus;
			_previousMinus = viMinus;
			_hasPrevious = true;
			return;
		}

		var crossedUp = _previousPlus <= _previousMinus && viPlus > viMinus;
		var crossedDown = _previousPlus >= _previousMinus && viPlus < viMinus;

		if (crossedUp)
		{
			if (Position < 0)
			{
				// Flatten existing short positions when a bullish crossover appears.
				BuyMarket();
			}

			_pendingBuyTrigger = candle.HighPrice;
			_pendingSellTrigger = null;
		}
		else if (crossedDown)
		{
			if (Position > 0)
			{
				// Flatten existing long positions when a bearish crossover appears.
				SellMarket();
			}

			_pendingSellTrigger = candle.LowPrice;
			_pendingBuyTrigger = null;
		}

		_previousPlus = viPlus;
		_previousMinus = viMinus;
	}
}