This strategy is a StockSharp port of the MetaTrader 5 expert advisor "FT_CCI (barabashkakvn's edition)". It uses the Commodity Channel Index (CCI) to capture sharp reversals once the oscillator stretches far away from its mean. The system mirrors the original logic: when CCI pierces the lower band it flips long, and when it pierces the upper band it flips short. Optional stop-loss and take-profit values are entered in pips and automatically converted into price offsets.
Overview
Core indicator: Commodity Channel Index with a configurable averaging period (default 14).
Bias: Symmetric long/short. The strategy always holds at most one net position and reverses on opposite signals.
Execution: Market orders on the close of finished candles from the selected timeframe.
Risk management: Optional stop-loss and take-profit distances expressed in pips. If either value is zero the corresponding protection is disabled.
Default timeframe: 30-minute candles (mirrors the "Period()" selection in the original expert).
How it works
Long setup
Subscribe to finished candles of the selected timeframe.
Update the CCI indicator with typical price values.
When the latest CCI value is at or below the configured lower threshold (default -210):
Close any open short exposure.
Enter or add to a long position using the configured trade volume.
Maintain the position until either an opposite short setup triggers, a stop-loss/take-profit event occurs, or the strategy is stopped manually.
Short setup
Monitor the same CCI values on finished candles.
When the indicator is at or above the upper threshold (default +210):
Close any open long exposure.
Enter or add to a short position using the configured volume.
Hold the short until an opposite long condition fires or protective orders close the trade.
Trade management
Stop-loss and take-profit distances are defined in pips. The strategy multiplies them by the detected pip size (price step, multiplied by 10 for 3- and 5-digit forex symbols) to obtain an absolute price offset before enabling StockSharp's built-in StartProtection.
Because the protection is applied once on start, any new position immediately inherits the same stop and target values relative to its fill price.
Position flips are executed via market orders sized at configured volume + |current position|, ensuring that reversing a position both closes the current exposure and opens the new one in a single transaction.
Parameters
Name
Description
Candle Type
Timeframe used for calculations and signal generation.
Trade Volume
Lot size for new positions. Used together with the current position value to size reversal trades.
CCI Period
Averaging length of the Commodity Channel Index.
CCI Upper Threshold
CCI level that triggers short entries.
CCI Lower Threshold
CCI level that triggers long entries.
Stop Loss (pips)
Distance to the protective stop in pips. Set to 0 to disable.
Take Profit (pips)
Distance to the profit target in pips. Set to 0 to disable.
All parameters support optimization through StockSharp's parameter manager.
Recommended usage
Works best on liquid forex pairs and indices where 30-minute to 4-hour candles produce pronounced CCI extremes.
Thresholds of ±210 recreate the FT_CCI defaults. Lower values make the system more reactive; higher values focus on only the most extreme reversals.
Ensure the security metadata exposes a valid PriceStep. The pip converter relies on this value to translate pips into price offsets.
The strategy assumes a netting account model (single net position). For hedging accounts set the trading volume appropriately so that reversals fully flatten the previous trade.
Notes
The indicator must be fully formed before any trade signal is considered. Early candles are ignored until the CCI has enough data to emit valid values.
Stop-loss and take-profit orders are optional. Leaving them at zero reproduces the original expert advisor behaviour that relied solely on opposite signals for exits.
Add the strategy to a chart in StockSharp to visualize candles, the CCI indicator, and executed trades; these visual aids are enabled automatically in the C# implementation.
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 breakout strategy. Opens long when CCI drops below lower band, shorts when above upper band.
/// </summary>
public class FtCciStrategy : Strategy
{
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<decimal> _upperThreshold;
private readonly StrategyParam<decimal> _lowerThreshold;
private readonly StrategyParam<DataType> _candleType;
public int CciPeriod { get => _cciPeriod.Value; set => _cciPeriod.Value = value; }
public decimal UpperThreshold { get => _upperThreshold.Value; set => _upperThreshold.Value = value; }
public decimal LowerThreshold { get => _lowerThreshold.Value; set => _lowerThreshold.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public FtCciStrategy()
{
_cciPeriod = Param(nameof(CciPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("CCI Period", "Averaging period for CCI", "Indicator");
_upperThreshold = Param(nameof(UpperThreshold), 210m)
.SetDisplay("CCI Upper", "CCI level for short entries", "Indicator");
_lowerThreshold = Param(nameof(LowerThreshold), -210m)
.SetDisplay("CCI Lower", "CCI level for long entries", "Indicator");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle Type", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
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 (cciValue <= LowerThreshold && Position <= 0)
BuyMarket();
else if (cciValue >= UpperThreshold && Position >= 0)
SellMarket();
// Exit when CCI returns to zero
if (Position > 0 && cciValue >= 0)
SellMarket();
else if (Position < 0 && cciValue <= 0)
BuyMarket();
}
}
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 ft_cci_strategy(Strategy):
def __init__(self):
super(ft_cci_strategy, self).__init__()
self._cci_period = self.Param("CciPeriod", 14) \
.SetDisplay("CCI Period", "Averaging period for CCI", "Indicator")
self._upper_threshold = self.Param("UpperThreshold", 210.0) \
.SetDisplay("CCI Upper", "CCI level for short entries", "Indicator")
self._lower_threshold = self.Param("LowerThreshold", -210.0) \
.SetDisplay("CCI Lower", "CCI level for long entries", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle Type", "General")
@property
def cci_period(self):
return self._cci_period.Value
@property
def upper_threshold(self):
return self._upper_threshold.Value
@property
def lower_threshold(self):
return self._lower_threshold.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(ft_cci_strategy, self).OnStarted2(time)
cci = CommodityChannelIndex()
cci.Length = self.cci_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(cci, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, cci)
self.DrawOwnTrades(area)
def on_process(self, candle, cci_value):
if candle.State != CandleStates.Finished:
return
if cci_value <= self.lower_threshold and self.Position <= 0:
self.BuyMarket()
elif cci_value >= self.upper_threshold and self.Position >= 0:
self.SellMarket()
# Exit when CCI returns to zero
if self.Position > 0 and cci_value >= 0:
self.SellMarket()
elif self.Position < 0 and cci_value <= 0:
self.BuyMarket()
def CreateClone(self):
return ft_cci_strategy()