Открыть на GitHub

Стратегия Exp Trading Channel Index

Обзор

Стратегия представляет собой портирование советника MQL5 Exp_Trading_Channel_Index на платформу StockSharp. Она использует осциллятор Trading Channel Index (TCI), который измеряет положение цены относительно сглаженного канала и окрашивает бары в пять цветов. Сделки совершаются при изменении цвета на указанном историческом баре, что полностью повторяет оригинальную логику.

Работа ведётся по выбранному таймфрейму свечей (по умолчанию H4). Обрабатываются только завершённые свечи, а все торговые решения принимаются на открытии следующей свечи после фиксации цветового перехода.

Индикатор Trading Channel Index

TCI рассчитывается в три этапа:

  1. Первичное сглаживание выбранной цены с помощью настраиваемого скользящего среднего (SMA, EMA, SMMA, WMA или Jurik), формирующего базовую линию XMA.
  2. Оценка волатильности через сглаживание абсолютного отклонения цены от базовой линии.
  3. Нормализация отклонения с учётом коэффициента и вторичного сглаживания. Полученный показатель сравнивается с уровнями HighLevel и LowLevel, после чего бар получает цветовой код:
    • 0 – значение выше HighLevel.
    • 1 – значение положительное, но ниже HighLevel.
    • 2 – значение близко к нулю.
    • 3 – значение отрицательное, но выше LowLevel.
    • 4 – значение ниже LowLevel.

В реализации StockSharp используются штатные классы индикаторов. Для Jurik Moving Average учитывается параметр Phase, для остальных методов он игнорируется, как и в оригинале.

Правила входа и выхода

Стратегия анализирует бар с номером SignalBar (по умолчанию последний закрытый) и предыдущий бар:

  • Открытие покупки: два бара назад (SignalBar + 1) имели цвет 0, а последний бар — другой цвет. При наличии короткой позиции она закрывается, затем открывается длинная позиция объёмом TradeVolume.
  • Открытие продажи: два бара назад имели цвет 4, а последний бар — другой цвет. При наличии длинной позиции она закрывается, после чего открывается короткая.
  • Закрытие покупки: выполняется, когда старший бар (два бара назад) окрашен в 4.
  • Закрытие продажи: выполняется, когда старший бар окрашен в 0.

Сначала выполняются условия выхода, затем условия входа. Перед открытием сделки противоположная позиция закрывается, что повторяет логику вспомогательных функций TradeAlgorithms.mqh.

Управление рисками

Стоп-лосс и тейк-профит задаются в шагах цены:

  • StopLossPoints определяет расстояние от цены входа до стоп-лосса (ниже для покупок и выше для продаж).
  • TakeProfitPoints задаёт расстояние до тейк-профита.

Проверка защитных уровней выполняется на каждой завершённой свече. При одновременном достижении обоих уровней закрытие происходит по первому сработавшему условию.

Параметры

  • Trade Volume (TradeVolume) — объём заявки.
  • Stop Loss (pts) (StopLossPoints) — стоп-лосс в шагах цены.
  • Take Profit (pts) (TakeProfitPoints) — тейк-профит в шагах цены.
  • Enable Long Entries/Exits (BuyPositionOpen, BuyPositionClose) — включение сигналов на покупку и их закрытие.
  • Enable Short Entries/Exits (SellPositionOpen, SellPositionClose) — включение сигналов на продажу и их закрытие.
  • Signal Bar (SignalBar) — сдвиг по истории, на котором отслеживается смена цвета.
  • High Level / Low Level (HighLevel, LowLevel) — уровни для окраски индикатора.
  • Primary / Secondary Method (Method1, Method2) — типы скользящих средних для каждого этапа сглаживания.
  • Length #1 / Length #2 (Length1, Length2) — периоды сглаживания.
  • Phase #1 / Phase #2 (Phase1, Phase2) — параметр фазового сдвига для Jurik MA.
  • Coefficient (Coefficient) — коэффициент нормализации отклонения.
  • Applied Price (AppliedPrice) — источник цены (close, open, high, low, median, typical, weighted, simple, quarter, trend-follow, trend-follow average, Demark).
  • Candle Type (CandleType) — используемый таймфрейм свечей.

Примечания

  • Python-версия намеренно не создавалась согласно требованию.
  • Код следует требованию табуляции и снабжён комментариями на английском языке.
  • Индикатор возвращает как числовое значение TCI, так и цветовой индекс, что позволяет при необходимости построить собственную визуализацию.
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>
/// Trading Channel Index strategy. Uses Highest/Lowest channel breakout.
/// </summary>
public class ExpTradingChannelIndexStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _period;

	private decimal? _prevHigh;
	private decimal? _prevLow;

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

	public int Period
	{
		get => _period.Value;
		set => _period.Value = value;
	}

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

		_period = Param(nameof(Period), 20)
			.SetGreaterThanZero()
			.SetDisplay("Period", "Channel period", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevHigh = null;
		_prevLow = null;
	}

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

		_prevHigh = null;
		_prevLow = null;

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

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

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevHigh = high;
			_prevLow = low;
			return;
		}

		if (_prevHigh == null || _prevLow == null)
		{
			_prevHigh = high;
			_prevLow = low;
			return;
		}

		var close = candle.ClosePrice;

		// Break above previous high → buy
		if (close > _prevHigh.Value && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Break below previous low → sell
		else if (close < _prevLow.Value && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevHigh = high;
		_prevLow = low;
	}
}