Ver no GitHub

Estratégia Vector

Visão geral

A Estratégia Vector é um sistema de seguidor de tendência multi-moeda convertido do expert "Vector" do MetaTrader 5. Opera quatro principais pares forex — EURUSD, GBPUSD, USDCHF e USDJPY — simultaneamente. A estratégia calcula médias móveis suavizadas sobre o preço mediano de cada par e abre posições sincronizadas quando a tendência combinada aponta na mesma direção. Um alvo de pips dinâmico baseado na volatilidade de quatro horas e limiares de lucro e perda em nível de portfólio controlam as saídas.

Ideias centrais

  • Usar médias móveis suavizadas (SMMA) construídas sobre preços medianos para medir a direção em cada par de moedas.
  • Resumir as médias rápidas e lentas de todos os instrumentos para determinar um viés altista ou baixista comum.
  • Inserir uma única ordem a mercado por par quando o viés global e o cruzamento rápido/lento local coincidem.
  • Gerenciar posições com um alvo de pips flutuante derivado do alcance médio de 50 candles de 4 horas concluídos no EURUSD.
  • Fechar todas as operações simultaneamente se o lucro ou perda flutuante atingir a porcentagem configurada do saldo inicial.

Parâmetros

Parâmetro Descrição
Fast MA Comprimento da média móvel suavizada usada para a tendência rápida em cada par.
Slow MA Comprimento da média móvel suavizada usada para a tendência lenta em cada par.
MA Shift Número adicional de candles finalizados exigidos antes de avaliar os sinais, espelhando a configuração de deslocamento no EA original.
Equity Take Profit % Porcentagem de lucro flutuante que aciona o fechamento de todas as posições abertas.
Equity Stop Loss % Porcentagem de perda flutuante que aciona uma saída de emergência para todas as operações.
Signal Timeframe Período de candles usado para as médias móveis suavizadas (padrão 15 minutos).
Range Timeframe Período de candles usado para a média de volatilidade (padrão 4 horas).
Range Period Número de candles de período superior usados para calcular o alvo médio de pips.
EURUSD / GBPUSD / USDCHF / USDJPY Ativos que correspondem a cada instrumento negociado.

Todos os parâmetros suportam intervalos de otimização idênticos ao expert advisor original onde aplicável.

Lógica de negociação

  1. Atualização do indicador — Cada candle finalizado em um período de negociação atualiza as médias móveis suavizadas rápida e lenta para o par correspondente. Os valores só são considerados após a conclusão do aquecimento configurado (MA Shift).
  2. Cálculo do viés — A estratégia soma as últimas médias rápidas de todos os pares e subtrai a soma das médias lentas. Um resultado positivo indica pressão altista, enquanto um negativo indica pressão baixista.
  3. Condições de entrada — Quando não existe posição para um par, a estratégia entra com uma ordem de compra se o viés global for altista e a média rápida do par estiver acima da lenta. Abre uma ordem de venda no caso contrário.
  4. Saída por alvo de pips — A assinatura de quatro horas do EURUSD calcula o alcance médio de candles ao longo do período configurado. O alvo de pips atual é o maior entre esta média e 13 pips. Comprados fecham assim que o preço ganha pelo menos o número alvo de pips, e vendidos fecham após um movimento favorável equivalente.
  5. Proteção de capital — Sempre que o lucro flutuante exceder a porcentagem de take-profit, ou a perda flutuante ultrapassar a porcentagem de stop-loss, a estratégia fecha imediatamente todas as posições gerenciadas.

Notas de uso

  • Anexe a estratégia a um portfólio que forneça acesso a todos os quatro instrumentos forex e defina cada parâmetro de ativo explicitamente.
  • O período de sinal padrão é 15 minutos; certifique-se de que candles correspondentes estejam disponíveis para cada par de moedas.
  • Apenas uma posição aberta por par é mantida a qualquer momento. O parâmetro de volume da estratégia base é usado para cada entrada.
  • Como as saídas dependem do L/P flutuante, a estratégia é destinada à operação contínua em vez de apenas backtesting barra a barra.
  • O alvo de pips dinâmico usa a volatilidade do EURUSD em linha com a implementação original. Ajuste o período de range ou o período se preferir adaptar o alvo a um ambiente de mercado diferente.
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>
/// Trend strategy using smoothed moving averages. Simplified from multi-currency Vector.
/// </summary>
public class VectorStrategy : Strategy
{
	private readonly StrategyParam<int> _fastMaPeriod;
	private readonly StrategyParam<int> _slowMaPeriod;
	private readonly StrategyParam<int> _maShift;
	private readonly StrategyParam<decimal> _profitPercent;
	private readonly StrategyParam<decimal> _lossPercent;
	private readonly StrategyParam<DataType> _candleType;

