Cronex CCI
Strategy based on Cronex Commodity Channel Index crossover. The indicator smooths the CCI through two exponential moving averages to create a fast and a slow line.
The strategy opens a long position when the fast line crosses below the slow line and closes any short position. A short position is opened when the fast line crosses above the slow line and closes any long position.
This contrarian approach attempts to capture reversals after momentum shifts. It works on higher timeframes such as 4 hour candles.
Details
- Entry Criteria: Crossovers of fast and slow smoothed CCI lines.
- Long/Short: Both directions.
- Exit Criteria: Opposite crossover.
- Stops: No.
- Default Values:
CciPeriod= 25FastPeriod= 14SlowPeriod= 25CandleType= TimeSpan.FromHours(4)EnableLongEntry= trueEnableShortEntry= trueEnableLongExit= trueEnableShortExit= true
- Filters:
- Category: Mean Reversion
- Direction: Both
- Indicators: CCI, EMA
- Stops: No
- Complexity: Basic
- Timeframe: Swing (4h)
- Seasonality: No
- Neural Networks: No
- Divergence: No
- Risk Level: Medium
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on Cronex CCI indicator.
/// </summary>
public class CronexCciStrategy : Strategy
{
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<bool> _enableLongEntry;
private readonly StrategyParam<bool> _enableShortEntry;
private readonly StrategyParam<bool> _enableLongExit;
private readonly StrategyParam<bool> _enableShortExit;
private decimal? _prevFast;
private decimal? _prevSlow;
private EMA _fastMa;
private EMA _slowMa;
private Order _pendingOrder;
/// <summary>
/// CCI period.
/// </summary>
public int CciPeriod
{
get => _cciPeriod.Value;
set => _cciPeriod.Value = value;
}
/// <summary>
/// Fast smoothing period.
/// </summary>
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
/// <summary>
/// Slow smoothing period.
/// </summary>
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Enable opening long positions.
/// </summary>
public bool EnableLongEntry
{
get => _enableLongEntry.Value;
set => _enableLongEntry.Value = value;
}
/// <summary>
/// Enable opening short positions.
/// </summary>
public bool EnableShortEntry
{
get => _enableShortEntry.Value;
set => _enableShortEntry.Value = value;
}
/// <summary>
/// Enable closing long positions.
/// </summary>
public bool EnableLongExit
{
get => _enableLongExit.Value;
set => _enableLongExit.Value = value;
}
/// <summary>
/// Enable closing short positions.
/// </summary>
public bool EnableShortExit
{
get => _enableShortExit.Value;
set => _enableShortExit.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="CronexCciStrategy"/>.
/// </summary>
public CronexCciStrategy()
{
_cciPeriod = Param(nameof(CciPeriod), 25)
.SetRange(5, 100)
.SetDisplay("CCI Period", "CCI calculation length", "Indicators")
;
_fastPeriod = Param(nameof(FastPeriod), 14)
.SetRange(2, 50)
.SetDisplay("Fast Period", "Fast smoothing period", "Indicators")
;
_slowPeriod = Param(nameof(SlowPeriod), 25)
.SetRange(2, 100)
.SetDisplay("Slow Period", "Slow smoothing period", "Indicators")
;
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
_enableLongEntry = Param(nameof(EnableLongEntry), true)
.SetDisplay("Enable Long Entry", "Allow opening long positions", "Trading");
_enableShortEntry = Param(nameof(EnableShortEntry), true)
.SetDisplay("Enable Short Entry", "Allow opening short positions", "Trading");
_enableLongExit = Param(nameof(EnableLongExit), true)
.SetDisplay("Enable Long Exit", "Allow closing long positions", "Trading");
_enableShortExit = Param(nameof(EnableShortExit), true)
.SetDisplay("Enable Short Exit", "Allow closing short positions", "Trading");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = null;
_prevSlow = null;
_fastMa = _slowMa = null;
_pendingOrder = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = _prevSlow = null;
_pendingOrder = null;
var cci = new CommodityChannelIndex { Length = CciPeriod };
_fastMa = new EMA { Length = FastPeriod };
_slowMa = new EMA { Length = SlowPeriod };
Indicators.Add(_fastMa);
Indicators.Add(_slowMa);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(cci, ProcessCandle).Start();
// Setup chart visualization
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _fastMa);
DrawIndicator(area, _slowMa);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal cciValue)
{
if (candle.State != CandleStates.Finished) return;
var fastResult = _fastMa.Process(cciValue, candle.OpenTime, true);
var slowResult = _slowMa.Process(cciValue, candle.OpenTime, true);
if (!fastResult.IsFormed || !slowResult.IsFormed) return;
var fast = fastResult.ToDecimal();
var slow = slowResult.ToDecimal();
var buy = _prevFast is decimal oldFast && _prevSlow is decimal oldSlow && oldFast >= oldSlow && fast < slow;
var sell = _prevFast is decimal oldFast2 && _prevSlow is decimal oldSlow2 && oldFast2 <= oldSlow2 && fast > slow;
_prevFast = fast;
_prevSlow = slow;
if (!IsFormedAndOnlineAndAllowTrading() || (_pendingOrder is not null && _pendingOrder.State is not (OrderStates.Done or OrderStates.Failed))) return;
if (buy)
{
if (Position < 0m && EnableShortExit) _pendingOrder = BuyMarket(-Position + (EnableLongEntry ? Volume : 0m));
else if (Position == 0m && EnableLongEntry) _pendingOrder = BuyMarket(Volume);
}
else if (sell)
{
if (Position > 0m && EnableLongExit) _pendingOrder = SellMarket(Position + (EnableShortEntry ? Volume : 0m));
else if (Position == 0m && EnableShortEntry) _pendingOrder = SellMarket(Volume);
}
}
}
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, OrderStates
from StockSharp.Algo.Indicators import CommodityChannelIndex, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import process_float
class cronex_cci_strategy(Strategy):
def __init__(self):
super(cronex_cci_strategy, self).__init__()
self._cci_period = self.Param("CciPeriod", 25) \
.SetDisplay("CCI Period", "CCI calculation length", "Indicators")
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast smoothing period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 25) \
.SetDisplay("Slow Period", "Slow smoothing period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._enable_long_entry = self.Param("EnableLongEntry", True) \
.SetDisplay("Enable Long Entry", "Allow opening long positions", "Trading")
self._enable_short_entry = self.Param("EnableShortEntry", True) \
.SetDisplay("Enable Short Entry", "Allow opening short positions", "Trading")
self._enable_long_exit = self.Param("EnableLongExit", True) \
.SetDisplay("Enable Long Exit", "Allow closing long positions", "Trading")
self._enable_short_exit = self.Param("EnableShortExit", True) \
.SetDisplay("Enable Short Exit", "Allow closing short positions", "Trading")
self._prev_fast = None
self._prev_slow = None
@property
def CciPeriod(self):
return self._cci_period.Value
@CciPeriod.setter
def CciPeriod(self, value):
self._cci_period.Value = value
@property
def FastPeriod(self):
return self._fast_period.Value
@FastPeriod.setter
def FastPeriod(self, value):
self._fast_period.Value = value
@property
def SlowPeriod(self):
return self._slow_period.Value
@SlowPeriod.setter
def SlowPeriod(self, value):
self._slow_period.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def EnableLongEntry(self):
return self._enable_long_entry.Value
@EnableLongEntry.setter
def EnableLongEntry(self, value):
self._enable_long_entry.Value = value
@property
def EnableShortEntry(self):
return self._enable_short_entry.Value
@EnableShortEntry.setter
def EnableShortEntry(self, value):
self._enable_short_entry.Value = value
@property
def EnableLongExit(self):
return self._enable_long_exit.Value
@EnableLongExit.setter
def EnableLongExit(self, value):
self._enable_long_exit.Value = value
@property
def EnableShortExit(self):
return self._enable_short_exit.Value
@EnableShortExit.setter
def EnableShortExit(self, value):
self._enable_short_exit.Value = value
def OnStarted2(self, time):
super(cronex_cci_strategy, self).OnStarted2(time)
self._prev_fast = self._prev_slow = None
self._pending_order = None
self._cci = CommodityChannelIndex()
self._cci.Length = self.CciPeriod
self._fast_ma = ExponentialMovingAverage()
self._fast_ma.Length = self.FastPeriod
self._slow_ma = ExponentialMovingAverage()
self._slow_ma.Length = self.SlowPeriod
self.Indicators.Add(self._fast_ma)
self.Indicators.Add(self._slow_ma)
self.SubscribeCandles(self.CandleType).Bind(self._cci, self.ProcessCandle).Start()
def ProcessCandle(self, candle, cci_value):
if candle.State != CandleStates.Finished:
return
fast_result = process_float(self._fast_ma, cci_value, candle.OpenTime, True)
slow_result = process_float(self._slow_ma, cci_value, candle.OpenTime, True)
if not fast_result.IsFormed or not slow_result.IsFormed:
return
fast, slow = float(fast_result), float(slow_result)
buy = self._prev_fast is not None and self._prev_fast >= self._prev_slow and fast < slow
sell = self._prev_fast is not None and self._prev_fast <= self._prev_slow and fast > slow
self._prev_fast, self._prev_slow = fast, slow
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._pending_order is not None and self._pending_order.State not in (OrderStates.Done, OrderStates.Failed):
return
if buy:
if self.Position < 0 and self.EnableShortExit:
self._pending_order = self.BuyMarket(-self.Position + (self.Volume if self.EnableLongEntry else 0))
elif self.Position == 0 and self.EnableLongEntry:
self._pending_order = self.BuyMarket(self.Volume)
elif sell:
if self.Position > 0 and self.EnableLongExit:
self._pending_order = self.SellMarket(self.Position + (self.Volume if self.EnableShortEntry else 0))
elif self.Position == 0 and self.EnableShortEntry:
self._pending_order = self.SellMarket(self.Volume)
def OnReseted(self):
super(cronex_cci_strategy, self).OnReseted()
self._prev_fast = self._prev_slow = None
self._fast_ma = self._slow_ma = self._cci = self._pending_order = None
def CreateClone(self):
return cronex_cci_strategy()