Открыть на GitHub

Стратегия Exp Trend Intensity Index

Стратегия представляет собой порт MetaTrader-советника Exp_Trend_Intensity_Index на платформу StockSharp. Торговля ведётся по закрытым свечам выбранного таймфрейма. Индикатор Trend Intensity Index (TII) отслеживает моменты, когда импульс выходит из экстремальных бычьих или медвежьих зон: при выходе из верхней зоны стратегия закрывает шорты и может открыть лонг, при выходе из нижней зоны — закрывает лонги и может открыть шорт.

Расчёт индикатора

  1. Выбирается источник цены (AppliedPrice — close, open, медианные и т.д.).
  2. Поток цен сглаживается первой скользящей средней (PriceMaMethod, PriceMaLength).
  3. Разница между ценой и сглаженным значением разделяется на положительный и отрицательный потоки.
  4. Оба потока сглаживаются второй скользящей средней (SmoothingMethod, SmoothingLength).
  5. Рассчитывается TII: 100 * Positive / (Positive + Negative).
  6. Значение сравнивается с уровнями HighLevel и LowLevel, что задаёт цветовое состояние: верхняя зона (0), нейтральная (1), нижняя зона (2).

В реализации используются стандартные индикаторы StockSharp (простая, экспоненциальная, сглаженная, взвешенная средние). Экзотические методы из MQL-библиотеки не поддерживаются.

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

  • Сигналы обрабатываются только после полного закрытия свечи (CandleStates.Finished).
  • Параметр SignalBar задаёт, какую завершённую свечу анализировать (по умолчанию предыдущую), дополнительно читается ещё более старая свеча — так воспроизводится работа MQL с буфером цвета.
  • Если более старая свеча относится к верхней зоне (color == 0):
    • При включённом EnableSellExits закрываются короткие позиции.
    • Если более свежая свеча покинула верхнюю зону и разрешён EnableBuyEntries, открывается/переворачивается лонг.
  • Если более старая свеча относится к нижней зоне (color == 2):
    • При включённом EnableBuyExits закрываются длинные позиции.
    • Если свежая свеча покинула нижнюю зону и разрешён EnableSellEntries, открывается/переворачивается шорт.
  • Заявки выставляются методами BuyMarket и SellMarket. При развороте учитывается текущий объём позиции плюс значение свойства Volume.
  • Необязательные защитные уровни в ценовых пунктах задаются параметрами StopLossPoints и TakeProfitPoints и подключаются через StartProtection.

Параметры

Параметр Описание
CandleType Таймфрейм свечей, по которым рассчитывается индикатор и принимаются решения.
PriceMaMethod, PriceMaLength Тип и период первой скользящей средней.
SmoothingMethod, SmoothingLength Тип и период сглаживания положительного и отрицательного потоков.
AppliedPrice Источник цены для расчёта (close, open, median, trend-follow, Demark и др.).
HighLevel, LowLevel Пороговые уровни, определяющие бычью и медвежью зоны.
SignalBar Количество завершённых свечей для анализа сигнала.
EnableBuyEntries, EnableSellEntries Разрешение на открытие длинных/коротких позиций.
EnableBuyExits, EnableSellExits Разрешение на автоматическое закрытие позиций при смене зоны индикатора.
StopLossPoints, TakeProfitPoints Опциональные расстояния до стоп-лосса и тейк-профита (в ценовых единицах).

Отличия от оригинального эксперта

  • Параметры управления капиталом (MM, MMMode, Deviation) заменены стандартным параметром Volume и рыночными ордерами StockSharp; контроль проскальзывания не реализован.
  • Доступны только типы скользящих средних, имеющиеся в StockSharp (SMA, EMA, SMMA, WMA).
  • Параметры фазы из MQL-версии опущены — у используемых индикаторов нет аналогичных настроек.
  • Сделки исполняются сразу после подтверждения сигнала на закрытой свече без отложенного запуска на следующем баре.

Такой подход сохраняет основную идею стратегии и соответствует рекомендациям по использованию высокоуровневого API 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>
/// Trend Intensity Index strategy. Uses EMA crossover for trend following.
/// </summary>
public class ExpTrendIntensityIndexStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;

	private decimal? _prevFast;
	private decimal? _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 ExpTrendIntensityIndexStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

		_fastPeriod = Param(nameof(FastPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 30)
			.SetGreaterThanZero()
			.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(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 ExponentialMovingAverage { Length = FastPeriod };
		var slow = new ExponentialMovingAverage { 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 fastVal, decimal slowVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevFast = fastVal;
			_prevSlow = slowVal;
			return;
		}

		if (_prevFast == null || _prevSlow == null)
		{
			_prevFast = fastVal;
			_prevSlow = slowVal;
			return;
		}

		var prevAbove = _prevFast.Value > _prevSlow.Value;
		var currAbove = fastVal > slowVal;

		_prevFast = fastVal;
		_prevSlow = slowVal;

		if (!prevAbove && currAbove && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (prevAbove && !currAbove && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}
	}
}