La estrategia Eliot Waves replica el comportamiento del asesor experto de MetaTrader "Eliot Waves" usando la API de alto nivel de StockSharp. El algoritmo combina detección de tendencia mediante dos medias móviles ponderadas lineales con confirmación de momentum y salidas basadas en volatilidad. Todos los cálculos se realizan sobre velas cerradas de un marco temporal configurable para reflejar la ejecución determinista del robot original.
Lógica de trading
Filtro de tendencia. La estrategia compara una LWMA rápida (período predeterminado 6) con una LWMA lenta (período predeterminado 85), calculadas sobre el precio típico de la vela. Las operaciones largas solo se consideran cuando la LWMA rápida cierra por encima de la LWMA lenta, mientras que las cortas requieren la alineación opuesta.
Confirmación de momentum. Un indicador de momentum (período predeterminado 14) debe mostrar al menos una de las tres últimas lecturas desviándose del valor neutral 100 por más que el umbral configurado (predeterminado 0.3). Esto replica el EA original, que comprobaba la diferencia absoluta de tres valores recientes de momentum.
Filtro de estructura de vela. Las señales largas requieren que el mínimo de la vela de hace dos barras esté por debajo del máximo de la vela anterior. Las señales cortas exigen que el mínimo de la vela anterior permanezca por debajo del máximo de dos barras atrás. Esto captura el filtro de estilo divergencia presente en el código fuente.
Escalado de posición. Cada señal intenta añadir un paso de volumen fijo (predeterminado 0.1) hasta el número máximo de pasos (predeterminado 10). La estrategia cierra cualquier exposición opuesta antes de abrir una nueva posición para mantenerse alineada con la implementación de MetaTrader.
Gestión de riesgos
Stop-loss y take-profit. Los objetivos de precio se definen en pips relativos al precio medio de entrada y se recalculan cada vez que cambia la posición.
Trailing stop. Cuando está habilitado, el stop se arrastra detrás del precio cuando la ganancia abierta supera la distancia trailing.
Break-even. Después de alcanzar el disparador configurado, el stop se mueve al precio de entrada más un desplazamiento opcional, protegiendo ganancias acumuladas.
Salida por Bollinger Band. Las posiciones largas salen cuando el precio toca la banda inferior de un canal Bollinger de 20 períodos, mientras que las cortas salen al tocar la banda superior. Esto refleja la lógica de cierre basada en volatilidad del script MQL.
Confirmación MACD. Las posiciones también se cierran ante un cruce de señal MACD (12, 26, 9) contra la dirección de la operación, reproduciendo la salida MACD mensual del experto original.
Interruptor de salida forzada. El parámetro EnableExitStrategy permite que un operador liquide al instante toda posición abierta.
Parámetros
Nombre
Descripción
Predeterminado
TradeVolume
Volumen usado para cada paso de posición.
0.1
CandleType
Marco temporal de vela empleado para todos los indicadores.
Velas de 15 minutos
FastMaPeriod / SlowMaPeriod
Períodos de las medias móviles ponderadas lineales rápida y lenta.
6 / 85
MomentumPeriod
Retrospectiva de momentum usada en el bloque de confirmación.
14
MomentumThreshold
Desviación mínima desde 100 requerida para habilitar el trading.
0.3
StopLossPips / TakeProfitPips
Distancias de stop-loss y take-profit expresadas en pips.
20 / 50
EnableTrailing / TrailingStopPips
Interruptor y distancia para la función trailing stop.
Interruptor de activación, disparador y desplazamiento de break-even en pips.
true, 30, 30
MaxPositions
Número máximo de pasos de volumen permitidos.
10
EnableExitStrategy
Fuerza a la estrategia a dejar la posición plana cuando está habilitado.
false
Notas de conversión
La implementación StockSharp se apoya en la canalización de alto nivel SubscribeCandles().BindEx(...) para procesar todos los indicadores simultáneamente y operar estrictamente sobre velas completadas.
La conversión de pips usa el paso de precio del instrumento siempre que sea posible y recurre al valor del paso de precio cuando el bróker no expone precisión de pip, coincidiendo con el comportamiento adaptativo de la versión MetaTrader.
La lógica de stop-loss, take-profit, trailing y break-even se gestiona internamente en lugar de usar órdenes de bróker, para mantener un comportamiento determinista durante backtests.
Las llamadas de alerta, correo electrónico y notificación del experto MQL se eliminaron, ya que StockSharp proporciona sus propias capacidades de registro.
Consejos de uso
Seleccione el instrumento deseado y ajuste TradeVolume y MaxPositions para adecuarlos al tamaño de la cuenta. Los valores predeterminados reproducen el escalado conservador usado en el EA.
Optimice MomentumThreshold, StopLossPips y TrailingStopPips con datos históricos si el mercado objetivo muestra características de volatilidad diferentes.
Al probar en múltiples símbolos, asegúrese de que el instrumento exponga un paso de precio correcto para que las distancias basadas en pips se conviertan con precisión.
Monitorice el log para la advertencia "Unable to determine pip size from security settings". Si aparece, considere configurar el instrumento con el paso de precio correcto para evitar niveles de riesgo desajustados.
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 EliotWavesStrategy : 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 EliotWavesStrategy()
{
_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 eliot_waves_strategy(Strategy):
def __init__(self):
super(eliot_waves_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(eliot_waves_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(eliot_waves_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 eliot_waves_strategy()