Открыть на GitHub

Стратегия «Meeting Lines Stochastic»

Общее описание

Стратегия Meeting Lines Stochastic переносит механику MetaTrader-советника Expert_AML_Stoch в инфраструктуру StockSharp. Торговые решения принимаются на основе свечных моделей «бычьи/медвежьи встречные линии», подтверждённых показаниями сигнальной линии %D стохастического осциллятора. Использование высокоуровневого API StockSharp обеспечивает чистую архитектуру, гибкие параметры и готовность к оптимизации в Designer.

Логика торговли

  1. Фильтр свечных моделей

    • На каждой закрывшейся свече анализируются две предыдущие свечи.
    • Для бычьей конфигурации требуется длинная чёрная свеча, за которой следует длинная белая свеча; их цены закрытия должны отличаться менее чем на 10% от среднего тела.
    • Для медвежьей конфигурации условия зеркальны: сначала длинная белая свеча, затем длинная чёрная с близкими ценами закрытия.
    • Средний размер тела вычисляется простым скользящим средним по модулю тела, что эквивалентно функции AvgBody из исходного MQL.
  2. Подтверждение стохастиком

    • Учитывается только сигнальная линия %D.
    • Для открытия длинной позиции %D предыдущей свечи должен находиться ниже порогового значения (по умолчанию 30).
    • Для открытия короткой позиции %D должен превышать верхний порог (по умолчанию 70).
  3. Правила выхода

    • Короткие позиции закрываются при пробое %D вверх через нижний (20) или верхний (80) уровень.
    • Длинные позиции закрываются при пробое %D вниз через те же уровни.
    • При появлении противоположного сигнала стратегия отправляет рыночную заявку объёмом, достаточным для переворота позиции.
  4. Обработка объёма

    • Если свойство Volume задано положительным числом, используется оно; иначе применяется объём 1, что соответствует фиксированному лоту оригинального эксперта.

Параметры

Параметр Назначение Значение по умолчанию Примечание
CandleType Тип свечей для анализа 15-минутные свечи Можно использовать любой DataType.
StochasticLength База расчёта %K 3 Аналог %K period в MetaTrader.
StochasticSmoothing Сглаживание %K (slowing) 25 Контролирует сглаживание входной серии.
StochasticSignal Период сигнальной линии %D 36 Аналог %D period.
BodyAveragePeriod Длина окна для среднего тела свечи 3 Отсекает малые свечи.
LongEntryLevel Максимальное значение %D для входа в лонг 30 Порог перепроданности.
ShortEntryLevel Минимальное значение %D для входа в шорт 70 Порог перекупленности.
ExitLowerLevel Нижняя граница для выхода 20 Используется для фиксации прибыли в обе стороны.
ExitUpperLevel Верхняя граница для выхода 80 Используется совместно с нижней границей.

Все параметры реализованы через StrategyParam<T>, что упрощает оптимизацию и интеграцию с интерфейсами StockSharp.

Сигналы

  • Открытие лонга: обнаружена бычья встречная линия и %D предыдущей свечи ниже LongEntryLevel. При наличии короткой позиции выполняется переворот.
  • Открытие шорта: обнаружена медвежья встречная линия и %D выше ShortEntryLevel. При наличии лонга выполняется переворот.
  • Закрытие лонга: %D пересекает вниз ExitUpperLevel или ExitLowerLevel.
  • Закрытие шорта: %D пересекает вверх ExitLowerLevel или ExitUpperLevel.

Особенности реализации

  • Подписка на свечи выполняется через SubscribeCandles, а значения стохастика поступают в обработчик посредством BindEx, поэтому дополнительных коллекций индикаторов не требуется.
  • Среднее тело вычисляется индикатором SimpleMovingAverage, который получает абсолютный размер тела через DecimalIndicatorValue — полностью соответствует MQL-алгоритму.
  • Код оформлен с соблюдением требований AGENTS.md: английские комментарии и табуляция.
  • При наличии окна графика автоматически отображаются свечи и стохастик, что облегчает визуальный контроль.

