Дивергенции индекса товарного канала (CCI) могут предвещать разворот тренда, когда цена движется в противоположную сторону от индикатора. Стратегия сравнивает экстремумы цены и CCI, чтобы выявить скрытую силу или слабость.
Тестирование показывает среднегодичную доходность около 91%. Стратегию лучше запускать на фондовом рынке.
На каждой свече система обновляет последние значения цены и CCI, отмечая бычью дивергенцию, когда цена делает новый минимум, а CCI формирует более высокий минимум. Медвежья дивергенция — обратная ситуация. Когда дивергенция совпадает с перепроданностью или перекупленностью, открывается сделка со стопом по волатильности.
Выход из позиции происходит, когда CCI вновь пересекает нулевую линию, что сигнализирует об окончании импульса. Поскольку дивергенции могут затягиваться, правила также сбрасываются через фиксированное число баров, чтобы избежать устаревших сигналов.
Детали
Условия входа: Дивергенция цены и CCI, при этом CCI ниже -100 для лонга или выше +100 для шорта.
Направление: Лонг и шорт.
Условия выхода: Пересечение CCI нуля или стоп‑лосс.
Стопы: Да, процентные.
Значения по умолчанию:
CciPeriod = 20
DivergencePeriod = 5
OverboughtLevel = 100
OversoldLevel = -100
CandleType = 15 минут
StopLossPercent = 2
Фильтры:
Категория: Дивергенция
Направление: Оба
Индикаторы: CCI
Стопы: Да
Сложность: Продвинутая
Таймфрейм: Внутридневной
Сезонность: Нет
Нейронные сети: Нет
Дивергенция: Да
Уровень риска: Средний
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// CCI Divergence strategy.
/// Detects divergences between price and CCI for reversal signals.
/// Bullish: price falling but CCI rising.
/// Bearish: price rising but CCI falling.
/// </summary>
public class CciDivergenceStrategy : Strategy
{
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private decimal _prevPrice;
private decimal _prevCci;
private int _cooldown;
/// <summary>
/// CCI Period.
/// </summary>
public int CciPeriod
{
get => _cciPeriod.Value;
set => _cciPeriod.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Cooldown bars.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public CciDivergenceStrategy()
{
_cciPeriod = Param(nameof(CciPeriod), 14)
.SetRange(5, 30)
.SetDisplay("CCI Period", "Period for CCI", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_cooldownBars = Param(nameof(CooldownBars), 500)
.SetRange(1, 1000)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevPrice = default;
_prevCci = default;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevPrice = 0;
_prevCci = 0;
_cooldown = 0;
var cci = new CommodityChannelIndex { Length = CciPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(cci, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, cci);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal cciValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_prevPrice == 0)
{
_prevPrice = candle.ClosePrice;
_prevCci = cciValue;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevPrice = candle.ClosePrice;
_prevCci = cciValue;
return;
}
var bullishDiv = candle.ClosePrice < _prevPrice && cciValue > _prevCci;
var bearishDiv = candle.ClosePrice > _prevPrice && cciValue < _prevCci;
if (Position == 0 && bullishDiv && cciValue > -100)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (Position == 0 && bearishDiv && cciValue < 100)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position > 0 && cciValue > 100)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position < 0 && cciValue < -100)
{
BuyMarket();
_cooldown = CooldownBars;
}
_prevPrice = candle.ClosePrice;
_prevCci = cciValue;
}
}
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 cci_divergence_strategy(Strategy):
"""
CCI Divergence strategy.
Detects divergences between price and CCI for reversal signals.
Bullish: price falling but CCI rising.
Bearish: price rising but CCI falling.
"""
def __init__(self):
super(cci_divergence_strategy, self).__init__()
self._cci_period = self.Param("CciPeriod", 14).SetDisplay("CCI Period", "Period for CCI", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._cooldown_bars = self.Param("CooldownBars", 500).SetDisplay("Cooldown Bars", "Bars to wait between trades", "General")
self._prev_price = 0.0
self._prev_cci = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(cci_divergence_strategy, self).OnReseted()
self._prev_price = 0.0
self._prev_cci = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(cci_divergence_strategy, self).OnStarted2(time)
self._prev_price = 0.0
self._prev_cci = 0.0
self._cooldown = 0
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
close = float(candle.ClosePrice)
cv = float(cci_val)
if self._prev_price == 0:
self._prev_price = close
self._prev_cci = cv
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_price = close
self._prev_cci = cv
return
cd = self._cooldown_bars.Value
bullish_div = close < self._prev_price and cv > self._prev_cci
bearish_div = close > self._prev_price and cv < self._prev_cci
if self.Position == 0 and bullish_div and cv > -100:
self.BuyMarket()
self._cooldown = cd
elif self.Position == 0 and bearish_div and cv < 100:
self.SellMarket()
self._cooldown = cd
elif self.Position > 0 and cv > 100:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0 and cv < -100:
self.BuyMarket()
self._cooldown = cd
self._prev_price = close
self._prev_cci = cv
def CreateClone(self):
return cci_divergence_strategy()