Ver no GitHub

Estratégia Métrica Euclidiana Estatística

Visão geral

A estratégia reproduz o comportamento do MetaTrader consultor especialista Stat_Euclidean_Metric.mq4. Ele monitora MACD reversões em um único instrumento e período. Quando a linha MACD forma um ponto de inflexão local, a estratégia abre uma posição imediatamente (modo de treinamento) ou valida a configuração com um classificador k-vizinhos mais próximos (k-NN) que compara a estrutura atual do mercado com vetores de recursos históricos armazenados em arquivos binários.

Lógica de negociação

  1. Assine o tipo de vela configurado e calcule o indicador MACD no preço típico ((High + Low + Close) / 3).
  2. Detecte uma reversão de baixa quando os últimos três valores MACD concluídos satisfizerem MACD[2] <= MACD[1] e MACD[1] > MACD[0].
  3. Detecte uma reversão de alta quando MACD[2] >= MACD[1] e MACD[1] < MACD[0].
  4. Dependendo do modo selecionado:
    • Modo de treinamento (TrainingMode = true) – abra uma ordem de mercado na direção da reversão após, opcionalmente, fechar a posição atual. Isso imita o comportamento original EA ao coletar novas amostras.
    • Modo classificador (TrainingMode = false) – calcule cinco proporções de médias móveis simples do preço típico e avalie a probabilidade de sucesso com um modelo k-NN. Faça pedidos somente se a probabilidade ultrapassar os limites configurados.
  5. Aplique o módulo StartProtection integrado para anexar níveis de stop-loss e take-profit nas etapas do instrumento.

Vetor de recursos para classificação

O modelo k-NN usa as seguintes proporções calculadas na vela recém-fechada:

  • SMA(89) / SMA(144)
  • SMA(144) / SMA(233)
  • SMA(21) / SMA(89)
  • SMA(55) / SMA(89)
  • SMA(2) / SMA(55)

Cada amostra armazenada nos arquivos do conjunto de dados contém seis valores double: as cinco proporções acima e um rótulo (0 para um resultado desfavorável, 1 para uma negociação bem-sucedida). Durante a avaliação, a estratégia seleciona as amostras NeighborCount mais próximas, calcula a média de seus rótulos e interpreta o resultado como a probabilidade de sucesso.

Arquivos de conjunto de dados

  • BuyDatasetPath – caminho para o arquivo binário com vetores coletados após negociações de alta.
  • SellDatasetPath – caminho para o arquivo binário com vetores coletados após negociações de baixa.

Se um caminho for relativo, ele será resolvido em Environment.CurrentDirectory. Os arquivos ausentes são relatados no log e tratados como um conjunto de dados vazio. Esta implementação lê conjuntos de dados, mas não atualiza nem acrescenta novas amostras automaticamente; a exportação de novos vetores deve ser tratada externamente durante a execução no modo de treinamento.

Parâmetros

  • TrainingMode – alterne entre negociação MACD pura e negociação assistida por classificador.
  • BuyThreshold / SellThreshold – probabilidade mínima retornada pelo classificador para abrir negociações na direção primária.
  • AllowInverseEntries – permite negociações contrárias quando a probabilidade é extremamente baixa.
  • InverseBuyThreshold / InverseSellThreshold – probabilidade máxima que ainda desencadeia uma negociação na direção oposta.
  • FastLength / SlowLength / SignalLength – MACD EMA comprimentos.
  • TakeProfitPoints / StopLossPoints – níveis de proteção expressos em etapas do instrumento.
  • ClosePositionsOnSignal – fecha a posição líquida atual antes de enviar uma nova ordem.
  • BuyDatasetPath / SellDatasetPath – arquivos binários que armazenam vetores históricos.
  • NeighborCount – número de vizinhos usados na votação k-NN.
  • CandleType – série de velas usada para todos os indicadores.

Recomendações de uso

  • Forneça caminhos absolutos ou relativos ao diretório de trabalho para os arquivos do conjunto de dados antes de ativar o modo classificador.
  • Colete amostras de alta qualidade executando a estratégia no modo de treinamento em dados históricos e exportando vetores manualmente.
  • Otimize os limites e a contagem de vizinhos para adaptar o classificador a novos mercados ou instrumentos.
  • Mantenha o parâmetro Volume do instrumento alinhado ao modelo de risco, pois a estratégia sempre abre Volume + |Position| lotes para reverter a posição líquida quando necessário.

Diferenças da versão MQL4

  • Os conjuntos de dados do classificador são apenas lidos; o EA original grava novas amostras durante a desinicialização. Aqui o usuário deve atualizar os arquivos manualmente após analisar o histórico de negociações.
  • Todas as ordens de proteção são anexadas por meio de StockSharp StartProtection em vez de parâmetros manuais OrderSend.
  • O fechamento de ordem no modo classificador sempre sai de toda a posição quando ClosePositionsOnSignal está habilitado, enquanto o script MQL4 fecha apenas ordens lucrativas antes de receber novos sinais.
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// MACD reversal strategy with moving average ratio filter.
/// Enters on MACD histogram reversals filtered by MA trend alignment.
/// </summary>
public class StatEuclideanMetricStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastLength;
	private readonly StrategyParam<int> _slowLength;
	private readonly StrategyParam<int> _trendMaLength;

	private readonly List<decimal> _macdHistory = new();

	public StatEuclideanMetricStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for analysis.", "General");

		_fastLength = Param(nameof(FastLength), 12)
			.SetDisplay("Fast Length", "Fast EMA period for MACD.", "Indicators");

		_slowLength = Param(nameof(SlowLength), 26)
			.SetDisplay("Slow Length", "Slow EMA period for MACD.", "Indicators");

		_trendMaLength = Param(nameof(TrendMaLength), 50)
			.SetDisplay("Trend MA Length", "Period for trend filter MA.", "Indicators");
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int FastLength
	{
		get => _fastLength.Value;
		set => _fastLength.Value = value;
	}

	public int SlowLength
	{
		get => _slowLength.Value;
		set => _slowLength.Value = value;
	}

	public int TrendMaLength
	{
		get => _trendMaLength.Value;
		set => _trendMaLength.Value = value;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_macdHistory.Clear();
	}

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

		var fastEma = new ExponentialMovingAverage { Length = FastLength };
		var slowEma = new ExponentialMovingAverage { Length = SlowLength };
		var trendMa = new SimpleMovingAverage { Length = TrendMaLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fastEma, slowEma, trendMa, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, trendMa);
			DrawOwnTrades(area);
		}
	}

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

		var macdLine = fastValue - slowValue;

		_macdHistory.Add(macdLine);
		if (_macdHistory.Count > 5)
			_macdHistory.RemoveAt(0);

		if (_macdHistory.Count < 3)
			return;

		var close = candle.ClosePrice;
		var macd1 = _macdHistory[^1];
		var macd2 = _macdHistory[^2];
		var macd3 = _macdHistory[^3];

		// MACD reversal patterns
		var buyReversal = macd3 >= macd2 && macd2 < macd1; // V-shape bottom
		var sellReversal = macd3 <= macd2 && macd2 > macd1; // inverted V top

		// Exit conditions
		if (Position > 0 && sellReversal)
		{
			SellMarket();
		}
		else if (Position < 0 && buyReversal)
		{
			BuyMarket();
		}

		// Entry conditions with trend filter
		if (Position == 0)
		{
			if (buyReversal && close > trendValue)
			{
				BuyMarket();
			}
			else if (sellReversal && close < trendValue)
			{
				SellMarket();
			}
		}
	}
}