	private SmoothedMovingAverage _fastMa;
	private SmoothedMovingAverage _slowMa;
	private decimal _entryPrice;
	private decimal _initialBalance;
	private int _processedBars;

	/// <summary>
	/// Fast smoothed moving average period.
	/// </summary>
	public int FastMaPeriod
	{
		get => _fastMaPeriod.Value;
		set => _fastMaPeriod.Value = value;
	}

	/// <summary>
	/// Slow smoothed moving average period.
	/// </summary>
	public int SlowMaPeriod
	{
		get => _slowMaPeriod.Value;
		set => _slowMaPeriod.Value = value;
	}

	/// <summary>
	/// Additional warm-up shift in bars.
	/// </summary>
	public int MaShift
	{
		get => _maShift.Value;
		set => _maShift.Value = value;
	}

	/// <summary>
	/// Floating profit target percent of balance.
	/// </summary>
	public decimal ProfitPercent
	{
		get => _profitPercent.Value;
		set => _profitPercent.Value = value;
	}

	/// <summary>
	/// Floating loss limit percent of balance.
	/// </summary>
	public decimal LossPercent
	{
		get => _lossPercent.Value;
		set => _lossPercent.Value = value;
	}

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

	/// <summary>
	/// Initializes strategy parameters.
	/// </summary>
	public VectorStrategy()
	{
		_fastMaPeriod = Param(nameof(FastMaPeriod), 3)
			.SetGreaterThanZero()
			.SetDisplay("Fast MA", "Fast smoothed moving average period", "Indicators")
			.SetOptimize(3, 15, 1);

		_slowMaPeriod = Param(nameof(SlowMaPeriod), 7)
			.SetGreaterThanZero()
			.SetDisplay("Slow MA", "Slow smoothed moving average period", "Indicators")
			.SetOptimize(5, 25, 1);

		_maShift = Param(nameof(MaShift), 8)
			.SetNotNegative()
			.SetDisplay("MA Shift", "Additional warm-up bars before signals", "Indicators");

		_profitPercent = Param(nameof(ProfitPercent), 0.5m)
			.SetNotNegative()
			.SetDisplay("Equity TP %", "Close all when floating profit reaches this percent", "Risk");

		_lossPercent = Param(nameof(LossPercent), 30m)
			.SetNotNegative()
			.SetDisplay("Equity SL %", "Close all when floating loss reaches this percent", "Risk");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Signal Timeframe", "Timeframe for moving averages", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, CandleType);
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_fastMa = null;
		_slowMa = null;
		_entryPrice = 0m;
		_initialBalance = 0m;
		_processedBars = 0;
	}

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

		_fastMa = new SmoothedMovingAverage { Length = FastMaPeriod };
		_slowMa = new SmoothedMovingAverage { Length = SlowMaPeriod };
		_initialBalance = Portfolio?.CurrentValue ?? 0m;

		SubscribeCandles(CandleType)
			.Bind(_fastMa, _slowMa, ProcessCandle)
			.Start();
	}

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

		_processedBars++;

		if (!IsFormed)
			return;

		if (_processedBars <= MaShift)
			return;

		// Check equity thresholds
		if (Position != 0 && _initialBalance > 0m)
		{
			var equity = Portfolio?.CurrentValue ?? 0m;
			var floating = equity - _initialBalance;
			var profitThreshold = _initialBalance * ProfitPercent / 100m;
			var lossThreshold = _initialBalance * LossPercent / 100m;

			if ((profitThreshold > 0m && floating >= profitThreshold) ||
				(lossThreshold > 0m && floating <= -lossThreshold))
			{
				if (Position > 0) SellMarket();
				else if (Position < 0) BuyMarket();
				_entryPrice = 0m;
				return;
			}
		}

		// Entry/exit logic
		if (fastValue > slowValue && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
			_entryPrice = candle.ClosePrice;
		}
		else if (fastValue < slowValue && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
			_entryPrice = candle.ClosePrice;
		}
	}
}