Открыть на GitHub

Стратегия Graal Fractal Channel

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

Graal Fractal Channel — порт советника MetaTrader 4 «Graal-003» на платформу StockSharp. Алгоритм отслеживает фрактальные комбинации из пяти свечей и подтверждает пробои адаптивными ценовыми каналами. После появления бычьего или медвежьего фрактала стратегия проверяет набор фильтров (фрактальный тоннель, канал по ценам закрытия и опциональный запрет на флэт) и только затем входит в сторону пробоя. Дополнительный фильтр Williams %R повторяет ручную логику выхода оригинального эксперта, а контр-трендовые стоп-заявки имитируют хеджирующее поведение робота.

Поток данных и индикаторы

  • Подписка на свечи типа CandleType (по умолчанию часовые).
  • Очередь последних ChannelPeriod свечей используется для расчёта канала по ценам закрытия, который помогает распознавать флэт и направление пробоя.
  • Фракталы верх/низ вычисляются напрямую по завершённым свечам.
  • Индикатор WilliamsPercentRange служит источником сигналов на досрочное закрытие позиций.

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

  1. Определение фракталов — стратегия хранит пять завершённых свечей. Если максимум (или минимум) центральной свечи экстремален относительно двух предыдущих и двух последующих, фиксируется верхний или нижний фрактал и отмечается ожидаемый шорт/лонг сигнал.
  2. Старение сигналов — при каждом новом баре увеличивается возраст фрактала. Как только количество свечей превысит SignalAgeLimit, сигнал аннулируется.
  3. Оценка каналов — скользящее окно цен закрытия обеспечивает три фильтра:
    • Фрактальный тоннель: при активном UseFractalChannel цена закрытия должна находиться внутри доли (DepthPercent) диапазона между последними фракталами.
    • Ориентация канала: при включённом UseHighLowChannel пробой допускается только в пределах OrientationPercent ширины канала.
    • Флэт-фильтр: если AllowFlatTrading = false, торговля блокируется, пока ширина канала меньше FlatThresholdPips.
  4. Исполнение сделок — после прохождения фильтров объём OrderVolume нормализуется с учётом ограничений инструмента и отправляется рыночная заявка в сторону фрактала.
  5. Хеджирующие стопы — при активном UseCounterOrders формируется противоположный стоп-ордер на уровне фрактала плюс/минус OffsetPips, как в оригинальном советнике.
  6. Выходы по Williams %R — если включён UseWilliamsExit, позиция закрывается, когда Williams %R поднимается выше -WilliamsThreshold (для лонга) или опускается ниже -100 + WilliamsThreshold (для шорта).

Стоп-лосс и тейк-профит задаются опционально. При положительных StopLossPips или TakeProfitPips значения переводятся в абсолютные цены с учётом шага котировки (включая корректировку под 3/5 знаков) и передаются в StartProtection, которая управляет защитными заявками.

Параметры

Параметр Значение по умолчанию Описание
OrderVolume 0.1 Базовый объём рыночных сделок до нормализации по ограничениям инструмента.
StopLossPips 500 Расстояние стоп-лосса в пунктах, переводится в цену и передаётся в StartProtection.
TakeProfitPips 500 Расстояние тейк-профита в пунктах, переводится в цену и передаётся в StartProtection.
OffsetPips 5 Дополнительный отступ при постановке контр-трендовых стоп-заявок.
ChannelPeriod 14 Количество последних свечей для расчёта канала цен закрытия.
UseFractalChannel false Требует, чтобы цена оставалась внутри внутреннего фрактального коридора.
DepthPercent 25 Доля диапазона между фракталами, формирующая внутренний коридор.
UseHighLowChannel false Включает фильтр по направлению канала на основе цен закрытия.
OrientationPercent 20 Допустимое проникновение цены в канал при активном UseHighLowChannel.
AllowFlatTrading true Разрешает торговлю во флэте.
FlatThresholdPips 20 Минимальная ширина канала (в пунктах) при отключённой торговле во флэте.
UseWilliamsExit false Активирует выходы по Williams %R.
WilliamsPeriod 14 Период расчёта индикатора Williams %R.
WilliamsThreshold 30 Порог чувствительности (в процентных пунктах) для выходов Williams %R.
UseCounterOrders false После входа размещает противоположный стоп-ордер.
SinglePosition false Блокирует повторные входы в том же направлении при открытой позиции.
SignalAgeLimit 3 Максимальный возраст фрактального сигнала в барах.
CandleType H1 Тип свечей для анализа (по умолчанию часовой таймфрейм).

Практические замечания

  • Инструмент должен предоставлять корректные PriceStep, MinVolume и VolumeStep, чтобы нормализация объёма и перевод пунктов в цену работали верно.
  • Контр-трендовые ордера автоматически снимаются при закрытии позиции, остановке стратегии или при отключении функции.
  • Выходы по Williams %R служат страховкой и могут закрыть позицию даже при активном фрактальном сигнале.
  • Метод OnReseted очищает внутренние буферы (фракталы, значения Williams, отложенные ордера), что полезно при повторном запуске.

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

  • Используется высокоуровневый SubscribeCandles().Bind(...), что заменяет ручные циклы обработки индикаторов в MQL.
  • Защитные стопы оформляются через StartProtection, поэтому нет прямого управления стоп/лимит заявками.
  • Перед отправкой заявок объём нормализуется по требованиям биржи, что соответствует практикам StockSharp.
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>
/// Graal Fractal Channel strategy - Highest/Lowest channel breakout.
/// Buys when close crosses above the upper channel.
/// Sells when close crosses below the lower channel.
/// Uses midpoint crossing for exits.
/// </summary>
public class GraalFractalChannelStrategy : Strategy
{
	private readonly StrategyParam<int> _channelPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevClose;
	private decimal _prevMid;
	private bool _hasPrev;

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

	public GraalFractalChannelStrategy()
	{
		_channelPeriod = Param(nameof(ChannelPeriod), 20)
			.SetDisplay("Channel Period", "Highest/Lowest lookback", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
	protected override void OnReseted() { base.OnReseted(); _prevClose = 0m; _prevMid = 0m; _hasPrev = false; }

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

		_hasPrev = false;

		var highest = new Highest { Length = ChannelPeriod };
		var lowest = new Lowest { Length = ChannelPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(highest, lowest, ProcessCandle)
			.Start();
	}

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

		var close = candle.ClosePrice;
		var mid = (highest + lowest) / 2;

		if (!_hasPrev)
		{
			_prevClose = close;
			_prevMid = mid;
			_hasPrev = true;
			return;
		}

		// Close crosses above midpoint = buy
		if (_prevClose <= _prevMid && close > mid && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Close crosses below midpoint = sell
		else if (_prevClose >= _prevMid && close < mid && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevClose = close;
		_prevMid = mid;
	}
}