Открыть на GitHub

Стратегия Divergence Trader Basket

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

Данная стратегия представляет собой портирование эксперта MetaTrader "Divergence Trader" на платформу StockSharp. Алгоритм сравнивает две простые скользящие средние, рассчитанные по настраиваемым ценовым источникам, и анализирует величину их расхождения. Когда дистанция между быстрой и медленной средними попадает в заданный нейтральный коридор, стратегия ожидает возобновления импульса и открывает позицию в направлении текущего преобладающего движения. Обработка ведётся только по завершённым свечам выбранного таймфрейма с использованием высокоуровневого API и привязок индикаторов.

Параметры

  • Lot Size – объём сделки при открытии позиции. Значение автоматически приводится к шагу объёма инструмента.
  • Fast SMA Period / Price – период и тип цены для быстрой простой скользящей средней.
  • Slow SMA Period / Price – период и тип цены для медленной простой скользящей средней.
  • Buy Threshold – минимальное положительное расхождение, необходимое для открытия длинной позиции.
  • Stay-Out Threshold – максимальное допустимое расхождение; значения вне диапазона блокируют новые сделки.
  • Take Profit (pips) – целевой профит в пунктах. Ноль отключает функцию.
  • Stop Loss (pips) – допустимый убыток в пунктах. Ноль отключает функцию.
  • Trailing Stop (pips) – расстояние трейлинг-стопа, активируемого после выхода позиции в прибыль.
  • Break-Even Trigger / Buffer (pips) – прирост в пунктах для перевода сделки в безубыток и дополнительный буфер, позволяющий сместить уровень безубытка от цены входа.
  • Basket Profit / Basket Loss – пороги по доходности счёта, при достижении которых закрываются все позиции. Контроль по убытку по умолчанию отключён.
  • Start Hour / Stop Hour – торговое окно в локальном времени. При равных значениях стратегия работает круглосуточно.
  • Candle Type – таймфрейм, используемый для сигналов и управления позицией.

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

  1. Подписаться на свечи выбранного таймфрейма и вычислять значения двух простых скользящих средних.
  2. Работать только с закрытыми свечами, чтобы избежать внутрисвечного шума и сохранить поведение оригинального советника.
  3. Отслеживать расхождение (разницу между быстрой и медленной средней) на предыдущей закрытой свече:
    • Если расхождение положительное и лежит между Buy Threshold и Stay-Out Threshold, отправить рыночную заявку на покупку.
    • Если расхождение отрицательное и его модуль остаётся в пределах коридора, отправить рыночную заявку на продажу.
  4. Сделки игнорируются вне торговых часов или при наличии открытой позиции.

Управление позицией

  • Контроль безубытка – при достижении заданного профита сохраняется уровень безубытка (при необходимости смещённый буфером). Свеча, коснувшаяся уровня, закрывает позицию.
  • Трейлинг-стоп – когда прибыль превышает расстояние трейлинга, стоп сопровождает цену, оставаясь на заданном числе пунктов позади неё.
  • Take profit / stop loss – фиксированные уровни, рассчитываемые от цены входа в пунктах.
  • Защита портфеля – текущая стоимость портфеля сравнивается с заданными порогами прибыли и убытка. При срабатывании любого из порогов позиция закрывается, а активные заявки отменяются, что повторяет логику функции CloseEverything в MQL.

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

  • Коридор расхождения симметричен: увеличение Stay-Out Threshold позволяет дольше удерживать сделки, уменьшение – повышает частоту сигналов.
  • Типы цен соответствуют значениям CandlePrice в StockSharp, поэтому доступны цена открытия, закрытия, медианная, типичная и другие варианты, аналогичные MetaTrader.
  • На диаграмме выводятся свечи, обе скользящие средние и исполненные сделки для наглядного контроля.
  • Функции управления капиталом зависят от данных портфеля. В тестовой среде без статистики по счёту корзинные ограничения будут автоматически игнорироваться.
using System;
using System.Linq;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Divergence-based strategy converted from the Divergence Trader MQL expert advisor.
/// Trades based on the divergence between fast and slow moving averages.
/// </summary>
public class DivergenceTraderBasketStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<decimal> _buyThreshold;
	private readonly StrategyParam<decimal> _stayOutThreshold;
	private readonly StrategyParam<DataType> _candleType;

	private decimal? _previousDifference;
	private decimal _entryPrice;

	public DivergenceTraderBasketStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 7)
			.SetDisplay("Fast SMA Period", "Length of the fast simple moving average.", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 88)
			.SetDisplay("Slow SMA Period", "Length of the slow simple moving average.", "Indicators");

		_buyThreshold = Param(nameof(BuyThreshold), 0.0001m)
			.SetDisplay("Buy Threshold", "Minimum divergence value required before buying.", "Signals");

		_stayOutThreshold = Param(nameof(StayOutThreshold), 1000m)
			.SetDisplay("Stay-Out Threshold", "Upper divergence limit that disables new entries.", "Signals");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Primary timeframe used for calculations.", "General");
	}

	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	public decimal BuyThreshold
	{
		get => _buyThreshold.Value;
		set => _buyThreshold.Value = value;
	}

	public decimal StayOutThreshold
	{
		get => _stayOutThreshold.Value;
		set => _stayOutThreshold.Value = value;
	}

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

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

		_previousDifference = null;
		_entryPrice = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_previousDifference = null;
		_entryPrice = 0;

		var fastMa = new SimpleMovingAverage { Length = FastPeriod };
		var slowMa = new SimpleMovingAverage { Length = SlowPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fastMa, slowMa, ProcessCandle)
			.Start();

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

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

		var currentDiff = fastValue - slowValue;

		if (_previousDifference == null)
		{
			_previousDifference = currentDiff;
			return;
		}

		var prevDiff = _previousDifference.Value;
		_previousDifference = currentDiff;

		// Manage open position
		if (Position != 0)
		{
			// Exit on divergence sign change
			if (Position > 0 && currentDiff < 0)
			{
				SellMarket();
				_entryPrice = 0;
			}
			else if (Position < 0 && currentDiff > 0)
			{
				BuyMarket();
				_entryPrice = 0;
			}
			return;
		}

		// Entry logic: divergence crosses zero line
		if (currentDiff > 0 && prevDiff <= 0)
		{
			// Bullish divergence crossover
			BuyMarket();
			_entryPrice = candle.ClosePrice;
		}
		else if (currentDiff < 0 && prevDiff >= 0)
		{
			// Bearish divergence crossover
			SellMarket();
			_entryPrice = candle.ClosePrice;
		}
	}
}