Рекомендации по применению

  1. Оптимизация: тестируйте параметры на истории и используйте walk-forward для контроля устойчивости.
  2. Риск-менеджмент: при необходимости подключите StartProtection или внешние модули управления капиталом для ограничения убытков.
  3. Качество данных: формация чувствительна к ценам открытия/закрытия, поэтому желательно исключить периоды низкой ликвидности и сильных гэпов.
  4. Гибкость таймфреймов: параметр CandleType позволяет адаптировать стратегию под внутридневные или позиционные подходы.

Стратегия подходит тем, кто доверяет свечным разворотным моделям, но предпочитает подтверждать их объективными индикаторными фильтрами.

namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Meeting Lines + Stochastic strategy.
/// Buys on bullish meeting lines with low stochastic, sells on bearish meeting lines with high stochastic.
/// </summary>
public class MeetingLinesStochasticStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _stochPeriod;
	private readonly StrategyParam<decimal> _stochLow;
	private readonly StrategyParam<decimal> _stochHigh;

	private ICandleMessage _prevCandle;
	private ICandleMessage _prevPrevCandle;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int StochPeriod { get => _stochPeriod.Value; set => _stochPeriod.Value = value; }
	public decimal StochLow { get => _stochLow.Value; set => _stochLow.Value = value; }
	public decimal StochHigh { get => _stochHigh.Value; set => _stochHigh.Value = value; }

	public MeetingLinesStochasticStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_stochPeriod = Param(nameof(StochPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("Stochastic Period", "Stochastic K period", "Indicators");
		_stochLow = Param(nameof(StochLow), 30m)
			.SetDisplay("Stoch Low", "Stochastic oversold level", "Signals");
		_stochHigh = Param(nameof(StochHigh), 70m)
			.SetDisplay("Stoch High", "Stochastic overbought level", "Signals");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevCandle = null;
		_prevPrevCandle = null;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevCandle = null;
		_prevPrevCandle = null;
		var stoch = new StochasticOscillator { K = { Length = StochPeriod }, D = { Length = 3 } };
		var subscription = SubscribeCandles(CandleType);
		subscription.BindEx(stoch, ProcessCandle).Start();
	}

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

		var stochTyped = stochValue as StochasticOscillatorValue;
		if (stochTyped?.K is not decimal kValue) { UpdateState(candle); return; }

		if (_prevCandle != null && _prevPrevCandle != null)
		{
			var avgBody = (Math.Abs(_prevCandle.ClosePrice - _prevCandle.OpenPrice) +
						   Math.Abs(_prevPrevCandle.ClosePrice - _prevPrevCandle.OpenPrice)) / 2m;

			if (avgBody > 0)
			{
				// Bullish meeting lines: prev bearish, current bullish, closes near
				var prevBearish = _prevCandle.OpenPrice > _prevCandle.ClosePrice &&
								  (_prevCandle.OpenPrice - _prevCandle.ClosePrice) > avgBody * 0.5m;
				var currBullish = candle.ClosePrice > candle.OpenPrice &&
								  (candle.ClosePrice - candle.OpenPrice) > avgBody * 0.5m;
				var closesNear = Math.Abs(candle.ClosePrice - _prevCandle.ClosePrice) < avgBody * 0.3m;

				if (prevBearish && currBullish && closesNear && kValue < StochLow && Position <= 0)
					BuyMarket();

				// Bearish meeting lines: prev bullish, current bearish, closes near
				var prevBullish = _prevCandle.ClosePrice > _prevCandle.OpenPrice &&
								  (_prevCandle.ClosePrice - _prevCandle.OpenPrice) > avgBody * 0.5m;
				var currBearish = candle.OpenPrice > candle.ClosePrice &&
								  (candle.OpenPrice - candle.ClosePrice) > avgBody * 0.5m;
				var closesNear2 = Math.Abs(candle.ClosePrice - _prevCandle.ClosePrice) < avgBody * 0.3m;

				if (prevBullish && currBearish && closesNear2 && kValue > StochHigh && Position >= 0)
					SellMarket();
			}
		}

		UpdateState(candle);
	}

	private void UpdateState(ICandleMessage candle)
	{
		_prevPrevCandle = _prevCandle;
		_prevCandle = candle;
	}
}