Открыть на GitHub

Стратегия Color Schaff JCCX Trend Cycle MMRec Duplex

Обзор

  • Переносит двунаправленного эксперта "ColorSchaffJCCXTrendCycle_MMRec_Duplex" из MetaTrader в инфраструктуру StockSharp.
  • Использует две независимые цепочки индикатора Schaff Trend Cycle на основе скользящих средних Джурика для поиска бычьих и медвежьих разворотов.
  • Реализует упрощённый блок MMRec, уменьшающий объём после серии убыточных сделок.
  • Позволяет настраивать раздельные параметры для длинной и короткой частей, включая таймфреймы и источник цены.

Схема индикаторов

  1. Аппроксимация JCCX – цена фильтруется скользящей средней Джурика, после чего разность с исходной ценой и её модуль сглаживаются ещё раз, формируя аналог исходного осциллятора JCCX.
  2. Слой MACD – разница между быстрым и медленным JCCX задаёт базовый импульс.
  3. Двойное стохастическое преобразование – скользящие окна минимума/максимума нормализуют импульс и формируют окончательное значение Schaff Trend Cycle в диапазоне -100..+100.
  4. Фаза – параметр Phase управляет внутренним коэффициентом сглаживания (0.05–0.95), что имитирует поведение фазового параметра в оригинальном индикаторе.

Полный конвейер рассчитывается отдельно для длинного и короткого блоков, поэтому оба направления могут работать на разных типах свечей и ценах.

Торговая логика

Длинный блок

  • Вход: индикатор пересекает уровень 0 снизу вверх (текущее значение > 0, предыдущее задержанное ≤ 0). Перед открытием длинной позиции закрываются короткие сделки.
  • Выход: индикатор падает ниже 0 при включённой опции закрытия.
  • Стопы: по закрытию каждой свечи проверяются уровни стоп-лосса и тейк-профита (значения указываются в шагах цены).

Короткий блок

  • Вход: индикатор опускается ниже 0 (текущее значение < 0, задержанное ≥ 0). Перед открытием короткой позиции закрываются длинные.
  • Выход: индикатор поднимается выше 0 при разрешённом закрытии.
  • Стопы: симметричная проверка стоп-лосса и тейк-профита для коротких сделок.

Параметр SignalBar задаёт количество уже закрытых свечей, которые пропускаются перед оценкой сигнала. Значение 1 повторяет поведение оригинального эксперта, использующего предыдущую завершённую свечу.

Управление объёмом (MMRec)

  • Для длинной и короткой частей ведутся независимые очереди результатов последних сделок.
  • TotalTrigger ограничивает длину очереди – учитывается только N последних сделок.
  • LossTrigger определяет число убытков в очереди, после которого используется объём SmallVolume.
  • При отсутствии необходимого количества убытков применяется стандартный объём NormalVolume.

Параметры

Группа Параметр Описание По умолчанию
Long LongCandleType Тип свечей для расчёта длинного блока. 8 часов
Long LongFastLength Быстрый период JCCX. 23
Long LongSlowLength Медленный период JCCX. 50
Long LongSmoothLength Период сглаживания числителя и знаменателя. 8
Long LongPhase Фазовый параметр, переводимый в коэффициент сглаживания. 100
Long LongCycle Длина окна для стохастических преобразований. 10
Long LongSignalBar Задержка (в барах) перед оценкой сигнала. 1
Long LongAppliedPrice Источник цены для длинного блока. Close
Long LongAllowOpen / LongAllowClose Разрешение на открытие/закрытие длинных сделок. true
Long LongTotalTrigger Количество последних длинных сделок в очереди MMRec. 5
Long LongLossTrigger Число убытков, переключающее объём на SmallVolume. 3
Long LongSmallVolume / LongNormalVolume Уменьшенный и стандартный объёмы для длинных сделок. 0.01 / 0.1
Long LongStopLoss / LongTakeProfit Стоп-лосс и тейк-профит в шагах цены. 1000 / 2000
Short Аналогичные параметры (с префиксом Short).

Риски и рекомендации

  • Используется значение PriceStep из выбранного инструмента – убедитесь, что оно задано корректно.
  • Стопы проверяются на закрытии свечи; точность исполнения зависит от выбранного таймфрейма.
  • Модуль MMRec оценивает результат по цене закрытия свечи, поэтому в реальной торговле проскальзывание может изменить исход сделки.

Практические советы

  • Начинайте с симметричных параметров длинной и короткой частей, чтобы повторить оригинальный алгоритм, затем экспериментируйте с асимметрией.
  • Для более быстрой реакции уменьшите SignalBar до 0; увеличение значения помогает отфильтровать шум.
  • Оптимизируйте Phase вместе со сглаживанием, чтобы подобрать баланс между скоростью и гладкостью сигналов.
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

public class ColorSchaffJccxTrendCycleMmrecDuplexStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private decimal? _prevFast, _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 ColorSchaffJccxTrendCycleMmrecDuplexStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
		_fastPeriod = Param(nameof(FastPeriod), 12).SetGreaterThanZero().SetDisplay("Fast EMA", "Fast period", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 26).SetGreaterThanZero().SetDisplay("Slow EMA", "Slow period", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(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 fast, decimal slow)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!IsFormedAndOnlineAndAllowTrading()) { _prevFast = fast; _prevSlow = slow; return; }
		if (_prevFast == null || _prevSlow == null) { _prevFast = fast; _prevSlow = slow; return; }
		var prevAbove = _prevFast.Value > _prevSlow.Value;
		var currAbove = fast > slow;
		_prevFast = fast; _prevSlow = slow;
		if (!prevAbove && currAbove && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
		else if (prevAbove && !currAbove && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
	}
}