Стратегия повторяет логику советника 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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import CommodityChannelIndex
from StockSharp.Algo.Strategies import Strategy
class morning_evening_star_cci_strategy(Strategy):
"""
Morning/Evening Star + CCI: buy on morning star with negative CCI, sell on evening star with positive CCI.
"""
def __init__(self):
super(morning_evening_star_cci_strategy, self).__init__()
self._cci_period = self.Param("CciPeriod", 14).SetDisplay("CCI Period", "CCI period", "Indicators")
self._cci_level = self.Param("CciLevel", 0.0).SetDisplay("CCI Level", "CCI threshold", "Signals")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candles", "General")
self._prev_candle = None
self._prev_prev_candle = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(morning_evening_star_cci_strategy, self).OnReseted()
self._prev_candle = None
self._prev_prev_candle = None
def OnStarted2(self, time):
super(morning_evening_star_cci_strategy, self).OnStarted2(time)
cci = CommodityChannelIndex()
cci.Length = self._cci_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(cci, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, cci)
self.DrawOwnTrades(area)
def _process_candle(self, candle, cci_val):
if candle.State != CandleStates.Finished:
return
cci = float(cci_val)
cci_level = float(self._cci_level.Value)
if self._prev_candle is not None and self._prev_prev_candle is not None:
prev_body = abs(float(self._prev_candle.ClosePrice) - float(self._prev_candle.OpenPrice))
prev_range = float(self._prev_candle.HighPrice) - float(self._prev_candle.LowPrice)
is_small_body = prev_range > 0 and prev_body < prev_range * 0.3
pp_open = float(self._prev_prev_candle.OpenPrice)
pp_close = float(self._prev_prev_candle.ClosePrice)
c_open = float(candle.OpenPrice)
c_close = float(candle.ClosePrice)
first_bearish = pp_open > pp_close
curr_bullish = c_close > c_open
is_morning = first_bearish and is_small_body and curr_bullish and c_close > pp_open * 0.5 + pp_close * 0.5
first_bullish = pp_close > pp_open
curr_bearish = c_open > c_close
is_evening = first_bullish and is_small_body and curr_bearish and c_close < pp_open * 0.5 + pp_close * 0.5
if is_morning and cci < -cci_level and self.Position <= 0:
self.BuyMarket()
elif is_evening and cci > cci_level and self.Position >= 0:
self.SellMarket()
self._prev_prev_candle = self._prev_candle
self._prev_candle = candle
def CreateClone(self):
return morning_evening_star_cci_strategy()