Esta estrategia replica el Asesor Experto "iMA iSAR EA" de MetaTrader 5 usando la API de alto nivel de StockSharp. Combina un filtro de triple media móvil ponderada con dos trayectorias de SAR Parabólico para identificar rupturas de momentum. Se abre una posición larga cuando la media móvil ponderada más rápida permanece por encima de las otras dos medias y ambas trayectorias SAR están por debajo del cierre de la vela. Una condición espejo genera entradas cortas. Los stops protectores, los objetivos de beneficio y un trailing stop opcional se gestionan en puntos de precio (pips).
La implementación funciona en una única serie de velas configurable a través del parámetro CandleType. Todos los indicadores se evalúan en este marco temporal. El experto de MetaTrader original usaba múltiples marcos temporales para sus indicadores; en StockSharp este comportamiento se aproxima permitiendo desplazamientos individuales de las medias móviles que pueden retrasar cada señal un número de barras completadas.
Reglas de trading
Indicadores
Tres medias móviles ponderadas (Fast, Normal, Slow) calculadas en el flujo de velas configurado. Los desplazamientos de barras opcionales emulan los buffers retrasados del código MQ5 original.
Dos indicadores SAR Parabólico (FastSAR, NormalSAR) comparten el mismo flujo de velas pero tienen parámetros de aceleración y máximo independientes.
Condiciones de entrada
Largo: la MA Fast está por encima de Normal y Slow, mientras que ambos valores SAR están por debajo del cierre de la vela.
Corto: la MA Fast está por debajo de Normal y Slow, mientras que ambos valores SAR están por encima del cierre de la vela.
Cuando aparece una señal de reversión, la estrategia cierra cualquier exposición opuesta y cambia de dirección en una única orden de mercado.
Controles de riesgo
Los niveles fijos de stop-loss y take-profit se expresan en pips (múltiplos del paso de precio del instrumento). Se evalúan en velas completadas.
Trailing stop opcional: una vez habilitado, el stop sigue el precio de cierre a una distancia configurable y solo avanza después de moverse la cantidad especificada por el paso de trailing.
Los volúmenes se ajustan a la configuración VolumeStep, MinVolume y MaxVolume del instrumento antes de enviar las órdenes.
Parámetros
Nombre
Tipo
Predeterminado
Descripción
Volume
decimal
0.1
Tamaño base de la orden. Se incrementa automáticamente para cubrir una posición opuesta al cambiar de dirección.
StopLossPips
decimal
50
Distancia de stop protector en pips. Establecer en 0 para deshabilitar.
TakeProfitPips
decimal
50
Distancia del objetivo de beneficio en pips. Establecer en 0 para deshabilitar.
UseTrailing
bool
true
Habilita la gestión dinámica del trailing stop.
TrailingStopPips
decimal
25
Distancia entre el precio y el trailing stop, en pips.
TrailingStepPips
decimal
5
Movimiento favorable mínimo (pips) antes de que el trailing stop avance.
CandleType
DataType
TimeFrameCandle 15m
Serie de velas usada para todos los cálculos.
FastMaPeriod
int
10
Período de la media móvil ponderada rápida.
FastMaShift
int
0
Número de barras completadas para desplazar la MA rápida hacia atrás.
NormalMaPeriod
int
30
Período de la media móvil ponderada normal.
NormalMaShift
int
3
Número de barras completadas para desplazar la MA normal hacia atrás.
SlowMaPeriod
int
60
Período de la media móvil ponderada lenta.
SlowMaShift
int
6
Número de barras completadas para desplazar la MA lenta hacia atrás.
FastSarStep
decimal
0.02
Factor de aceleración para el SAR Parabólico rápido.
FastSarMax
decimal
0.2
Aceleración máxima para el SAR Parabólico rápido.
NormalSarStep
decimal
0.02
Factor de aceleración para el SAR Parabólico normal.
NormalSarMax
decimal
0.2
Aceleración máxima para el SAR Parabólico normal.
Notas
Las verificaciones del trailing stop se realizan al cierre de la vela. Si se requiere precisión intrabarra, combine la estrategia con un componente protector a nivel de tick.
El tamaño del pip equivale al paso de precio del instrumento cuando está disponible. De lo contrario, se asume un tick estándar de 0.0001 para pares FX.
Para mayor consistencia con la versión de MetaTrader, todas las señales de indicadores operan en velas cerradas. Las transacciones pendientes no se preparan; cada señal envía una orden de mercado inmediata.
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 ImaIsarEaStrategy : 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 ImaIsarEaStrategy()
{
_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 ima_isar_ea_strategy(Strategy):
def __init__(self):
super(ima_isar_ea_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(ima_isar_ea_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(ima_isar_ea_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 ima_isar_ea_strategy()