Открыть на GitHub

VmMatrix Double Zero

Обзор

VmMatrix Double Zero — порт советника MetaTrader 4 vMATRIXDoubleZero для StockSharp. Исходная система ищет пробои «двойного нуля»: предыдущий закрытый бар округляется до двух знаков после запятой, и сделка открывается, когда цена пересекает это округлённое значение. В портированной версии сохранена многоуровневая структура фильтров: настраиваемые сравнения отклонений нескольких баров, опциональные проверки объёма и диапазона, фильтр ускорения ATR и дополнительный индикатор силы свинга. Стратегия может требовать подтверждения направления от дневного CCI и поддерживает адаптивный расчёт тейк-профита на основе часовых ATR.

Торговля ограничена пользовательским временным окном, заданным в часах терминала. Для длинных и коротких сигналов есть отдельные переключатели. Стопы и цели управляются внутри стратегии, включая приближение оригинального трейлинг-стопа: при его активации тейк-профит автоматически отодвигается дальше, чтобы позиция могла сопровождаться по стопу.

Логика стратегии

Определение направления

  • Округлённый пробой — базовый сигнал сравнивает закрытия двух последних завершённых свечей с предыдущим закрытием, округлённым до двух знаков. Для входа в лонг необходимо, чтобы Close[2] < round(Close[1], 2) и одновременно Close[1] > round(Close[1], 2); для шорта условия зеркальные.
  • Матрица фильтров (опционально) — при активном фильтре используются шесть исторических баров, задаваемых параметрами LongK1…LongK6 (для покупок) или ShortK1…ShortK6 (для продаж). Каждый бар оценивается через отклонение Close - (High + Low) / 2. Требуется, чтобы первое отклонение превышало второе, третье — масштабированное четвёртое (LongQc/ShortQc), а пятое — второе масштабированное шестое (LongQg/ShortQg).

Дополнительные фильтры

  • Торговая сессия — сигналы обрабатываются только тогда, когда час закрытия свечи попадает в диапазон StartHourEndHour.
  • Объём — если фильтр включён, объём предыдущей свечи должен превышать MinimumVolume.
  • Сжатие диапазона — максимум и минимум последних RangeBars свечей обязаны лежать в пределах RangeThresholdPips пунктов.
  • Ускорение ATR — текущий ATR (период AtrPeriod на рабочем таймфрейме) сравнивается со значением AtrShift баров назад. Сигнал допускается только если ATR вырос, что повторяет поведение переключателя VSA в оригинале.
  • Вторичный фильтр свингов — при включении вычисляется взвешенная сумма разниц максимумов и минимумов с отступом SecondaryPivot. Для лонга сумма должна быть положительной, для шорта — отрицательной. Весовые параметры (Xb2, Xs2, Yb2, Ys2) интерпретируются так же, как в советнике: значение 50 означает нейтральность.
  • Подтверждение дневным CCI — опциональный барьер, требующий положительного значения дневного CCI (DailyCciPeriod) для покупок и отрицательного для продаж.

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

  • Размер входа — сделки открываются объёмом OrderVolume, нормированным по шагу объёма инструмента. Если уже открыта противоположная позиция и активен флаг CloseOnBiasFlip, стратегия сначала закрывает её (неттинг не позволяет держать разнонаправленные позиции одновременно). Иначе сигнал пропускается.
  • Изначальные уровни — стоп-лосс и тейк-профит задаются в пунктах через LongStopLossPips/ShortStopLossPips и LongTakeProfitPips/ShortTakeProfitPips. Значения автоматически переводятся в цену с учётом размера пункта. При необходимости к тейк-профиту добавляется динамический компонент.
  • Динамический тейк-профит — если включён UseDynamicTakeProfit, к базовой дистанции прибавляется взвешенная сумма часовых ATR: изменение ATR(1), текущее ATR(1), ATR(25) и разница между хайями, разделёнными SwingPivot барами. Все веса (WeightSn1…WeightSn4) отсчитываются от 50, как в оригинальной функции TPb().
  • Трейлинг-стоп — параметр UseTrailingStop включает ступенчатое сопровождение: стоп сдвигается, когда цена проходит примерно вдвое большее расстояние, чем текущий стоп. Одновременно тейк-профит умножается на 10, что имитирует приём автора советника и оставляет главную роль за трейлингом.
  • Защитные выходы — на каждой завершённой свече проверяется пробой стоп-лосса или тейк-профита. Позиция закрывается рыночным приказом. Если включён CloseOnBiasFlip, противоположный сигнал также приводит к немедленному закрытию.

Параметры

Ниже приведены основные настройки стратегии:

