A Estratégia de Ilan iMA é um port do StockSharp do consultor especialista de MetaTrader 5 Ilan iMA.mq5. O consultor combina um filtro de tendência de média móvel deslocada com uma grade de média estilo martingale. A versão StockSharp reimplementa as mesmas ideias com a API de alto nível: quando a média móvel ponderada confirma uma tendência, a estratégia abre uma ordem de mercado e continua adicionando operações sempre que o preço se move contra a posição por um passo configurável. Todo o cesto é fechado quando um alvo de lucro, trailing stop ou stop-loss explícito é atingido, reproduzindo o modelo de gestão de dinheiro do EA original.
Lógica de trading
Assinar o período selecionado (CandleType) e alimentar uma média móvel configurável (MaMethod, MaPeriod, PriceMode). Um MaShift positivo desloca o indicador para frente, de modo que a estratégia avalia valores históricos para imitar o comportamento do MT5.
Aguardar o fechamento da vela. Apenas barras finalizadas geram sinais e atualizam a lógica de trailing/stop.
Detectar a tendência comparando quatro valores consecutivos de média móvel deslocados por MaShift barras:
valores estritamente decrescentes sinalizam uma tendência de baixa;
valores estritamente crescentes sinalizam uma tendência de alta.
Quando nenhum cesto está aberto:
em tendência de baixa, se o fechamento estiver acima do valor da média móvel, abrir um vendido com StartVolume;
em tendência de alta, se o fechamento estiver abaixo do valor da média móvel, abrir um comprado com StartVolume.
Quando um cesto existe:
se o preço se mover contra a posição pelo menos GridStepPips, abrir outra ordem cujo tamanho cresce por LotExponent, mas é limitado por LotMaximum e os limites de volume da bolsa;
o preço médio de entrada, o menor preço de compra e o maior preço de venda são rastreados internamente para manter o comportamento próximo à lógica do MT5.
Condições de fechamento:
uma vez que o lucro flutuante de um cesto com mais de uma operação atinge ProfitMinimum (na moeda da conta), fechar todas as ordens nessa direção;
se o lucro flutuante atinge TakeProfitPips ou a perda chega a StopLossPips, fechar o cesto;
a proteção de trailing fica ativa após TrailingStopPips + TrailingStepPips pontos de movimento favorável e se move em passos de TrailingStepPips.
Gestão de risco e dimensionamento
StartVolume replica o parâmetro StartLots do MT5. Cada ordem adicional multiplica o tamanho anterior por LotExponent respeitando LotMaximum e os limites do local (Security.MinVolume, Security.VolumeStep, Security.MaxVolume).
ProfitMinimum preserva o comportamento de "liberação de bloqueio" da versão MT5: uma vez que a grade se recuperou de uma cobertura e imprime o lucro solicitado, todas as operações nessa direção são fechadas.
As distâncias de stop-loss e take-profit são medidas em pips (StopLossPips, TakeProfitPips). O método helper converte pips em passos de preço da bolsa usando Security.PriceStep.
O bloco de trailing emula a implementação do MT5: o trailing começa apenas após o preço exceder TrailingStopPips + TrailingStepPips e é atualizado em passos discretos para evitar ajustes prematuros do stop.
Parâmetros
Nome
Tipo
Padrão
Contraparte MT5
Descrição
MaPeriod
int
15
Inp_MA_ma_period
Período da média móvel do filtro de tendência.
MaShift
int
5
Inp_MA_ma_shift
Deslocamento para frente da linha de média móvel em barras.
MaMethod
MovingAverageMethod
Weighted
Inp_MA_ma_method
Algoritmo de suavização (SMA, EMA, SMMA, LWMA).
PriceMode
CandlePrice
Weighted
Inp_MA_applied_price
Preço de vela alimentado ao indicador.
StartVolume
decimal
1
InpStartLots
Volume base da ordem para a primeira operação em um cesto.
GridStepPips
decimal
30
InpStep
Distância (em pips) entre entradas de média.
LotExponent
decimal
1.6
InpLotExponent
Multiplicador aplicado ao tamanho da ordem anterior.
LotMaximum
decimal
15
InpLotMaximum
Limite máximo para um único volume de ordem.
ProfitMinimum
decimal
15
InpProfitMinimum
Lucro flutuante mínimo necessário para fechar um cesto com várias operações.
StopLossPips
decimal
0
InpStopLoss
Distância do stop-loss em pips (0 desabilita o stop).
TakeProfitPips
decimal
100
InpTakeProfit
Distância do take-profit em pips.
TrailingStopPips
decimal
15
InpTrailingStop
Limiar de lucro que ativa o trailing stop.
TrailingStepPips
decimal
5
InpTrailingStep
Lucro adicional mínimo antes do trailing stop se mover novamente.
CandleType
DataType
Período de 15 minutos
período do gráfico
Período usado para o cálculo de sinais.
Diferenças do EA original
O StockSharp funciona em um ambiente de netting, portanto, existe apenas uma posição líquida por direção. A estratégia mantém uma lista interna de preços de entrada e volumes para emular a contabilidade de cesto do MT5.
Os limites de volume específicos da bolsa são sempre respeitados ao arredondar volumes, enquanto o código do MT5 dependia de verificações manuais. Isso evita ordens que seriam rejeitadas pelo conector do broker.
A lógica de stop-loss, take-profit e trailing é expressa por meio de saídas de mercado em vez de modificar posições existentes do MT5. O comportamento funcional permanece o mesmo, mas o gerenciamento de ordens é tratado pelo StockSharp.
Notas de uso
Certifique-se de que os metadados do instrumento (PriceStep, StepPrice, MinVolume, VolumeStep, MaxVolume) estão preenchidos no conector para que as conversões de pip para preço e o arredondamento de volume funcionem corretamente.
O bloco de trailing assume que o tamanho do pip é igual ao passo de preço da bolsa. Ajuste GridStepPips, StopLossPips e TrailingStopPips para instrumentos com tamanhos de tick não convencionais.
As grades de martingale são inerentemente arriscadas. Teste a estratégia em dados históricos e use configurações realistas de comissão/slippage antes de implantar em produção.
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()