La Estrategia de Ilan iMA es un port de StockSharp del asesor experto de MetaTrader 5 Ilan iMA.mq5. El asesor combina un filtro de tendencia de media móvil desplazada con una cuadrícula de promediación estilo martingala. La versión de StockSharp reimplementa las mismas ideas con la API de alto nivel: cuando la media móvil ponderada confirma una tendencia, la estrategia abre una orden de mercado y sigue añadiendo operaciones cada vez que el precio se mueve contra la posición por un paso configurable. Toda la cesta se cierra cuando se alcanza un objetivo de ganancia, trailing stop o stop-loss explícito, reproduciendo el modelo de gestión de dinero del EA original.
Lógica de trading
Suscribirse al marco temporal seleccionado (CandleType) y alimentar una media móvil configurable (MaMethod, MaPeriod, PriceMode). Un MaShift positivo desplaza el indicador hacia adelante, por lo que la estrategia evalúa valores históricos para imitar el comportamiento de MT5.
Esperar a que la vela cierre. Solo las barras finalizadas generan señales y actualizan la lógica de trailing/stop.
Detectar la tendencia comparando cuatro valores consecutivos de media móvil desplazados por MaShift barras:
valores estrictamente decrecientes señalan una tendencia bajista;
valores estrictamente crecientes señalan una tendencia alcista.
Cuando no hay ninguna cesta abierta:
en tendencia bajista, si el cierre está por encima del valor de la media móvil, abrir un short con StartVolume;
en tendencia alcista, si el cierre está por debajo del valor de la media móvil, abrir un long con StartVolume.
Cuando existe una cesta:
si el precio se mueve contra la posición al menos GridStepPips, abrir otra orden cuyo tamaño crece por LotExponent pero está limitado por LotMaximum y los límites de volumen del intercambio;
el precio de entrada promedio, el precio de compra más bajo y el precio de venta más alto se rastrean internamente para mantener el comportamiento cerca de la lógica de MT5.
Condiciones de cierre:
una vez que el beneficio flotante de una cesta con más de una operación alcanza ProfitMinimum (en moneda de cuenta), cerrar todas las órdenes en esa dirección;
si el beneficio flotante alcanza TakeProfitPips o la pérdida llega a StopLossPips, cerrar la cesta;
la protección de trailing se activa después de TrailingStopPips + TrailingStepPips puntos de movimiento favorable y se mueve en pasos de TrailingStepPips.
Gestión de riesgos y dimensionamiento
StartVolume replica el parámetro StartLots de MT5. Cada orden adicional multiplica el tamaño anterior por LotExponent respetando LotMaximum y los límites del lugar (Security.MinVolume, Security.VolumeStep, Security.MaxVolume).
ProfitMinimum preserva el comportamiento de "liberación de bloqueo" de la versión MT5: una vez que la cuadrícula se recuperó de una cobertura e imprime el beneficio solicitado, todas las operaciones en esa dirección se cierran.
Las distancias de stop-loss y take-profit se miden en pips (StopLossPips, TakeProfitPips). El método helper convierte pips en pasos de precio del intercambio usando Security.PriceStep.
El bloque de trailing emula la implementación de MT5: el trailing comienza solo después de que el precio supera TrailingStopPips + TrailingStepPips y se actualiza en pasos discretos para evitar ajustes prematuros del stop.
Parámetros
Nombre
Tipo
Por defecto
Contraparte MT5
Descripción
MaPeriod
int
15
Inp_MA_ma_period
Período de la media móvil del filtro de tendencia.
MaShift
int
5
Inp_MA_ma_shift
Desplazamiento hacia adelante de la línea de media móvil en barras.
MaMethod
MovingAverageMethod
Weighted
Inp_MA_ma_method
Algoritmo de suavizado (SMA, EMA, SMMA, LWMA).
PriceMode
CandlePrice
Weighted
Inp_MA_applied_price
Precio de vela alimentado al indicador.
StartVolume
decimal
1
InpStartLots
Volumen base de la orden para la primera operación de una cesta.
GridStepPips
decimal
30
InpStep
Distancia (en pips) entre entradas de promediación.
LotExponent
decimal
1.6
InpLotExponent
Multiplicador aplicado al tamaño de la orden anterior.
LotMaximum
decimal
15
InpLotMaximum
Límite máximo para un solo volumen de orden.
ProfitMinimum
decimal
15
InpProfitMinimum
Beneficio flotante mínimo requerido para cerrar una cesta con varias operaciones.
StopLossPips
decimal
0
InpStopLoss
Distancia del stop-loss expresada en pips (0 deshabilita el stop).
TakeProfitPips
decimal
100
InpTakeProfit
Distancia del take-profit expresada en pips.
TrailingStopPips
decimal
15
InpTrailingStop
Umbral de beneficio que activa el trailing stop.
TrailingStepPips
decimal
5
InpTrailingStep
Beneficio adicional mínimo antes de que el trailing stop se mueva de nuevo.
CandleType
DataType
Marco temporal de 15 minutos
período del gráfico
Marco temporal utilizado para el cálculo de señales.
Diferencias del EA original
StockSharp funciona en un entorno de netting, por lo que solo existe una posición neta por dirección. La estrategia mantiene una lista interna de precios de entrada y volúmenes para emular la contabilidad de cesta de MT5.
Los límites de volumen específicos del intercambio siempre se respetan al redondear volúmenes, mientras que el código de MT5 dependía de verificaciones manuales. Esto previene órdenes que serían rechazadas por el conector del broker.
La lógica de stop-loss, take-profit y trailing se expresa a través de salidas de mercado en lugar de modificar posiciones existentes de MT5. El comportamiento funcional sigue siendo el mismo, pero la gestión de órdenes es manejada por StockSharp.
Notas de uso
Asegúrese de que los metadatos del instrumento (PriceStep, StepPrice, MinVolume, VolumeStep, MaxVolume) estén completos en el conector para que las conversiones de pip a precio y el redondeo de volumen funcionen correctamente.
El bloque de trailing asume que el tamaño del pip es igual al paso de precio del intercambio. Ajuste GridStepPips, StopLossPips y TrailingStopPips para instrumentos con tamaños de tick no convencionales.
Las cuadrículas de martingala son inherentemente arriesgadas. Pruebe la estrategia en datos históricos y use configuraciones realistas de comisión/slippage antes de implementar en producción.
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()