Открыть на GitHub

Стратегия Blau TVI Timed Reversal

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

  • Перенос с MetaTrader 5 эксперта Exp_BlauTVI_Tm.mq5 из каталога MQL/21014.
  • Полностью повторяет вычисление индикатора Blau Tick Volume Index с тремя последовательными этапами сглаживания.
  • Формирует разворотные сигналы, когда сглаженный TVI меняет направление, и может ограничивать открытие сделок заданным торговым окном.
  • Поддерживает защиту по фиксированному стоп-лоссу и тейк-профиту в пунктах цены.

Алгоритм Blau TVI

Оригинальный эксперт использует ресурс BlauTVI.ex5. В версии StockSharp выполняются следующие шаги:

  1. Оценка количества тиков вверх/вниз
    • UpTicks = (Volume + (Close - Open) / PriceStep) / 2
    • DownTicks = Volume - UpTicks
    • Поскольку агрегированные свечи не содержат tick-volume, используется суммарный объём свечи как приближение.
  2. Сглаживание 1-го уровня — отдельные средние для UpTicks и DownTicks с длиной Length1 и выбранным типом MA (EMA, SMA, SMMA, WMA, JMA).
  3. Сглаживание 2-го уровня — второе сглаживание тех же серий длиной Length2.
  4. Расчёт TVITVI = 100 * (Up2 - Down2) / (Up2 + Down2).
  5. Сглаживание 3-го уровня — финальная фильтрация TVI длиной Length3.

Для эмуляции SignalBar хранится короткая история последних значений TVI, что соответствует вызову CopyBuffer с отступом в исходном коде.

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

  • Определение наклона
    • Если значение TVI на свече SignalBar + 1 ниже, чем на SignalBar + 2, и при этом наиболее свежий TVI превышает предыдущий, фиксируется смена наклона вверх (бычий сигнал).
    • Если значение TVI на SignalBar + 1 выше, чем на SignalBar + 2, и последнее значение ниже предыдущего, это смена наклона вниз (медвежий сигнал).
  • Управление позициями
    • При EnableBuyOpen = true и бычьем сигнале стратегия открывает длинную позицию, предварительно закрывая существующий шорт (увеличивает объём заявки на величину обратной позиции).
    • При EnableSellOpen = true и медвежьем сигнале открывается шорт с аналогичным учётом длинной позиции.
    • EnableBuyClose = true — закрытие лонга при смене наклона вниз; EnableSellClose = true — закрытие шорта при смене наклона вверх.
  • Временной фильтр
    • EnableTimeFilter = true ограничивает открытия диапазоном [StartHour:StartMinute, EndHour:EndMinute]. Если начало больше конца, окно трактуется как «через ночь».
    • Выход из позиции выполняется сразу после выхода текущего времени за пределы окна.
  • Защитные ордера
    • StopLossPoints и TakeProfitPoints переводятся в абсолютное расстояние умножением на PriceStep и передаются в StartProtection. Ноль отключает соответствующий барьер.

Параметры

Параметр Описание
Volume Базовый объём сделки; при развороте добавляется величина текущей позиции противоположного знака.
CandleType Тип/таймфрейм свечей для расчётов (по умолчанию H4).
MaType Тип средних для всех этапов (EMA, SMA, SMMA, WMA, JMA).
Length1, Length2, Length3 Длины трёх последовательных сглаживаний.
SignalBar Сдвиг по истории TVI: 1 означает использование предыдущей завершённой свечи.
EnableBuyOpen, EnableSellOpen Разрешение открывать лонги/шорты.
EnableBuyClose, EnableSellClose Разрешение закрывать лонги/шорты при смене наклона.
EnableTimeFilter Включение торгового окна.
StartHour, StartMinute, EndHour, EndMinute Границы торговой сессии в локальном времени.
StopLossPoints, TakeProfitPoints Фиксированные расстояния стоп-лосса и тейк-профита в пунктах цены.

Особенности реализации

  • Вместо tick-volume применяется объём свечи, что максимально приближает расчёт к оригиналу при ограничениях StockSharp.
  • Для соблюдения требований репозитория хранится только небольшой список последних значений TVI.
  • StartProtection активируется только при известном шаге цены; иначе защита запускается без фиксированных целей.
  • Комментарии переведены на английский язык, отступы выполнены табуляцией в соответствии с AGENTS.md.

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

  1. Используйте значения по умолчанию (H4 + EMA), чтобы получить поведение, идентичное исходному советнику.
  2. Изменение SignalBar на 0 позволяет реагировать на последнюю свечу сразу после закрытия, но приводит к расхождению с логикой MT5.
  3. Для инструментов с разрывами в торговой сессии настройте временной фильтр, чтобы избежать сделок в периоды низкой ликвидности.
  4. Стратегия работает с фиксированным объёмом; если необходим динамический мани-менеджмент, подключайте его на уровне портфеля.
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Blau TVI timed reversal strategy (simplified). Uses Momentum indicator
/// to detect slope changes and generate reversal entries.
/// </summary>
public class BlauTviTimedReversalStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _momentumLength;

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

	public int MomentumLength
	{
		get => _momentumLength.Value;
		set => _momentumLength.Value = value;
	}

	public BlauTviTimedReversalStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candles", "General");

		_momentumLength = Param(nameof(MomentumLength), 12)
			.SetGreaterThanZero()
			.SetDisplay("Momentum Length", "Momentum period", "Indicators");
	}

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

		var momentum = new Momentum { Length = MomentumLength };

		decimal prevMom = 0;
		decimal prevPrevMom = 0;
		var count = 0;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(momentum, (ICandleMessage candle, decimal momValue) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				count++;
				if (count < 3)
				{
					prevPrevMom = prevMom;
					prevMom = momValue;
					return;
				}

				if (!IsFormedAndOnlineAndAllowTrading())
				{
					prevPrevMom = prevMom;
					prevMom = momValue;
					return;
				}

				// Detect slope reversal: was falling, now rising => buy
				if (prevMom < prevPrevMom && momValue > prevMom && Position <= 0)
					BuyMarket();
				// Was rising, now falling => sell
				else if (prevMom > prevPrevMom && momValue < prevMom && Position >= 0)
					SellMarket();

				prevPrevMom = prevMom;
				prevMom = momValue;
			})
			.Start();

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