The Ilan iMA Strategy is a StockSharp port of the MetaTrader 5 expert advisor Ilan iMA.mq5. The advisor combines a shifted
moving-average trend filter with a martingale-style averaging grid. The StockSharp version re-implements the same ideas with the
high-level API: when the weighted moving average confirms a trend, the strategy opens a market order and keeps adding trades every
time price moves against the position by a configurable step. The whole basket is closed when a profit target, trailing stop or
explicit stop-loss is reached, reproducing the money-management model of the original EA.
Trading logic
Subscribe to the selected timeframe (CandleType) and feed a configurable moving average (MaMethod, MaPeriod,
PriceMode). A positive MaShift shifts the indicator forward, so the strategy evaluates historic values to mimic the MT5
behaviour.
Wait for the candle to close. Only finished bars generate signals and update trailing/stop logic.
Detect the trend by comparing four consecutive moving-average values shifted by MaShift bars:
strictly decreasing values signal a downtrend;
strictly increasing values signal an uptrend.
When no basket is open:
in a downtrend, if the close is above the moving average value, open a short with StartVolume;
in an uptrend, if the close is below the moving average value, open a long with StartVolume.
When a basket exists:
if price moves against the position by at least GridStepPips, open another order whose size grows by LotExponent but is
capped by LotMaximum and the exchange volume limits;
the average entry price, lowest buy price and highest sell price are tracked internally to keep the behaviour close to the
MT5 logic.
Close conditions:
once the floating profit of a basket with more than one trade reaches ProfitMinimum (in account currency), close all
orders in that direction;
if the floating profit reaches TakeProfitPips or the loss hits StopLossPips, close the basket;
trailing protection becomes active after TrailingStopPips + TrailingStepPips points of favourable movement and moves in
steps of TrailingStepPips.
Risk management and sizing
StartVolume replicates the MT5 StartLots parameter. Every additional order multiplies the previous size by LotExponent
while respecting LotMaximum and the venue limits (Security.MinVolume, Security.VolumeStep, Security.MaxVolume).
ProfitMinimum preserves the "lock release" behaviour from the MT5 version: once the grid recovered from a hedge and prints
the requested profit, all trades in that direction are closed.
Stop-loss and take-profit distances are measured in pips (StopLossPips, TakeProfitPips). The helper method converts pips
into exchange price steps using Security.PriceStep.
The trailing block emulates the MT5 implementation: trailing starts only after the price exceeds
TrailingStopPips + TrailingStepPips and is updated in discrete steps to avoid premature stop adjustments.
Parameters
Name
Type
Default
MT5 counterpart
Description
MaPeriod
int
15
Inp_MA_ma_period
Period of the trend filter moving average.
MaShift
int
5
Inp_MA_ma_shift
Forward shift of the moving-average line in bars.
MaMethod
MovingAverageMethod
Weighted
Inp_MA_ma_method
Smoothing algorithm (SMA, EMA, SMMA, LWMA).
PriceMode
CandlePrice
Weighted
Inp_MA_applied_price
Candle price fed into the indicator.
StartVolume
decimal
1
InpStartLots
Base order volume for the first trade in a basket.
GridStepPips
decimal
30
InpStep
Distance (in pips) between averaging entries.
LotExponent
decimal
1.6
InpLotExponent
Multiplier applied to the previous order size.
LotMaximum
decimal
15
InpLotMaximum
Hard cap for a single order volume.
ProfitMinimum
decimal
15
InpProfitMinimum
Minimum floating profit required to close a basket with several trades.
StopLossPips
decimal
0
InpStopLoss
Stop-loss distance expressed in pips (0 disables the stop).
TakeProfitPips
decimal
100
InpTakeProfit
Take-profit distance expressed in pips.
TrailingStopPips
decimal
15
InpTrailingStop
Profit threshold that activates the trailing stop.
TrailingStepPips
decimal
5
InpTrailingStep
Minimum additional profit before the trailing stop moves again.
CandleType
DataType
15-minute timeframe
chart period
Timeframe used for signal calculation.
Differences from the original EA
StockSharp works in a netting environment, therefore only one net position per direction exists. The strategy keeps an internal
list of entry prices and volumes to emulate the MT5 basket accounting.
Exchange-specific volume limits are always respected when rounding volumes, whereas the MT5 code relied on manual checks. This
prevents orders that would be rejected by the broker connector.
Stop-loss, take-profit and trailing logic are expressed through market exits instead of modifying existing MT5 positions. The
functional behaviour remains the same, but order management is handled by StockSharp.
Usage notes
Ensure the security metadata (PriceStep, StepPrice, MinVolume, VolumeStep, MaxVolume) is filled in the connector so
that the pip-to-price conversions and volume rounding operate correctly.
The trailing block assumes the pip size equals the exchange price step. Adjust GridStepPips, StopLossPips and
TrailingStopPips for instruments with unconventional tick sizes.
Martingale grids are risky by nature. Test the strategy on historical data and use realistic commission/slippage settings
before deploying to production.
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 IlanImaStrategy : 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 IlanImaStrategy()
{
_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 ilan_ima_strategy(Strategy):
def __init__(self):
super(ilan_ima_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(ilan_ima_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(ilan_ima_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 ilan_ima_strategy()