Открыть на GitHub

Стратегия Morning/Evening Star CCI

Обзор

Стратегия повторяет логику советника MetaTrader 5 Expert_AMS_ES_CCI, реализованную на высокоуровневом API StockSharp. Алгоритм ищет разворотные свечные модели «Утренняя звезда» и «Вечерняя звезда» и подтверждает их сигналом индикатора Commodity Channel Index (CCI). Все расчёты выполняются по закрытым свечам базового инструмента.

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

  • Вход в лонг (Morning Star)
    • Последовательно появляется три свечи, образующие «утреннюю звезду»:
      • Свеча 1: длинное чёрное тело (длина превышает средний размер тела за заданный период).
      • Свеча 2: короткое тело с разрывом вниз относительно первой свечи.
      • Свеча 3: закрывается выше середины тела первой свечи.
    • Значение CCI на сигнальной свече ниже отрицательного порога входа (по умолчанию −50).
  • Вход в шорт (Evening Star)
    • Фиксируется модель «вечерняя звезда»:
      • Свеча 1: длинное белое тело.
      • Свеча 2: короткое тело с разрывом вверх относительно первой свечи.
      • Свеча 3: закрывается ниже середины тела первой свечи.
    • Значение CCI на сигнальной свече выше положительного порога входа (по умолчанию +50).
  • Правила выхода
    • Для коротких позиций: закрывать при переходе CCI выше −NeutralThreshold либо ниже +NeutralThreshold (по умолчанию ±80).
    • Для длинных позиций: закрывать при переходе CCI ниже +NeutralThreshold либо при падении ниже −NeutralThreshold.
    • Дополнительных стопов и тейков не задано; при необходимости используйте защиту позиций (StartProtection).

Используемые индикаторы

  • CCI с периодом CciPeriod (по умолчанию 25) — фильтр подтверждения.
  • Простая скользящая средняя размера свечных тел длиной BodyAveragePeriod (по умолчанию 5) для оценки силы модели.

Параметры

Параметр Описание Значение по умолчанию Примечание
CciPeriod Период расчёта CCI. 25 Доступен для оптимизации.
BodyAveragePeriod Окно усреднения длины тел свечей. 5 Доступен для оптимизации.
EntryThreshold Абсолютное значение CCI для открытия позиций. 50 Используется знак ±.
NeutralThreshold Абсолютное значение CCI для фиксации прибыли/убытка. 80 Используется знак ±.
CandleType Тип (таймфрейм) свечей для анализа. Часовые свечи Можно изменить под свой инструмент.

Дополнительные замечания

  • Подписка на свечи выполняется через SubscribeCandles, индикаторы подключаются методом Bind.
  • Сделки исполняются рыночными ордерами BuyMarket и SellMarket в объёме Volume с учётом текущей позиции.
  • Все комментарии в коде оставлены на английском языке.
  • Стратегия не изменяет блок тестов и не требует дополнительных внешних библиотек.
namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Morning/Evening Star + CCI strategy.
/// Buys on morning star with negative CCI, sells on evening star with positive CCI.
/// </summary>
public class MorningEveningStarCciStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _cciPeriod;
	private readonly StrategyParam<decimal> _cciLevel;

	private ICandleMessage _prevCandle;
	private ICandleMessage _prevPrevCandle;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int CciPeriod { get => _cciPeriod.Value; set => _cciPeriod.Value = value; }
	public decimal CciLevel { get => _cciLevel.Value; set => _cciLevel.Value = value; }

	public MorningEveningStarCciStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_cciPeriod = Param(nameof(CciPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("CCI Period", "CCI period", "Indicators");
		_cciLevel = Param(nameof(CciLevel), 0m)
			.SetDisplay("CCI Level", "CCI threshold for confirmation", "Signals");
	}

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

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevCandle = null;
		_prevPrevCandle = null;
		var cci = new CommodityChannelIndex { Length = CciPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(cci, ProcessCandle).Start();
	}

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

		if (_prevCandle != null && _prevPrevCandle != null)
		{
			var prevBody = Math.Abs(_prevCandle.ClosePrice - _prevCandle.OpenPrice);
			var prevRange = _prevCandle.HighPrice - _prevCandle.LowPrice;
			var isSmallBody = prevRange > 0 && prevBody < prevRange * 0.3m;

			var firstBearish = _prevPrevCandle.OpenPrice > _prevPrevCandle.ClosePrice;
			var currBullish = candle.ClosePrice > candle.OpenPrice;
			var isMorningStar = firstBearish && isSmallBody && currBullish &&
							   candle.ClosePrice > _prevPrevCandle.OpenPrice * 0.5m + _prevPrevCandle.ClosePrice * 0.5m;

			var firstBullish = _prevPrevCandle.ClosePrice > _prevPrevCandle.OpenPrice;
			var currBearish = candle.OpenPrice > candle.ClosePrice;
			var isEveningStar = firstBullish && isSmallBody && currBearish &&
							   candle.ClosePrice < _prevPrevCandle.OpenPrice * 0.5m + _prevPrevCandle.ClosePrice * 0.5m;

			if (isMorningStar && cciValue < -CciLevel && Position <= 0)
				BuyMarket();
			else if (isEveningStar && cciValue > CciLevel && Position >= 0)
				SellMarket();
		}

		_prevPrevCandle = _prevCandle;
		_prevCandle = candle;
	}
}