EMA RSI Estratégia de cruzamento adaptativo de volatilidade
Esta estratégia é uma porta direta do consultor especialista MetaTrader EA_MARSI_1-02. Ele comercializa crossovers entre duas cópias de
Indicador EMA_RSI_VA personalizado do número inteiro, uma média móvel adaptável à volatilidade impulsionada pelo Índice de Força Relativa (RSI).
Sempre que a linha lenta cruza a linha rápida, o motor inverte a posição líquida, reproduzindo o "flip on crossover" original
comportamento, respeitando as práticas recomendadas de tratamento de pedidos de StockSharp.
Mecânica do indicador
O pacote MQL original vem com um indicador personalizado chamado EMA_RSI_VA. Ele calcula um EMA suavizado por preço cujo efetivo
o comprimento é modulado pela distância de RSI de seu valor neutro. A porta StockSharp apresenta o
Classe EmaRsiVolatilityAdaptiveIndicator que replica a fórmula com precisão:
Calcule RSI na fonte AppliedPrice selecionada com período RSIPeriod.
Meça a distância RSI de 50 (|RSI - 50| + 1), que atua como um proxy de volatilidade.
Multiplique o período EMA configurado por este multiplicador para obter um comprimento dinâmico pdsx.
Aplique a recursão padrão EMA com fator de suavização 2 / (pdsx + 1) usando o preço aplicado da vela como entrada.
Grandes excursões RSI encurtam a janela de suavização e fazem a linha reagir mais rapidamente; um plano RSI alonga a janela e umedece
barulho. Ambas as linhas lenta e rápida expõem o conjunto completo de modos de preço suportados por StockSharp.Messages.AppliedPrice.
Regras de negociação
Detecção de sinal
Venda / venda a descoberto: anterior lento < anterior rápido e atual lento ≥ atual rápido.
Comprar / longo: lento anterior > rápido anterior e lento atual ≤ rápido atual.
Execução
A estratégia analisa apenas velas finalizadas da série de velas configuradas.
Quando ocorre um sinal, ele envia uma ordem de mercado dimensionada para fechar a exposição existente e abrir a nova direção.
Os limites de troca são respeitados por meio de Security.MinVolume, Security.VolumeStep e Security.MaxVolume.
Reversões
Os pedidos são compensados de modo que uma única chamada SellMarket ou BuyMarket assuma a posição na linha zero, correspondendo ao
Comportamento MQL em que um sinal oposto inverte imediatamente a negociação.
Gestão de risco
TakeProfitPoints e StopLossPoints replicam os campos TP/SL do consultor especialista (expressos em faixas de preço). Quando
o valor for diferente de zero, a estratégia inicia o gerenciador de proteção de StockSharp com compensações de preço absoluto e useMarketOrders = true
para espelhar o loop de modificação de parada/limite OrderSend original.
UseBalanceMultiplier implementa a alternância use_Multpl. Quando ativo, o volume efetivo do pedido torna-se
Volume * PortfolioEquity / MaxDrawdown com uma braçadeira defensiva para troca de restrições.
A chamada da classe base StartProtection() ainda é executada para que os módulos de risco externos possam anexar o trailing ou o ponto de equilíbrio
lógica, se necessário.
Parâmetros
Parâmetro
Padrão
Descrição
Volume
0.1
Tamanho base da ordem de mercado antes de qualquer multiplicador de saldo ser aplicado.
TakeProfitPoints
0
Distância de take-profit em pontos de instrumento; 0 desativa a perna de lucro.
StopLossPoints
0
Distância de stop-loss em pontos de instrumento; 0 desativa a parada protetora.
UseBalanceMultiplier
false
Ativa o dimensionamento de posição proporcional ao equilíbrio idêntico a use_Multpl no EA.
MaxDrawdown
10000
Denominador do multiplicador de saldo; corresponde ao EA do Max_drawdown.
SlowRsiPeriod
310
RSI lookback para a linha EMA_RSI_VA lenta.
SlowEmaPeriod
40
Comprimento base de EMA para a linha lenta antes da adaptação de RSI.
SlowAppliedPrice
Close
Modo de preço encaminhado para o indicador lento.
FastRsiPeriod
200
RSI lookback para a linha rápida EMA_RSI_VA.
FastEmaPeriod
50
Comprimento base de EMA para a linha rápida antes da adaptação de RSI.
FastAppliedPrice
Close
Modo de preço encaminhado para o indicador rápido.
CandleType
TimeFrame(1m)
Série de velas usadas para cálculos.
Notas de implementação
A porta é escrita com StockSharp de alto nível API (SubscribeCandles().Bind(...)) para evitar loops manuais de indicadores.
Apenas velas concluídas são processadas, correspondendo às chamadas CopyBuffer(..., 1, 2, ...) na origem MQL.
A normalização de volume usa Security.MinVolume, Security.VolumeStep e Security.MaxVolume, evitando pedidos inválidos em
verdadeiras trocas.
Uma versão Python é omitida intencionalmente conforme solicitado; o diretório contém apenas a implementação e documentação do C#.
O comportamento resultante espelha a fonte EA enquanto expõe parâmetros amigáveis e controles de risco StockSharp adequados para
Designer, Runner ou qualquer host personalizado criado no StockSharp API.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// EMA RSI VA Crossover: Fast/slow EMA crossover with RSI volatility filter.
/// </summary>
public class EmaRsiVaCrossStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastEmaLength;
private readonly StrategyParam<int> _slowEmaLength;
private readonly StrategyParam<int> _rsiLength;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
public EmaRsiVaCrossStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_fastEmaLength = Param(nameof(FastEmaLength), 10)
.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");
_slowEmaLength = Param(nameof(SlowEmaLength), 40)
.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");
_rsiLength = Param(nameof(RsiLength), 14)
.SetDisplay("RSI Length", "RSI period.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastEmaLength
{
get => _fastEmaLength.Value;
set => _fastEmaLength.Value = value;
}
public int SlowEmaLength
{
get => _slowEmaLength.Value;
set => _slowEmaLength.Value = value;
}
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0;
_prevSlow = 0;
_entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = 0;
_prevSlow = 0;
_entryPrice = 0;
var fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
var slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var atr = new AverageTrueRange { Length = 14 };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastEma, slowEma, rsi, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastEma);
DrawIndicator(area, slowEma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal rsiVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0)
{
_prevFast = fastVal;
_prevSlow = slowVal;
return;
}
var close = candle.ClosePrice;
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 3m || close <= _entryPrice - atrVal * 2m || (fastVal < slowVal && _prevFast >= _prevSlow))
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 3m || close >= _entryPrice + atrVal * 2m || (fastVal > slowVal && _prevFast <= _prevSlow))
{
BuyMarket();
_entryPrice = 0;
}
}
if (Position == 0)
{
if (fastVal > slowVal && _prevFast <= _prevSlow && rsiVal > 40 && rsiVal < 70)
{
_entryPrice = close;
BuyMarket();
}
else if (fastVal < slowVal && _prevFast >= _prevSlow && rsiVal > 30 && rsiVal < 60)
{
_entryPrice = close;
SellMarket();
}
}
_prevFast = fastVal;
_prevSlow = slowVal;
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import ExponentialMovingAverage, RelativeStrengthIndex, AverageTrueRange
class ema_rsi_va_cross_strategy(Strategy):
def __init__(self):
super(ema_rsi_va_cross_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._fast_ema_length = self.Param("FastEmaLength", 10) \
.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators")
self._slow_ema_length = self.Param("SlowEmaLength", 40) \
.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period.", "Indicators")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastEmaLength(self):
return self._fast_ema_length.Value
@property
def SlowEmaLength(self):
return self._slow_ema_length.Value
@property
def RsiLength(self):
return self._rsi_length.Value
def OnStarted2(self, time):
super(ema_rsi_va_cross_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._fast_ema = ExponentialMovingAverage()
self._fast_ema.Length = self.FastEmaLength
self._slow_ema = ExponentialMovingAverage()
self._slow_ema.Length = self.SlowEmaLength
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.RsiLength
self._atr = AverageTrueRange()
self._atr.Length = 14
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._fast_ema, self._slow_ema, self._rsi, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, fast_val, slow_val, rsi_val, atr_val):
if candle.State != CandleStates.Finished:
return
fv = float(fast_val)
sv = float(slow_val)
rv = float(rsi_val)
av = float(atr_val)
if self._prev_fast == 0 or self._prev_slow == 0 or av <= 0:
self._prev_fast = fv
self._prev_slow = sv
return
close = float(candle.ClosePrice)
if self.Position > 0:
if close >= self._entry_price + av * 3.0 or close <= self._entry_price - av * 2.0 or (fv < sv and self._prev_fast >= self._prev_slow):
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if close <= self._entry_price - av * 3.0 or close >= self._entry_price + av * 2.0 or (fv > sv and self._prev_fast <= self._prev_slow):
self.BuyMarket()
self._entry_price = 0.0
if self.Position == 0:
if fv > sv and self._prev_fast <= self._prev_slow and rv > 40 and rv < 70:
self._entry_price = close
self.BuyMarket()
elif fv < sv and self._prev_fast >= self._prev_slow and rv > 30 and rv < 60:
self._entry_price = close
self.SellMarket()
self._prev_fast = fv
self._prev_slow = sv
def OnReseted(self):
super(ema_rsi_va_cross_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
def CreateClone(self):
return ema_rsi_va_cross_strategy()