Открыть на GitHub

Стратегия Stat Euclidean Metric

Обзор

Стратегия повторяет логику советника MetaTrader Stat_Euclidean_Metric.mq4. Она отслеживает развороты линии MACD на выбранном инструменте и таймфрейме. В базовом режиме стратегия сразу открывает позицию по сигналу, а в основном режиме сравнивает текущие рыночные условия с историческими векторами признаков из бинарных файлов и принимает решение на основе алгоритма k ближайших соседей.

Торговая логика

  1. Подписка на свечи заданного типа и расчёт MACD по типичной цене ((High + Low + Close) / 3).
  2. Фиксация медвежьего разворота, когда для трёх последних завершённых свечей выполняется MACD[2] <= MACD[1] и MACD[1] > MACD[0].
  3. Фиксация бычьего разворота, когда MACD[2] >= MACD[1] и MACD[1] < MACD[0].
  4. Реакция на сигнал зависит от режима:
    • TrainingMode = true — открытие рыночной позиции по направлению разворота (при необходимости сначала закрывается текущая позиция).
    • TrainingMode = false — расчёт пяти отношений простых скользящих средних типичной цены, оценка вероятности успеха по k-NN и открытие сделок только при выполнении порогов.
  5. Подключение встроенной защиты StartProtection, которая автоматически добавляет стоп-лосс и тейк-профит в шагах цены.

Признаки для классификатора

Вектор признаков формируется на только что закрытой свече и включает:

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

Каждая запись в файлах содержит шесть значений типа double: пять отношений и метку (0 — неудачная сделка, 1 — успешная). При вычислении стратегии выбирается NeighborCount ближайших записей, после чего среднее по меткам трактуется как вероятность успеха.

Файлы данных

  • BuyDatasetPath — путь к бинарному файлу с векторами после покупок.
  • SellDatasetPath — путь к бинарному файлу с векторами после продаж.

Относительные пути приводятся к Environment.CurrentDirectory. Отсутствие файлов фиксируется в журнале и трактуется как пустой набор. Текущая реализация только читает файлы и не добавляет новые записи автоматически; при работе в учебном режиме экспорт векторов необходимо выполнять вручную.

Параметры

  • TrainingMode — выбор между чистой логикой MACD и режимом с классификатором.
  • BuyThreshold / SellThreshold — минимальная вероятность для открытия позиции по сигналу.
  • AllowInverseEntries — разрешение на контртрендовые сделки при очень низкой вероятности.
  • InverseBuyThreshold / InverseSellThreshold — максимальная вероятность, при которой открывается противоположная сделка.
  • FastLength / SlowLength / SignalLength — периоды EMA в MACD.
  • TakeProfitPoints / StopLossPoints — расстояние до тейк-профита и стоп-лосса в шагах цены.
  • ClosePositionsOnSignal — закрывать ли текущую позицию перед новой заявкой.
  • BuyDatasetPath / SellDatasetPath — пути к бинарным файлам с обучающими данными.
  • NeighborCount — количество соседей в голосовании k-NN.
  • CandleType — тип свечей для всех расчётов.

Рекомендации по использованию

  • Перед запуском режима с классификатором укажите корректные пути к файлам данных (абсолютные или относительные).
  • Для пополнения базы данных запустите стратегию в учебном режиме на исторических данных и сохраните признаки вручную.
  • Оптимизируйте пороги и количество соседей, чтобы адаптировать модель к различным инструментам.
  • Контролируйте параметр Volume: при смене направления стратегия выставляет заявку объёмом Volume + |Position|, чтобы мгновенно перевернуться.

Отличия от версии MQL4

  • Данные классификатора только читаются. В оригинальном советнике новые записи добавлялись при завершении работы, здесь файлы нужно обновлять самостоятельно.
  • Защита позиции реализована через StartProtection, а не через параметры OrderSend.
  • В классификационном режиме при включённом ClosePositionsOnSignal стратегия полностью закрывает позицию перед новым сигналом. В MQL4-скрипте закрывались только прибыльные ордера.
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();
			}
		}
	}
}