EMA RSI Estrategia de cruce adaptativo de volatilidad
Esta estrategia es una adaptación directa del asesor experto MetaTrader EA_MARSI_1-02. Intercambia cruces entre dos copias de
Indicador personalizado EMA_RSI_VA de Integer, un promedio móvil adaptable a la volatilidad impulsado por el índice de fuerza relativa (RSI).
Cada vez que la línea lenta cruza la línea rápida, el motor invierte la posición neta, reproduciendo el "volteo en cruce" original.
comportamiento respetando las mejores prácticas de manejo de pedidos de StockSharp.
Mecánica del indicador
El paquete MQL original se envía con un indicador personalizado llamado EMA_RSI_VA. Calcula un precio suavizado EMA cuya efectividad
la longitud está modulada por la distancia de RSI desde su valor neutro. El puerto StockSharp introduce el
EmaRsiVolatilityAdaptiveIndicator clase que replica la fórmula con precisión:
Calcule RSI en la fuente AppliedPrice seleccionada con el período RSIPeriod.
Mida la distancia RSI desde 50 (|RSI - 50| + 1), que actúa como indicador de volatilidad.
Multiplique el período EMA configurado por este multiplicador para obtener una longitud dinámica pdsx.
Aplique la recursión estándar EMA con factor de suavizado 2 / (pdsx + 1) utilizando el precio aplicado de la vela como entrada.
Las excursiones grandes de RSI acortan la ventana de suavizado y hacen que la línea reaccione más rápido; un piso RSI alarga la ventana y humedece
ruido. Tanto la línea lenta como la rápida exponen el conjunto completo de modos de precios admitidos por StockSharp.Messages.AppliedPrice.
Reglas comerciales
Detección de señal
Venta / corto: lento anterior < rápido anterior y lento actual ≥ rápido actual.
Comprar / largo: lento anterior > rápido anterior y lento actual ≤ rápido actual.
Ejecución
La estrategia sólo analiza velas terminadas de la serie de velas configuradas.
Cuando se produce una señal, envía una orden de mercado del tamaño de cerrar la exposición existente y abrir la nueva dirección.
Los límites de cambio se respetan a través de Security.MinVolume, Security.VolumeStep y Security.MaxVolume.
Reversiones
Las órdenes se netean de modo que una sola llamada SellMarket o BuyMarket tome la posición a través de la línea cero, igualando la
MQL comportamiento donde una señal opuesta inmediatamente invierte la operación.
Gestión de riesgos
TakeProfitPoints y StopLossPoints replican los campos TP/SL del asesor experto (expresados en puntos de precio). cuando cualquiera
el valor es distinto de cero, la estrategia inicia el administrador de protección de StockSharp con compensaciones de precios absolutas y useMarketOrders = true
para reflejar el bucle de modificación de límite/detención OrderSend original.
UseBalanceMultiplier implementa la palanca use_Multpl. Cuando está activo, el volumen efectivo de la orden se convierte en
Volume * PortfolioEquity / MaxDrawdown con una abrazadera defensiva para restringir el intercambio.
La llamada a la clase base StartProtection() aún se ejecuta para que los módulos de riesgo externos puedan adjuntar resultados finales o de equilibrio
lógica si es necesario.
Parámetros
Parámetro
Predeterminado
Descripción
Volume
0.1
Tamaño base de la orden de mercado antes de aplicar cualquier multiplicador de saldo.
TakeProfitPoints
0
Distancia de toma de ganancias en puntos del instrumento; 0 desactiva el tramo de obtención de beneficios.
StopLossPoints
0
Distancia de stop-loss en puntos del instrumento; 0 desactiva la parada de protección.
UseBalanceMultiplier
false
Permite un tamaño de posición proporcional al saldo idéntico a use_Multpl en EA.
MaxDrawdown
10000
Denominador del multiplicador de saldo; corresponde al Max_drawdown del EA.
SlowRsiPeriod
310
RSI búsqueda retrospectiva de la línea lenta EMA_RSI_VA.
SlowEmaPeriod
40
Longitud base EMA para la línea lenta antes de la adaptación RSI.
SlowAppliedPrice
Close
Modo de precio reenviado al indicador lento.
FastRsiPeriod
200
RSI búsqueda retrospectiva de la línea rápida EMA_RSI_VA.
FastEmaPeriod
50
Longitud base EMA para la línea rápida antes de la adaptación RSI.
FastAppliedPrice
Close
Modo de precio reenviado al indicador rápido.
CandleType
TimeFrame(1m)
Serie de velas utilizadas para los cálculos.
Notas de implementación
El puerto está escrito con el nivel alto API (SubscribeCandles().Bind(...)) de StockSharp para evitar bucles de indicadores manuales.
Solo se procesan velas completadas que coincidan con las llamadas CopyBuffer(..., 1, 2, ...) en la fuente MQL.
La normalización de volumen utiliza Security.MinVolume, Security.VolumeStep y Security.MaxVolume, lo que evita pedidos no válidos en
intercambios reales.
Se omite intencionalmente una versión de Python según lo solicitado; el directorio solo contiene la implementación y documentación de C#.
El comportamiento resultante refleja la fuente EA al tiempo que expone StockSharpparámetros amigables y controles de riesgo adecuados para
Designer, Runner o cualquier host personalizado creado en 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()