Открыть на GitHub

Стратегия Spazm Volatility Breakout

Краткое описание

  • Конвертация советника MetaTrader 4 Spazm (8683) на высокий уровень API StockSharp.
  • Торгует адаптивные пробои: сравнивает цену закрытия со смещёнными на волатильность уровнями вокруг последнего максимума и минимума.
  • При желании наносит на график линии между последовательными экстремумами, повторяя визуализацию оригинального MQL-скрипта.

Подготовка данных

  1. Стратегия подписывается на свечи типа CandleType по выбранному инструменту.
  2. Каждая завершённая свеча формирует выборку диапазона для оценки волатильности:
    • По умолчанию используется диапазон High - Low.
    • Если UseOpenCloseRange = true, берётся модуль тела |Open - Close|.
  3. Полученный диапазон переводится в количество ценовых шагов через PriceStep, что делает расчёты независимыми от тиковой стоимости инструмента.
  4. В зависимости от UseWeightedVolatility выбирается индикатор усреднения диапазона:
    • false — простое скользящее среднее длиной VolatilityPeriod.
    • true — линейно взвешенное скользящее среднее, усиливающее влияние последних свечей.
  5. Сглаженный диапазон (в шагах) умножается на VolatilityMultiplier и возвращается в ценовое выражение. Это и есть адаптивный порог, который используется при проверке пробоев.
  6. Параллельно в течение первых VolatilityPeriod * 3 свечей стратегия запоминает последние экстремумы и их время. Как только накоплено достаточное количество данных, по более свежему экстремуму определяется исходное направление тренда.

Параметры

Параметр Значение по умолчанию Описание
Volume 1 Объём заявки при открытии или реверсе позиции.
VolatilityMultiplier 5 Множитель усреднённой волатильности для расчёта ширины пробойного коридора.
VolatilityPeriod 24 Длина выборки для индикатора волатильности и для поиска стартовых экстремумов.
UseWeightedVolatility false Переключение между простым и линейно взвешенным средним.
UseOpenCloseRange false Использовать диапазон тела свечи вместо High - Low.
StopLossMultiplier 0 Множитель порога для расчёта защитного стопа. Минимальный отступ — три шага цены. 0 отключает стопы.
DrawSwingLines true Рисовать на графике линию между последним максимумом и минимумом.
CandleType 4 часа Таймфрейм свечей, на которых выполняются расчёты.

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

  1. Инициализация
    • Пока обрабатываются первые VolatilityPeriod * 3 свечей, обновляются _highestPrice, _lowestPrice и их время.
    • После накопления достаточного количества баров направление определяется по тому, какой экстремум сформирован позже: последний минимум → бычий режим, последний максимум → медвежий.
    • Эти значения сохраняются как стартовые опорные точки для отрисовки линий.
  2. Оценка волатильности
    • Каждая завершённая свеча добавляет свой диапазон в выбранное скользящее среднее.
    • Порог всегда не меньше одного шага цены, чтобы избежать нулевых значений.
  3. Сопровождение экстремумов
    • На каждом баре стратегия обновляет локальные максимум и минимум, если появляются новые абсолютные значения.
    • При смене тренда соответствующий экстремум фиксируется в качестве пивота и, при активной визуализации, соединяется линией с противоположным экстремумом.
  4. Правила входа
    • В бычьем режиме (_isTrendUp = true) закрытие ниже _highestPrice - threshold вызывает реверс в шорт. Объём равен Volume + |Position|, что одновременно закрывает старую позицию и открывает новую.
    • В медвежьем режиме (_isTrendUp = false) закрытие выше _lowestPrice + threshold зеркально переворачивает позицию в лонг.
  5. Управление стопом
    • Если StopLossMultiplier > 0, цена входа корректируется на threshold * StopLossMultiplier (но не менее трёх шагов). Полученный уровень хранится как виртуальный стоп.
    • При касании стопа минимумом (для лонга) или максимумом (для шорта) позиция немедленно закрывается рыночным ордером.
  6. Служебные действия
    • StartProtection() активирует встроенные механизмы защиты сразу после запуска.
    • Все расчёты выполняются только по завершённым свечам, что соответствует циклу пересчёта советника в MetaTrader.

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

  • Оригинальный советник работает по тикам, тогда как порт задействует завершённые свечи — это стандартный подход при использовании свечных подписок StockSharp.
  • Биржевые ограничения брокера (MODE_STOPLEVEL и т.п.) недоступны, поэтому минимальный стоп задаётся консервативно — три шага цены.
  • Для реверса позиций используется один вызов BuyMarket/SellMarket с объёмом Volume + |Position|, что заменяет цикл закрытия отдельных ордеров из MQL.
  • Визуализация построена на методе DrawLine, но последовательность соединения минимумов и максимумов совпадает с оригинальными трендовыми линиями.

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

  • Убедитесь, что у инструмента задан PriceStep. Если брокер его не предоставляет, стратегия использует значение 1, что может потребовать ручной корректировки.
  • Выбор слишком малого таймфрейма ухудшает качество оценки волатильности. Рекомендуется использовать интервалы, близкие к исходному H4.
  • Параметр StopLossMultiplier можно оставить равным нулю для повторения поведения исходного советника без ограничений риска.
  • Стратегия не содержит целей по прибыли и выходит из рынка только при смене режима или срабатывании защитного стопа.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Volatility breakout strategy that tracks swing extremes and reverses
/// when price breaks beyond an ATR-based volatility band.
/// </summary>
public class SpazmVolatilityBreakoutStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _atrLength;
	private readonly StrategyParam<decimal> _multiplier;

	private decimal _swingHigh;
	private decimal _swingLow;
	private bool _trendUp;
	private bool _initialized;

	public SpazmVolatilityBreakoutStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for analysis.", "General");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "Period for ATR volatility.", "Indicators");

		_multiplier = Param(nameof(Multiplier), 2.0m)
			.SetDisplay("Multiplier", "ATR multiplier for breakout threshold.", "Indicators");
	}

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

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

	public decimal Multiplier
	{
		get => _multiplier.Value;
		set => _multiplier.Value = value;
	}

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

		_swingHigh = 0;
		_swingLow = decimal.MaxValue;
		_trendUp = true;
		_initialized = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var atr = new AverageTrueRange { Length = AtrLength };

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

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

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

		if (atrValue <= 0)
			return;

		var close = candle.ClosePrice;
		var high = candle.HighPrice;
		var low = candle.LowPrice;
		var threshold = atrValue * Multiplier;

		if (!_initialized)
		{
			_swingHigh = high;
			_swingLow = low;
			_initialized = true;
			return;
		}

		if (_trendUp)
		{
			// Track swing high
			if (high > _swingHigh)
				_swingHigh = high;

			// Reversal: price breaks below swing high minus threshold
			if (close < _swingHigh - threshold)
			{
				_trendUp = false;
				_swingLow = low;

				// Enter short on trend reversal
				if (Position > 0)
					SellMarket();
				if (Position == 0)
					SellMarket();
			}
		}
		else
		{
			// Track swing low
			if (low < _swingLow)
				_swingLow = low;

			// Reversal: price breaks above swing low plus threshold
			if (close > _swingLow + threshold)
			{
				_trendUp = true;
				_swingHigh = high;

				// Enter long on trend reversal
				if (Position < 0)
					BuyMarket();
				if (Position == 0)
					BuyMarket();
			}
		}
	}
}