Ver en GitHub

Estrategia métrica euclidiana estadística

Descripción general

La estrategia reproduce el comportamiento del MetaTrader asesor experto Stat_Euclidean_Metric.mq4. Supervisa las reversiones de MACD en un solo instrumento y período de tiempo. Cuando la línea MACD forma un punto de inflexión local, la estrategia abre una posición inmediatamente (modo de entrenamiento) o valida la configuración con un clasificador de k vecinos más cercanos (k-NN) que compara la estructura actual del mercado con vectores de características históricas almacenados en archivos binarios.

Lógica comercial

  1. Suscríbase al tipo de vela configurado y calcule el indicador MACD sobre el precio típico ((Máximo + Mínimo + Cierre) / 3).
  2. Detecte una reversión bajista cuando los últimos tres valores completos de MACD cumplan con MACD[2] <= MACD[1] y MACD[1] > MACD[0].
  3. Detectar una reversión alcista cuando MACD[2] >= MACD[1] y MACD[1] < MACD[0].
  4. Dependiendo del modo seleccionado:
    • Modo de entrenamiento (TrainingMode = true): abra una orden de mercado en la dirección de la reversión después de cerrar opcionalmente la posición actual. Esto imita el comportamiento original de EA cuando recopila nuevas muestras.
    • Modo clasificador (TrainingMode = false): calcula cinco proporciones de promedios móviles simples del precio típico y evalúa la probabilidad de éxito con un modelo k-NN. Realice pedidos solo si la probabilidad cruza los umbrales configurados.
  5. Aplique el módulo integrado StartProtection para adjuntar niveles de stop-loss y take-profit en pasos del instrumento.

Vector de características para clasificación

El modelo k-NN utiliza los siguientes ratios calculados sobre la vela recién cerrada:

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

Cada muestra almacenada en los archivos del conjunto de datos contiene seis valores double: las cinco proporciones anteriores y una etiqueta (0 para un resultado desfavorable, 1 para una operación exitosa). Durante la evaluación, la estrategia selecciona las muestras NeighborCount más cercanas, promedia sus etiquetas e interpreta el resultado como la probabilidad de éxito.

Archivos de conjunto de datos

  • BuyDatasetPath: ruta al archivo binario con vectores recopilados después de operaciones alcistas.
  • SellDatasetPath: ruta al archivo binario con vectores recopilados después de operaciones bajistas.

Si una ruta es relativa, se resolverá con Environment.CurrentDirectory. Los archivos faltantes se informan en el registro y se tratan como un conjunto de datos vacío. Esta implementación lee conjuntos de datos pero no actualiza ni agrega nuevas muestras automáticamente; La exportación de nuevos vectores debe manejarse externamente cuando se ejecuta en modo de entrenamiento.

Parámetros

  • TrainingMode: cambie entre el comercio MACD puro y el comercio asistido por clasificador.
  • BuyThreshold / SellThreshold: probabilidad mínima devuelta por el clasificador para abrir operaciones en la dirección principal.
  • AllowInverseEntries: permite operaciones contrarias cuando la probabilidad es extremadamente baja.
  • InverseBuyThreshold / InverseSellThreshold: probabilidad máxima que aún activa una operación en dirección opuesta.
  • FastLength / SlowLength / SignalLength – MACD EMA longitudes.
  • TakeProfitPoints / StopLossPoints: niveles de protección expresados en pasos del instrumento.
  • ClosePositionsOnSignal: cierra la posición neta actual antes de enviar una nueva orden.
  • BuyDatasetPath / SellDatasetPath: archivos binarios que almacenan vectores históricos.
  • NeighborCount: número de vecinos utilizados en la votación k-NN.
  • CandleType – serie de velas utilizadas para todos los indicadores.

Recomendaciones de uso

  • Proporcione rutas absolutas o relativas al directorio de trabajo a los archivos del conjunto de datos antes de habilitar el modo clasificador.
  • Recopile muestras de alta calidad ejecutando la estrategia en modo de entrenamiento con datos históricos y exportando vectores manualmente.
  • Optimice los umbrales y el recuento de vecinos para adaptar el clasificador a nuevos mercados o instrumentos.
  • Mantenga el parámetro Volume del instrumento alineado con el modelo de riesgo porque la estrategia siempre abre Volume + |Position| lotes para revertir la posición neta cuando sea necesario.

Diferencias con la versión MQL4

  • Los conjuntos de datos del clasificador solo se leen; el EA original escribe nuevas muestras durante la desinicialización. Aquí el usuario debe actualizar los archivos manualmente después de analizar el historial comercial.
  • Todas las órdenes de protección se adjuntan mediante parámetros StockSharp StartProtection en lugar de parámetros manuales OrderSend.
  • El cierre de órdenes en el modo clasificador siempre sale de la posición completa cuando ClosePositionsOnSignal está habilitado, mientras que el script MQL4 cierra solo las órdenes rentables antes de recibir nuevas señales.
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();
			}
		}
	}
}