Ver en GitHub

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.75
    • TrailingActivationMultiplier = 2.25
    • FastCciPeriod = 14
    • MiddleCciPeriod = 25
    • SlowCciPeriod = 50
    • MfiLength = 14
    • EmaLength = 50
    • TrailingEmaLength = 20
    • AtrPeriod = 14
    • CandleType = 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;
		}
	}
}