Estratégia Eliot Wave (portada do MQL4 "Eliot Wave I")
Visão geral
A estratégia Eliot Wave é uma versão para a API do StockSharp do expert advisor original do MetaTrader 4 "Eliot Wave I". O sistema combina um cruzamento rápido/lento de média móvel ponderada linear (LWMA) com confirmação de momentum multitemporal e um filtro MACD muito lento. O objetivo é identificar movimentos impulsivos na direção da tendência predominante, mantendo o risco limitado por regras de proteção incorporadas.
Indicadores centrais
LWMA rápida (padrão 6) - acompanha a direção de curto prazo usando o preço típico (High + Low + Close) / 3.
LWMA lenta (padrão 85) - mede a tendência mais ampla no mesmo período.
Momentum (período padrão 14) - avaliado em um período mais alto e convertido em desvio relativo ao nível neutro 100. Uma leitura acima do limite configurado indica um impulso suficientemente forte.
MACD (12, 26, 9) - calculado em um período muito lento (mensal por padrão) e usado como filtro de longo prazo. A estratégia só compra quando a linha principal MACD está acima da linha de sinal e vende quando está abaixo.
Parâmetros
Nome
Descrição
Padrão
Base Candle
Período primário para processamento da LWMA.
Candles de 15 minutos
Momentum Candle
Período mais alto usado para confirmação de momentum.
Candles de 1 hora
MACD Candle
Período muito lento para o filtro de tendência MACD.
Candles de 30 dias
Fast LWMA
Comprimento da média móvel ponderada linear rápida.
6
Slow LWMA
Comprimento da média móvel ponderada linear lenta.
85
Momentum Period
Retrospectiva do indicador de momentum no período de confirmação.
14
Momentum Buy Threshold
Desvio mínimo acima de 100 necessário para validar uma configuração comprada.
0.3
Momentum Sell Threshold
Desvio mínimo acima de 100 necessário para validar uma configuração vendida.
0.3
Stop Loss (pts)
Distância do stop de proteção expressa em pontos do instrumento.
20
Take Profit (pts)
Distância do alvo expressa em pontos do instrumento.
50
Trade Volume
Tamanho da ordem para cada entrada.
1 lote
Max Position
Exposição líquida absoluta permitida; impede que a estratégia ultrapasse o limite Max_Trades do EA MQL.
10 lotes
Todos os parâmetros são implementados como StrategyParam<T>, para que possam ser otimizados diretamente no Designer ou Runner.
Regras de negociação
Filtro de tendência e estrutura
A LWMA rápida deve permanecer acima da LWMA lenta para considerar operações compradas.
A LWMA rápida deve permanecer abaixo da LWMA lenta para considerar vendidas.
Os dois últimos candles concluídos devem se sobrepor (Low[2] < High[1] para compras, Low[1] < High[2] para vendas), replicando o requisito de consolidação do EA.
Confirmação de momentum
O momentum do período mais alto é transformado em valores abs(momentum - 100).
Se qualquer um dos três últimos valores exceder o limite configurado, o impulso é considerado válido.
Filtro de tendência macro
Compras exigem que a linha principal MACD esteja acima da linha de sinal no período lento.
Vendas exigem que a linha principal MACD esteja abaixo da linha de sinal.
Execução de ordens
Quando todas as condições se alinham, a estratégia envia uma ordem a mercado dimensionada para inverter a posição atual e adicionar o volume de negociação configurado.
Viradas de posição são suportadas para que o comportamento corresponda à lógica de preço médio do EA original.
Gestão de risco
StartProtection aplica automaticamente distâncias de stop-loss e take-profit em pontos do instrumento.
Lógica adicional de saída fecha posições compradas quando a LWMA rápida cai abaixo da LWMA lenta ou quando o filtro MACD fica baixista (e vice-versa para vendidas). Isso espelha os blocos de saída MQL.
O parâmetro Max Position impede que a estratégia acumule exposição além do limite configurado, respeitando a restrição Max_Trades do EA.
Diferenças em relação ao EA original
Verificações gráficas de linhas de tendência e notificações manuais de operação foram removidas porque são específicas do MetaTrader e não têm equivalente no StockSharp.
Variantes de break-even e trailing-stop complexo do script MQL são substituídas pelo mecanismo mais simples StartProtection. Usuários podem estender a estratégia se esses comportamentos forem necessários.
Proteção de equity baseada em dinheiro não é implementada; o risco é controlado por stops fixos e pelo limite de posição.
Notas de uso
Anexe a estratégia a um instrumento líquido e garanta que os três fluxos de candles estejam disponíveis.
Ajuste Trade Volume, distâncias de stop/alvo e limites de acordo com a volatilidade do mercado negociado.
Otimize limites separadamente para impulsos altistas e baixistas se o instrumento apresentar comportamento assimétrico.
Considere habilitar os visuais de gráfico incorporados (candles, LWMAs, marcadores de operação) para facilitar a depuração.
Esta versão foca em reproduzir a lógica de sinais do EA original usando a API de alto nível do StockSharp, mantendo a implementação idiomática e fácil de manter.
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 EliotWaveStrategy : 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 EliotWaveStrategy()
{
_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_wave_strategy(Strategy):
def __init__(self):
super(eliot_wave_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_wave_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_wave_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_wave_strategy()