Открыть на GitHub

Стратегия CCIT3 Zero Cross

Обзор

CCIT3 Zero Cross — порт стратегии MetaTrader 5, которая торгует развороты осциллятора CCIT3 относительно нулевой линии. Индикатор строится путём применения цепочки сглаживания Tillson T3 к индексу товарного канала (CCI). Когда сглаженное значение меняет знак, стратегия открывает новую позицию по направлению разворота либо, при включённой опции реверса, закрывает текущую и открывает противоположную.

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

  • Рассчитывается CCI с заданным периодом и выбранным типом цены.
  • Осциллятор сглаживается фильтром Tillson T3. Доступны два режима вычисления:
    • Simple — классическое шестиступенчатое сглаживание с накоплением состояния, полностью повторяющее оригинальный индикатор с пересчётом истории.
    • NoRecalc — вычисляет полином T3 только для последнего бара, что соответствует облегчённой версии «без пересчёта» из исходного MQL-кода.
  • Переход CCIT3 из положительной области в отрицательную открывает длинную позицию (или переворачивает короткую при активном Trade Overturn).
  • Переход CCIT3 из отрицательной области в положительную открывает короткую позицию (или переворачивает длинную при активном Trade Overturn).
  • Тейк-профит, стоп-лосс и трейлинг-стоп управляются через помощник StartProtection платформы StockSharp.

Индикаторы и расчёты

  • CCI — использует настраиваемый тип цены (close, open, high, low, median, typical, weighted) и период.
  • Tillson T3 — реализован по формуле оригинального индикатора с коэффициентом B. В режиме Simple цепочка EMA хранит состояние между барами, в режиме NoRecalc полином пересчитывается только по текущему значению CCI.
  • Контроль пересечения нуля — сигналы формируются только на закрытых свечах, как и в исходном советнике.

Управление рисками и позицией

  • Параметры Take Profit (pts) и Stop Loss (pts) переводятся в абсолютные цены с учётом PriceStep инструмента.
  • Trailing Stop (pts) активирует встроенный трейлинг с указанной дистанцией.
  • Max Drawdown Target масштабирует базовый объём заявки пропорционально текущей или начальной стоимости портфеля (volume = OrderVolume * balance / target). Значение 0 отключает адаптивное масштабирование.
  • Trade Overturn включает полный реверс: текущая позиция закрывается перед открытием противоположной.

Параметры

Имя По умолчанию Описание
Volume 1 Базовый объём заявки до учёта ограничения просадки.
Take Profit (pts) 1750 Дистанция тейк-профита в пунктах.
Stop Loss (pts) 0 Дистанция стоп-лосса в пунктах.
Trailing Stop (pts) 0 Дистанция трейлинг-стопа в пунктах (0 — без трейлинга).
Trade Overturn false Разворот позиции при противоположном сигнале CCIT3.
CCI Period 285 Период расчёта индекса товарного канала.
CCI Price Typical Тип цены, подаваемой на вход CCI.
T3 Period 60 Длина сглаживания Tillson T3.
T3 Volume Factor 0.618 Коэффициент B фильтра Tillson T3.
Mode Simple Режим расчёта CCIT3 (Simple или NoRecalc).
Candle Type ТФ 1 час Тип свечей, используемый в расчётах.
Max Drawdown Target 0 Делитель баланса для адаптивного объёма (0 — без масштабирования).

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

  • Подписка ведётся на одну свечную серию, заданную параметром Candle Type, и обрабатываются только завершённые свечи.
  • Объём заявок нормируется по шагу объёма инструмента и ограничивается VolumeMin/VolumeMax.
  • Настройки по умолчанию повторяют опубликованную конфигурацию MT5: режим Simple, период CCI 285, T3 60 и коэффициент 0.618.
  • Режим NoRecalc сохраняет реакцию на знак «сырого» CCI, но всё равно возвращает положительные/отрицательные значения для определения направления.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// CCIT3 Zero Cross: EMA crossover with RSI filter and ATR stops.
/// </summary>
public class Ccit3ZeroCrossStrategy : 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 Ccit3ZeroCrossStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");
		_fastEmaLength = Param(nameof(FastEmaLength), 9)
			.SetDisplay("Fast EMA Length", "Fast EMA period.", "Indicators");
		_slowEmaLength = Param(nameof(SlowEmaLength), 21)
			.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;
	}
}