Triple CCI MFI Confirmed Strategy
This strategy enters long when the fast CCI crosses above zero while the middle and slow CCI remain positive, price is above EMA and MFI exceeds 50. Profit is trailed by EMA after an ATR based activation.
Testing shows moderate performance; it works best during trending markets.
Details
- Entry Criteria:
- Long: Fast CCI crosses above 0, middle CCI > 0, slow CCI > 0, MFI > 50, close above EMA
- Long/Short: Long only.
- Exit Criteria:
- Long: Close below trailing EMA after activation or low hits ATR stop
- Stops: Yes.
- Default Values:
StopLossAtrMultiplier= 1.75TrailingActivationMultiplier= 2.25FastCciPeriod= 14MiddleCciPeriod= 25SlowCciPeriod= 50MfiLength= 14EmaLength= 50TrailingEmaLength= 20AtrPeriod= 14CandleType= TimeSpan.FromMinutes(5)
- Filters:
- Category: Trend
- Direction: Long
- Indicators: CCI, MFI, EMA, ATR
- Stops: Yes
- Complexity: Intermediate
- Timeframe: Intraday
- Seasonality: No
- Neural networks: No
- Divergence: No
- Risk Level: Medium
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>
/// Strategy using triple CCI confirmed by MFI and ExponentialMovingAverage with ATR based trailing exit.
/// </summary>
public class TripleCciMfiConfirmedStrategy : Strategy
{
private readonly StrategyParam<decimal> _stopLossAtrMultiplier;
private readonly StrategyParam<decimal> _trailingActivationMultiplier;
private readonly StrategyParam<int> _fastCciPeriod;
private readonly StrategyParam<int> _middleCciPeriod;
private readonly StrategyParam<int> _slowCciPeriod;
private readonly StrategyParam<int> _mfiLength;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _trailingEmaLength;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<DateTimeOffset> _tradeStart;
private readonly StrategyParam<DateTimeOffset> _tradeStop;
private decimal _prevFastCci;
private decimal _stopLossLevel;
private decimal _activationLevel;
private decimal _takeProfitLevel;
private bool _trailingActivated;
private DateTimeOffset _lastSignal;
/// <summary>
/// ATR multiplier for stop loss.
/// </summary>
public decimal StopLossAtrMultiplier
{
get => _stopLossAtrMultiplier.Value;
set => _stopLossAtrMultiplier.Value = value;
}
/// <summary>
/// ATR multiplier for trailing profit activation.
/// </summary>
public decimal TrailingActivationMultiplier
{
get => _trailingActivationMultiplier.Value;
set => _trailingActivationMultiplier.Value = value;
}
/// <summary>
/// Fast CCI period.
/// </summary>
public int FastCciPeriod
{
get => _fastCciPeriod.Value;
set => _fastCciPeriod.Value = value;
}
/// <summary>
/// Middle CCI period.
/// </summary>
public int MiddleCciPeriod
{
get => _middleCciPeriod.Value;
set => _middleCciPeriod.Value = value;
}
/// <summary>
/// Slow CCI period.
/// </summary>
public int SlowCciPeriod
{
get => _slowCciPeriod.Value;
set => _slowCciPeriod.Value = value;
}
/// <summary>
/// MFI length.
/// </summary>
public int MfiLength
{
get => _mfiLength.Value;
set => _mfiLength.Value = value;
}
/// <summary>
/// ExponentialMovingAverage length.
/// </summary>
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
/// <summary>
/// Trailing ExponentialMovingAverage length.
/// </summary>
public int TrailingEmaLength
{
get => _trailingEmaLength.Value;
set => _trailingEmaLength.Value = value;
}
/// <summary>
/// ATR period.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Trading start date.
/// </summary>
public DateTimeOffset TradeStart
{
get => _tradeStart.Value;
set => _tradeStart.Value = value;
}
/// <summary>
/// Trading stop date.
/// </summary>
public DateTimeOffset TradeStop
{
get => _tradeStop.Value;
set => _tradeStop.Value = value;
}
/// <summary>
/// Initializes a new instance of the strategy.
/// </summary>
public TripleCciMfiConfirmedStrategy()
{
_stopLossAtrMultiplier = Param(nameof(StopLossAtrMultiplier), 1.75m)
.SetRange(0.5m, 5m)
.SetDisplay("ATR Stop Loss", "ATR multiplier for stop loss", "Risk Management")
;
_trailingActivationMultiplier = Param(nameof(TrailingActivationMultiplier), 2.25m)
.SetRange(0.5m, 5m)
.SetDisplay("ATR Trailing Activation", "ATR multiplier to activate trailing", "Risk Management")
;
_fastCciPeriod = Param(nameof(FastCciPeriod), 14)
.SetRange(5, 50)
.SetDisplay("CCI Fast Length", "Fast CCI period", "Indicators")
;
_middleCciPeriod = Param(nameof(MiddleCciPeriod), 25)
.SetRange(5, 100)
.SetDisplay("CCI Middle Length", "Middle CCI period", "Indicators")
;
_slowCciPeriod = Param(nameof(SlowCciPeriod), 50)
.SetRange(10, 150)
.SetDisplay("CCI Slow Length", "Slow CCI period", "Indicators")
;
_mfiLength = Param(nameof(MfiLength), 14)
.SetRange(1, 200)
.SetDisplay("MFI Length", "Money Flow Index length", "Indicators")
;
_emaLength = Param(nameof(EmaLength), 50)
.SetRange(10, 200)
.SetDisplay("ExponentialMovingAverage Length", "ExponentialMovingAverage filter length", "Indicators")
;
_trailingEmaLength = Param(nameof(TrailingEmaLength), 20)
.SetRange(10, 100)
.SetDisplay("Trailing ExponentialMovingAverage Length", "ExponentialMovingAverage length for trailing profit", "Indicators")
;
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetRange(5, 50)
.SetDisplay("ATR Period", "ATR calculation period", "Indicators")
;
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
_tradeStart = Param(nameof(TradeStart), new DateTimeOffset(2023, 1, 1, 0, 0, 0, TimeSpan.Zero))
.SetDisplay("Trade Start", "Start date for trading", "Time Range");
_tradeStop = Param(nameof(TradeStop), new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero))
.SetDisplay("Trade Stop", "Stop date for trading", "Time Range");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFastCci = default;
_stopLossLevel = default;
_activationLevel = default;
_takeProfitLevel = default;
_trailingActivated = default;
_lastSignal = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fastCci = new CommodityChannelIndex { Length = FastCciPeriod };
var middleCci = new CommodityChannelIndex { Length = MiddleCciPeriod };
var slowCci = new CommodityChannelIndex { Length = SlowCciPeriod };
var mfi = new MoneyFlowIndex { Length = MfiLength };
var ema = new ExponentialMovingAverage { Length = EmaLength };
var trailingEma = new ExponentialMovingAverage { Length = TrailingEmaLength };
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastCci, middleCci, slowCci, mfi, ema, trailingEma, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastCci);
DrawIndicator(area, middleCci);
DrawIndicator(area, slowCci);
DrawIndicator(area, mfi);
DrawIndicator(area, ema);
DrawIndicator(area, trailingEma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastCci, decimal middleCci, decimal slowCci, decimal mfi, decimal ema, decimal trailingEma, decimal atr)
{
if (candle.State != CandleStates.Finished)
return;
if (candle.OpenTime < TradeStart || candle.OpenTime > TradeStop)
return;
var crossedUp = _prevFastCci <= 0m && fastCci > 0m;
_prevFastCci = fastCci;
var cooldown = TimeSpan.FromMinutes(1440);
if (crossedUp && candle.ClosePrice > ema && middleCci > 0m && slowCci > 0m && mfi > 65m && Position <= 0 && candle.OpenTime - _lastSignal >= cooldown)
{
BuyMarket();
_lastSignal = candle.OpenTime;
_stopLossLevel = candle.ClosePrice - StopLossAtrMultiplier * atr;
_activationLevel = candle.ClosePrice + TrailingActivationMultiplier * atr;
_trailingActivated = false;
_takeProfitLevel = default;
return;
}
if (Position <= 0)
return;
if (!_trailingActivated && candle.HighPrice > _activationLevel)
_trailingActivated = true;
if (_trailingActivated)
_takeProfitLevel = trailingEma;
if (_takeProfitLevel != default && candle.ClosePrice < _takeProfitLevel)
{
SellMarket();
_lastSignal = candle.OpenTime;
return;
}
if (candle.LowPrice <= _stopLossLevel)
{
SellMarket();
_lastSignal = candle.OpenTime;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, DateTimeOffset, DateTime
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import AverageTrueRange, CommodityChannelIndex, ExponentialMovingAverage, MoneyFlowIndex
from StockSharp.Algo.Strategies import Strategy
class triple_cci_mfi_confirmed_strategy(Strategy):
def __init__(self):
super(triple_cci_mfi_confirmed_strategy, self).__init__()
self._stop_loss_atr_multiplier = self.Param("StopLossAtrMultiplier", 1.75) \
.SetDisplay("ATR Stop Loss", "ATR multiplier for stop loss", "Risk Management")
self._trailing_activation_multiplier = self.Param("TrailingActivationMultiplier", 2.25) \
.SetDisplay("ATR Trailing Activation", "ATR multiplier to activate trailing", "Risk Management")
self._fast_cci_period = self.Param("FastCciPeriod", 14) \
.SetDisplay("CCI Fast Length", "Fast CCI period", "Indicators")
self._middle_cci_period = self.Param("MiddleCciPeriod", 25) \
.SetDisplay("CCI Middle Length", "Middle CCI period", "Indicators")
self._slow_cci_period = self.Param("SlowCciPeriod", 50) \
.SetDisplay("CCI Slow Length", "Slow CCI period", "Indicators")
self._mfi_length = self.Param("MfiLength", 14) \
.SetDisplay("MFI Length", "Money Flow Index length", "Indicators")
self._ema_length = self.Param("EmaLength", 50) \
.SetDisplay("ExponentialMovingAverage Length", "ExponentialMovingAverage filter length", "Indicators")
self._trailing_ema_length = self.Param("TrailingEmaLength", 20) \
.SetDisplay("Trailing ExponentialMovingAverage Length", "ExponentialMovingAverage length for trailing profit", "Indicators")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "ATR calculation period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._trade_start = self.Param("TradeStart", DateTime(2023, 1, 1)) \
.SetDisplay("Trade Start", "Start date for trading", "Time Range")
self._trade_stop = self.Param("TradeStop", DateTime(2025, 1, 1)) \
.SetDisplay("Trade Stop", "Stop date for trading", "Time Range")
self._prev_fast_cci = 0.0
self._stop_loss_level = 0.0
self._activation_level = 0.0
self._take_profit_level = 0.0
self._trailing_activated = False
self._last_signal = DateTime.MinValue
@property
def stop_loss_atr_multiplier(self):
return self._stop_loss_atr_multiplier.Value
@property
def trailing_activation_multiplier(self):
return self._trailing_activation_multiplier.Value
@property
def fast_cci_period(self):
return self._fast_cci_period.Value
@property
def middle_cci_period(self):
return self._middle_cci_period.Value
@property
def slow_cci_period(self):
return self._slow_cci_period.Value
@property
def mfi_length(self):
return self._mfi_length.Value
@property
def ema_length(self):
return self._ema_length.Value
@property
def trailing_ema_length(self):
return self._trailing_ema_length.Value
@property
def atr_period(self):
return self._atr_period.Value
@property
def candle_type(self):
return self._candle_type.Value
@property
def trade_start(self):
return self._trade_start.Value
@property
def trade_stop(self):
return self._trade_stop.Value
def OnReseted(self):
super(triple_cci_mfi_confirmed_strategy, self).OnReseted()
self._prev_fast_cci = 0.0
self._stop_loss_level = 0.0
self._activation_level = 0.0
self._take_profit_level = 0.0
self._trailing_activated = False
self._last_signal = DateTime.MinValue
def OnStarted2(self, time):
super(triple_cci_mfi_confirmed_strategy, self).OnStarted2(time)
fast_cci = CommodityChannelIndex()
fast_cci.Length = self.fast_cci_period
middle_cci = CommodityChannelIndex()
middle_cci.Length = self.middle_cci_period
slow_cci = CommodityChannelIndex()
slow_cci.Length = self.slow_cci_period
mfi = MoneyFlowIndex()
mfi.Length = self.mfi_length
ema = ExponentialMovingAverage()
ema.Length = self.ema_length
trailing_ema = ExponentialMovingAverage()
trailing_ema.Length = self.trailing_ema_length
atr = AverageTrueRange()
atr.Length = self.atr_period
subscription = self.SubscribeCandles(self.candle_type)
def on_candle(candle, fast_cci_val, middle_cci_val, slow_cci_val, mfi_val, ema_val, trailing_ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
if candle.OpenTime < self.trade_start or candle.OpenTime > self.trade_stop:
return
fc = float(fast_cci_val)
mc = float(middle_cci_val)
sc = float(slow_cci_val)
mv = float(mfi_val)
ev = float(ema_val)
tv = float(trailing_ema_val)
av = float(atr_val)
crossed_up = self._prev_fast_cci <= 0 and fc > 0
self._prev_fast_cci = fc
cooldown = TimeSpan.FromMinutes(1440)
if crossed_up and float(candle.ClosePrice) > ev and mc > 0 and sc > 0 and mv > 65 and self.Position <= 0 and candle.OpenTime - self._last_signal >= cooldown:
self.BuyMarket()
self._last_signal = candle.OpenTime
self._stop_loss_level = float(candle.ClosePrice) - float(self.stop_loss_atr_multiplier) * av
self._activation_level = float(candle.ClosePrice) + float(self.trailing_activation_multiplier) * av
self._trailing_activated = False
self._take_profit_level = 0.0
return
if self.Position <= 0:
return
if not self._trailing_activated and float(candle.HighPrice) > self._activation_level:
self._trailing_activated = True
if self._trailing_activated:
self._take_profit_level = tv
if self._take_profit_level != 0.0 and float(candle.ClosePrice) < self._take_profit_level:
self.SellMarket()
self._last_signal = candle.OpenTime
return
if float(candle.LowPrice) <= self._stop_loss_level:
self.SellMarket()
self._last_signal = candle.OpenTime
subscription.Bind(fast_cci, middle_cci, slow_cci, mfi, ema, trailing_ema, atr, on_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_cci)
self.DrawIndicator(area, middle_cci)
self.DrawIndicator(area, slow_cci)
self.DrawIndicator(area, mfi)
self.DrawIndicator(area, ema)
self.DrawIndicator(area, trailing_ema)
self.DrawOwnTrades(area)
def CreateClone(self):
return triple_cci_mfi_confirmed_strategy()