Группа Параметр Описание
General StartHour / EndHour Часовой диапазон работы (по времени терминала).
General OrderVolume Базовый объём сделки с учётом шага объёма инструмента.
General UseTrailingStop Включает приблизительную реализацию трейлинг-стопа и растягивает тейк-профит.
General CloseOnBiasFlip При включении закрывает противоположную позицию перед разворотом.
Long / Short EnableLongs / EnableShorts Разрешение обработки лонговых или шортовых сигналов.
Long / Short LongStopLossPips, LongTakeProfitPips, ShortStopLossPips, ShortTakeProfitPips Дистанции стопов и тейков в пунктах.
Filters UseBiasFilter и параметры LongK1…LongK6, ShortK1…ShortK6, LongQc, LongQg, ShortQc, ShortQg Настройка матричных сравнений отклонений.
Filters UseRangeFilter, RangeBars, RangeThresholdPips Ограничение сигналов при слишком широком диапазоне.
Filters UseVolumeFilter, MinimumVolume Требование минимального объёма предыдущей свечи.
Filters UseVsaFilter, AtrPeriod, AtrShift Проверка роста ATR относительно значения AtrShift баров назад.
Filters UseSecondaryFilter, Xb2, Xs2, Yb2, Ys2, SecondaryPivot Дополнительный фильтр силы свинга по максимумам/минимумам.
Filters UseDailyCciFilter, DailyCciPeriod Дневной CCI должен подтверждать направление сделки.
Take Profit UseDynamicTakeProfit, WeightSn1…WeightSn4, SwingPivot Управление адаптивной надбавкой к тейк-профиту.
General CandleType Основной таймфрейм, на котором строятся расчёты.

Дополнительные замечания

  • Размер пункта вычисляется по Security.PriceStep. Для инструментов с тремя и пятью десятичными знаками автоматически применяется множитель 10, как в логике MQL (Digits/Point).
  • Стратегия подписывается на три потока данных: рабочие свечи, часовые (для ATR) и дневные (для CCI). Убедитесь, что источник данных поддерживает все необходимые таймфреймы.
  • Порт работает в режиме неттинга, поэтому хеджирование одного инструмента в разных направлениях невозможно. Для быстрого разворота включайте CloseOnBiasFlip.
  • Реализация трейлинг-стопа приблизительная: оригинал опирался на текущий спред. Здесь стоп подтягивается после движения примерно на двойное расстояние стопа, что даёт схожий эффект без необходимости знать фактический спред.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// VmMatrix DoubleZero: Dual EMA crossover with RSI confirmation and ATR stops.
/// </summary>
public class VmMatrixDoubleZeroStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastEmaLength;
	private readonly StrategyParam<int> _slowEmaLength;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;

	public VmMatrixDoubleZeroStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");
		_fastEmaLength = Param(nameof(FastEmaLength), 12)
			.SetDisplay("Fast EMA Length", "Fast EMA period.", "Indicators");
		_slowEmaLength = Param(nameof(SlowEmaLength), 26)
			.SetDisplay("Slow EMA Length", "Slow EMA period.", "Indicators");
		_rsiLength = Param(nameof(RsiLength), 14)
			.SetDisplay("RSI Length", "RSI period.", "Indicators");
		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period.", "Indicators");
	}

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int FastEmaLength { get => _fastEmaLength.Value; set => _fastEmaLength.Value = value; }
	public int SlowEmaLength { get => _slowEmaLength.Value; set => _slowEmaLength.Value = value; }
	public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
	public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }

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

		_prevFast = 0; _prevSlow = 0; _entryPrice = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0;
		var fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
		var slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };
		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var atr = new AverageTrueRange { Length = AtrLength };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fastEma, slowEma, rsi, atr, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fastEma); DrawIndicator(area, slowEma); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal rsiVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished) return;
		if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0) { _prevFast = fastVal; _prevSlow = slowVal; return; }
		var close = candle.ClosePrice;

		if (Position > 0)
		{
			if ((fastVal < slowVal && _prevFast >= _prevSlow) || close <= _entryPrice - atrVal * 2m) { SellMarket(); _entryPrice = 0; }
		}
		else if (Position < 0)
		{
			if ((fastVal > slowVal && _prevFast <= _prevSlow) || close >= _entryPrice + atrVal * 2m) { BuyMarket(); _entryPrice = 0; }
		}

		if (Position == 0)
		{
			if (fastVal > slowVal && _prevFast <= _prevSlow && rsiVal > 50) { _entryPrice = close; BuyMarket(); }
			else if (fastVal < slowVal && _prevFast >= _prevSlow && rsiVal < 50) { _entryPrice = close; SellMarket(); }
		}
		_prevFast = fastVal; _prevSlow = slowVal;
	}
}