Открыть на GitHub

Стратегия KWAN CCC

Обзор

Стратегия KWAN CCC повторяет работу MetaTrader-советника Exp_KWAN_CCC.mq5, используя высокоуровневый API StockSharp. Торговые сигналы формируются на основе пользовательского осциллятора:

  1. Рассчитывается осциллятор Chaikin (разница между быстрой и медленной скользящими средними линии накопления/распределения).
  2. Значение Chaikin умножается на Commodity Channel Index (CCI).
  3. Результат делится на значение Momentum. При нулевом Momentum используется постоянное значение 100, как и в оригинале, чтобы избежать деления на ноль.
  4. Полученный ряд сглаживается выбранным методом XMA.
  5. Определяется наклон сглаженного ряда. Растущие бары помечаются кодом 0, падающие — 2, прочие — 1.

Когда цвет меняется с 0 на любое другое значение, стратегия закрывает шорт и открывает лонг. Когда цвет меняется с 2 на любое другое значение, закрывается лонг и открывается шорт. Логика полностью соответствует MQL-версии и поддерживает смещение сигнала (SignalBar).

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

  • Вход в лонг: цвет бара на SignalBar + 1 равен 0, а цвет бара на SignalBar отличается от 0.
  • Вход в шорт: цвет бара на SignalBar + 1 равен 2, а цвет бара на SignalBar отличается от 2.
  • Выход из лонга: выполняется, если EnableLongExits = true и выполняется условие входа в шорт.
  • Выход из шорта: выполняется, если EnableShortExits = true и выполняется условие входа в лонг.
  • Защитные стопы и цели создаются через StartProtection с абсолютными смещениями цены, вычисляемыми как произведение StopLossPoints/TakeProfitPoints на PriceStep инструмента.

Параметры

Параметр Описание
OrderVolume Базовый объем заявки при открытии позиции.
CandleType Таймфрейм для расчета индикаторов (по умолчанию 1 час).
FastPeriod / SlowPeriod Длины скользящих средних внутри осциллятора Chaikin.
ChaikinMethod Тип скользящей средней (SMA, EMA, SMMA, WMA) для линии накопления/распределения.
CciPeriod Период индикатора CCI.
MomentumPeriod Период индикатора Momentum.
SmoothingMethod Метод сглаживания XMA. Значения JurX, Parabolic, T3 отображаются на Jurik MA; Vidya использует адаптивное сглаживание на базе CMO; Adaptive использует Kaufman AMA.
SmoothingLength Количество баров для выбранного сглаживания.
SmoothingPhase Дополнительный параметр для отдельных методов (например, период CMO для VIDYA или медленный период для AMA).
SignalBar Смещение (в закрытых барах) при анализе смены цвета. Значение 1 соответствует настройке MetaTrader.
EnableLongEntries / EnableShortEntries Разрешение на открытие длинных/коротких позиций.
EnableLongExits / EnableShortExits Разрешение на закрытие позиций по сигналу индикатора.
StopLossPoints / TakeProfitPoints Размеры стоп-лосса и тейк-профита в шагах цены (0 — без защиты).

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

  • Стратегия обрабатывает только завершенные свечи и использует Bind для потокового заполнения индикаторов.
  • Список методов сглаживания повторяет XMA из оригинальной библиотеки. Недоступные варианты заменены близкими аналогами (см. таблицу параметров).
  • Параметр MetaTrader VolumeType не используется, поскольку свечи StockSharp уже содержат нужные данные объема для линии накопления/распределения.
  • Денежный менеджмент оригинала строился на пользовательских функциях. В конверсии используется фиксированный объем OrderVolume.

Рекомендации по применению

  • Используйте инструменты с информативным объемом, чтобы Chaikin вел себя корректно. Для низколиквидных активов можно увеличить MomentumPeriod для фильтрации шума.
  • При оптимизации сглаживания сочетайте SmoothingLength и SmoothingPhase аккуратно: экстремальные комбинации сильно задерживают сигналы.
  • Значения по умолчанию (StopLossPoints = 1000, TakeProfitPoints = 2000) соответствуют крупным смещениям. Настройте их под шаг цены выбранного инструмента.
namespace StockSharp.Samples.Strategies;

using System;

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

/// <summary>
/// Strategy converted from the KWAN_CCC expert advisor.
/// Uses CCI and Momentum to detect trend transitions.
/// Enters long when CCI turns up while momentum is positive,
/// enters short when CCI turns down while momentum is negative.
/// </summary>
public class KwanCccStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _cciPeriod;

	private decimal _prevCci;
	private decimal _prevClose;
	private bool _initialized;

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

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

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

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevCci = 0m;
		_prevClose = 0m;
		_initialized = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevCci = 0m;
		_prevClose = 0m;
		_initialized = false;

		var cci = new CommodityChannelIndex { Length = CciPeriod };

		var subscription = SubscribeCandles(CandleType);

		subscription
			.Bind(cci, (ICandleMessage candle, decimal cciValue) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!IsFormedAndOnlineAndAllowTrading())
					return;

				if (!_initialized)
				{
					_prevCci = cciValue;
					_prevClose = candle.ClosePrice;
					_initialized = true;
					return;
				}

				var closeUp = candle.ClosePrice > _prevClose;
				var closeDown = candle.ClosePrice < _prevClose;

				// Buy when CCI crosses into positive territory and price confirms the move.
				if (_prevCci <= 0m && cciValue > 0m && closeUp && Position <= 0)
				{
					BuyMarket();
				}
				// Sell when CCI crosses into negative territory and price confirms the move.
				else if (_prevCci >= 0m && cciValue < 0m && closeDown && Position >= 0)
				{
					SellMarket();
				}

				_prevCci = cciValue;
				_prevClose = candle.ClosePrice;
			})
			.Start();

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