Открыть на GitHub

Стратегия XCCI Histogram Vol

Общее описание

Стратегия представляет собой перенос эксперта MetaTrader Exp_XCCI_Histogram_Vol на платформу StockSharp. Индикаторная часть повторяет логику кастомного индикатора «XCCI Histogram Vol»: значение CCI умножается на объём, оба ряда сглаживаются выбранным типом скользящей средней и сравниваются с динамическими уровнями. Реализация использует только закрытые свечи и сохраняет двухступенчатую схему входа из оригинального робота.

Работа индикатора

  1. Рассчитать CCI с указанным периодом.
  2. Умножить полученное значение на объём текущей свечи.
  3. Сгладить ряды CCI×Volume и Volume выбранной средней (Simple, Exponential, Smoothed, Weighted, Hull, VolumeWeighted).
  4. Умножить заданные коэффициенты HighLevel2/1 и LowLevel1/2 на сглаженный объём, получив адаптивные уровни.
  5. Определить зону значения CCI×Volume: 0 — экстремальный рост, 1 — рост, 2 — нейтрально, 3 — падение, 4 — экстремальное падение.

Стратегия хранит зону для каждой завершённой свечи. Параметр SignalBarOffset задаёт задержку по числу закрытых свечей перед тем, как использовать зону в торговых решениях (аналог параметра SignalBar в MQL-версии).

Правила торговли

  • Выход из лонга: если оцениваемая зона равна 3 или 4, все длинные позиции закрываются.
  • Выход из шорта: если зона равна 1 или 0, все короткие позиции закрываются.
  • Первичный вход в лонг: текущая зона стала 1, а предыдущая (более старая) зона была больше 1. Это переход из нейтральной/медвежьей области к бычьей. Открывается позиция объёмом PrimaryEntryVolume, при наличии шорта выполняется разворот.
  • Вторичный вход в лонг: текущая зона стала 0, а предыдущая была выше 0, что означает прорыв в экстремально бычью область. Используется объём SecondaryEntryVolume.
  • Первичный вход в шорт: текущая зона стала 3, а предыдущая была ниже 3. Сигнал на появление нового нисходящего импульса, используется PrimaryEntryVolume, перед открытием закрываются лонги.
  • Вторичный вход в шорт: текущая зона стала 4, а предыдущая была ниже 4, что указывает на сильное ускорение вниз. Используется SecondaryEntryVolume.

Когда чистая позиция пересекает ноль, флаги входов сбрасываются — это позволяет имитировать две «магические» позиции из терминала MetaTrader: повторный вход по тому же уровню возможен только после закрытия предыдущего ордера или срабатывания защиты.

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

  • Параметры UseStopLoss и UseTakeProfit включают защитные стопы/тейк-профиты в абсолютных пунктах через метод StartProtection. Они опциональны, как и в оригинале.
  • Все операции выполняются рыночными ордерами, поэтому учитываются глобальные настройки допуска по цене и проскальзыванию в StockSharp.
  • В журнал выводятся сообщения о каждом входе и выходе, что облегчает анализ поведения стратегии.

Параметры

  • CciPeriod – период CCI.
  • MaLength – длина скользящей средней, применяемой к CCI×Volume и объёму.
  • HighLevel2 / HighLevel1 / LowLevel1 / LowLevel2 – коэффициенты, формирующие адаптивные уровни после умножения на сглаженный объём.
  • SignalBarOffset – количество закрытых свечей, через которое используется сигнал (0 – последняя закрытая свеча, 1 – предыдущая и т. д.).
  • Smoothing – тип сглаживания (SMA, EMA, SMMA, WMA, Hull MA, VWMA).
  • AllowLongEntries / AllowShortEntries / AllowLongExits / AllowShortExits – включение/отключение входов и выходов по каждому направлению.
  • PrimaryEntryVolume / SecondaryEntryVolume – объёмы двух ступеней входа, применяются как для покупок, так и для продаж.
  • UseStopLoss / StopLossPoints – опциональный стоп-лосс в пунктах.
  • UseTakeProfit / TakeProfitPoints – опциональный тейк-профит в пунктах.
  • CandleType – тип свечей (таймфрейм), который запрашивается у коннектора.

Отличия от версии MetaTrader

  • Доступны только сглаживания, реализованные в StockSharp; алгоритмы JJMA, JurX, ParMA, VIDYA, AMA и другие специализированные фильтры не реализованы.
  • Объём берётся из ICandleMessage.TotalVolume. Тиковый объём не моделируется, поэтому на площадках, где доступно только количество сделок, результаты могут отличаться от терминала MetaTrader.
  • StockSharp использует неттинговую модель (одна позиция), а не несколько ордеров с разными magic number. Логика двух ступеней реализована через внутренние флаги и соответствует требованиям платформы.
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>
/// XCCI Histogram Vol strategy. Uses CCI zero-line crossover.
/// </summary>
public class XcciHistogramVolStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _cciPeriod;

	private decimal? _prevCci;

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

	public int CciPeriod
	{
		get => _cciPeriod.Value;
		set => _cciPeriod.Value = value;
	}

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

		_cciPeriod = Param(nameof(CciPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("CCI Period", "CCI lookback", "Indicators");
	}

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

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

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevCci = null;
		var cci = new CommodityChannelIndex { Length = CciPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(cci, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); var cciArea = CreateChartArea(); if (cciArea != null) DrawIndicator(cciArea, cci); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal cciVal)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!IsFormedAndOnlineAndAllowTrading()) { _prevCci = cciVal; return; }
		if (_prevCci == null) { _prevCci = cciVal; return; }
		if (_prevCci.Value < 0m && cciVal >= 0m && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
		else if (_prevCci.Value > 0m && cciVal <= 0m && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
		_prevCci = cciVal;
	}
}