The iCCI iRSI Strategy is a direct conversion of the MetaTrader 5 expert advisor iCCI iRSI.mq5. The original system blends the Commodity Channel Index (CCI) and the Relative Strength Index (RSI) to detect exhaustion zones. When both oscillators agree on an oversold or overbought state the advisor opens a position, attaches protective orders and optionally trails the stop as the trade moves into profit. This StockSharp port mirrors that behaviour with high-level APIs, including pip-based inputs, auto-closing of opposite positions and a reversible signal mode.
Trading Logic
Subscribe to the configured candle type and calculate a CommodityChannelIndex with period CciPeriod and a RelativeStrengthIndex with period RsiPeriod.
Evaluate only completed candles. Intrabar noise is ignored just like the MQL implementation that waits for a new bar.
When both indicators fall below their respective lower thresholds (CciLowerLevel and RsiLowerLevel) the strategy opens or reverses into a long position. When both indicators rise above the upper thresholds (CciUpperLevel and RsiUpperLevel) a short setup is triggered. Enabling ReverseSignals swaps the directions.
Before submitting a new order the current opposite exposure is closed so the net position always matches the active signal.
After entry the strategy monitors the close price of subsequent candles. Take-profit and stop-loss levels expressed in pips are converted to price units using the instrument’s PriceStep. For 3- or 5-digit forex symbols an additional ×10 adjustment reproduces the MetaTrader pip definition.
If TrailingStopPips is positive, the stop-loss is advanced toward the market whenever price moves more than TrailingStopPips + TrailingStepPips in the favourable direction. Updates respect the configured step to avoid rapid stop modifications.
Risk and Trade Management
Take-profit / Stop-loss – optional pip distances that become absolute price levels immediately after a fill. When either level is breached on the close of a candle the position is liquidated at market.
Trailing stop – mimics the EA’s trailing logic. Profits must exceed the trailing distance plus the trailing step before the stop is tightened.
Volume – a fixed TradeVolume parameter replaces the original lot-or-risk switch (ENUM_LOT_OR_RISK). Use optimisation to discover suitable volumes if money-management variants are required.
Position hygiene – when a new signal appears the strategy flattens any opposite holdings before opening the fresh trade, just as the EA performs ClosePositions.
Parameters
Candle Type – candle data series processed by the indicators (default: 1-hour candles).
CciPeriod – CCI averaging length (default: 14).
CciUpperLevel / CciLowerLevel – overbought and oversold CCI thresholds (defaults: +80 / −80).
ReverseSignals – flips the interpretation of the oscillator signals (default: false).
TradeVolume – market order size. Set to match the MT5 lot input (default: 0.1).
StopLossPips / TakeProfitPips – protective distances in pips (defaults: 0 and 140). Set to zero to disable.
TrailingStopPips / TrailingStepPips – trailing-stop distance and minimum step (defaults: 5 / 5). A zero trailing distance disables trailing even if a step is provided.
Implementation Notes
StockSharp indicators (CommodityChannelIndex, RelativeStrengthIndex) deliver ready-to-use decimal values through the Bind API, so no manual CopyBuffer logic is required.
All trade management takes place on finished candles. This matches the EA’s PrevBars guard and prevents multiple entries within the same bar.
Pip conversion honours fractional pip quotes by multiplying the PriceStep by 10 for instruments with 3 or 5 decimals – a direct analogue to the MQL digits_adjust logic.
Protective targets are simulated via market exits because StockSharp strategies operate inside a sandboxed environment where synchronous order modifications are not available.
Additional chart areas draw the CCI and RSI lines for visual validation of entry zones.
Differences from the Original Expert Advisor
The MetaTrader module MoneyFixedMargin is not ported. Position sizing is now a simple fixed volume parameter.
Broker-specific checks such as FreezeStopsLevels are not available in StockSharp. The trailing stop therefore only observes price distance and step requirements.
Logging and alert strings have been removed in favour of clean strategy output. StockSharp’s logging system can be attached externally if needed.
Trade management operates on candle closes. The MT5 version could react intrabar when the stop or take-profit is touched, but the end-of-bar approximation keeps the logic deterministic for backtests.
Usage Tips
Start with the default 1-hour timeframe to mirror the original template. Shorter frames can introduce more signals but also more whipsaws.
Optimise CciUpperLevel, CciLowerLevel, RsiUpperLevel and RsiLowerLevel together – the EA relies on agreement between both oscillators, so balanced thresholds are essential.
When running on forex pairs double-check that the security metadata exposes PriceStep and Decimals so pip distances convert correctly.
Disable ReverseSignals for classical trend-reversal behaviour. Enable it to trade breakouts out of overbought/oversold zones.
Combine with StockSharp risk modules (equity stop, drawdown protection) if portfolio-level controls are required – they replace the MT5 m_money helper.
This documentation should provide all necessary context to deploy, customise and extend the iCCI iRSI strategy within the StockSharp environment.
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;
public class IcciIrsiStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public IcciIrsiStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class icci_irsi_strategy(Strategy):
def __init__(self):
super(icci_irsi_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(icci_irsi_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(icci_irsi_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return icci_irsi_strategy()