Открыть на GitHub

Стратегия Exp XHullTrend Digit

Обзор

  • Перенос эксперта MQL5 Exp_XHullTrend_Digit.mq5 из каталога MQL/22117 на высокоуровневый API StockSharp.
  • Пользовательский индикатор XHullTrendDigitIndicator воспроизводит логику XHullTrend Digit вместе с округлением к дискрету инструмента.
  • По умолчанию анализирует свечи восьмичасового таймфрейма, но может работать на любом доступном интервале.

Логика индикатора

  1. В качестве входных данных используется выбранная цена свечи (по умолчанию закрытие).
  2. Рассчитываются две скользящие средние с длинами BaseLength и BaseLength / 2 в соответствии с выбранным методом сглаживания (SMA/EMA/SMMA/LWMA).
  3. Формируется Hull-комбинация 2 * короткая - длинная, которая дополнительно сглаживается с длиной SignalLength, а затем sqrt(BaseLength).
  4. Обе линии округляются до ближайшего шага цены, умноженного на 10^RoundingDigits, чтобы повторить режим Digit из исходного скрипта.
  5. Если округление делает линии равными, но необработанные значения различаются, быстрый или медленный канал сдвигается на один шаг по направлению исходного отклонения, чтобы пересечение не потерялось.

Торговые правила

  • Сигналы анализируются только на закрытых свечах.
  • Параметр SignalBar задаёт смещение по истории, используемое для поиска пересечения (1 — предыдущая свеча против позапрошлой и т.д.).
  • Открытие лонга: на предыдущей свече быстрая линия выше медленной и на выбранной свече быстрая линия находится не выше медленной (факт пересечения вверх). Одновременно может закрываться шорт.
  • Открытие шорта: на предыдущей свече быстрая линия ниже медленной и на выбранной свече быстрая линия не ниже медленной (пересечение вниз). Одновременно может закрываться лонг.
  • Закрытие лонга: предыдущая быстрая линия опускается ниже медленной.
  • Закрытие шорта: предыдущая быстрая линия поднимается выше медленной.
  • При появлении разворотного сигнала стратегия сначала закрывает противоположную позицию, затем выставляет рыночный ордер на разворот требуемого объёма.

Параметры

  • OrderVolume – объём рыночных заявок.
  • StopLoss / TakeProfit – защитные расстояния в шагах цены, передаются в StartProtection как UnitTypes.Step.
  • EnableBuyEntry, EnableSellEntry – разрешение открытия позиций в каждой из сторон.
  • EnableBuyExit, EnableSellExit – включение автоматического выхода для лонгов и шортов.
  • CandleType – таймфрейм свечей для расчётов (по умолчанию 8 часов).
  • BaseLength – базовая длина сглаживания индикатора (XLength в MQL5).
  • SignalLength – длина дополнительного сглаживания Hull (HLength в MQL5).
  • PriceSource – используемая цена свечи (close/open/high/low/typical/weighted/median/average).
  • SmoothMethod – тип скользящего среднего для всех стадий расчёта (простое, экспоненциальное, сглаженное, взвешенное).
  • Phase – параметр совместимости, не влияет на реализованные методы сглаживания.
  • RoundingDigits – количество дополнительных разрядов при округлении.
  • SignalBar – смещение бара для оценки сигнала (0 — текущий закрытый бар, 1 — предыдущий и т.д.).

Управление рисками

  • Встроенный StartProtection задаёт стоп-лосс и тейк-профит в шагах цены; параметры можно отключить установкой нуля.
  • Объём сделок регулируется через OrderVolume в соответствии с выбранным инструментом.

Примечания

  • Для корректного округления индикатор использует Security.PriceStep; убедитесь, что шаг цены инструмента заполнен.
  • Реализованы четыре метода сглаживания (SMA/EMA/SMMA/LWMA). Экзотические режимы из MQL5 можно добавить при необходимости.
  • Подходит для любых инструментов с доступным таймфреймом; при смене рынка стоит скорректировать длины сглаживания и количество разрядов округления.
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

public class ExpXHullTrendDigitStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private decimal? _prevFast, _prevSlow;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }

	public ExpXHullTrendDigitStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
		_fastPeriod = Param(nameof(FastPeriod), 9).SetGreaterThanZero().SetDisplay("Fast WMA", "Fast period", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 25).SetGreaterThanZero().SetDisplay("Slow WMA", "Slow period", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];

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

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevFast = null; _prevSlow = null;
		var fast = new WeightedMovingAverage { Length = FastPeriod };
		var slow = new WeightedMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fast, slow, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fast); DrawIndicator(area, slow); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!IsFormedAndOnlineAndAllowTrading()) { _prevFast = fast; _prevSlow = slow; return; }
		if (_prevFast == null || _prevSlow == null) { _prevFast = fast; _prevSlow = slow; return; }
		var prevAbove = _prevFast.Value > _prevSlow.Value;
		var currAbove = fast > slow;
		_prevFast = fast; _prevSlow = slow;
		if (!prevAbove && currAbove && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
		else if (prevAbove && !currAbove && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
	}
}