Esta estrategia es un port StockSharp del asesor experto MetaTrader 5 "RSI EA v2". Automatiza el trading en torno a los cruces de umbral del Relative Strength Index (RSI) mientras replica los controles de gestión de dinero, trailing stop y ventana de trading del asesor original. Por defecto, la estrategia procesa velas de un minuto, pero cualquier tipo de vela puede proporcionarse a través de parámetros.
Lógica de trading
Condiciones de entrada
Las posiciones largas se abren cuando RSI sube por encima del nivel de Compra configurado después de estar por debajo en la vela finalizada anterior, y las horas de trading permiten nuevas órdenes.
Las posiciones cortas se abren cuando RSI cae por debajo del nivel de Venta configurado después de estar por encima previamente, y la ventana de trading está abierta.
Cuando ya existe una posición opuesta, la estrategia dimensiona la nueva orden de mercado para aplanar la exposición actual y establecer la dirección solicitada (solo posiciones netas).
Condiciones de salida
Los niveles de stop-loss y take-profit expresados en pips se adjuntan tan pronto como se detecta una nueva posición.
Un trailing stop imita el EA original: se activa después de que el precio avanza Trailing stop + Trailing step y luego se mueve al menos el paso de trailing.
La lógica opcional de "cerrar por señal" sale de posiciones largas cuando RSI cruza hacia abajo a través del nivel de venta, y sale de posiciones cortas cuando RSI cruza hacia arriba a través del nivel de compra.
Los stops y señales se evalúan solo en velas finalizadas, manteniendo el comportamiento determinista en backtests.
Gestión de riesgo y trading
Stop-loss / Take-profit – definidos en pips, convertidos a incrementos de precio que respetan la precisión del instrumento (incluidos símbolos forex de 3/5 decimales).
Trailing stop – deshabilitado cuando la distancia es cero. Se requiere un paso de trailing positivo siempre que la distancia de trailing sea distinta de cero.
Dimensionamiento de posición – ya sea un volumen fijo o un volumen automático calculado a partir del porcentaje de riesgo y la distancia del stop. El dimensionamiento de riesgo requiere acceso a la cartera y metadatos de paso de precio válidos.
Ventana de trading – filtro diario opcional definido por horas de inicio inclusivas y fin exclusivas (0–23). Cuando inicio es igual a fin, el mercado se considera cerrado.
Parámetros
Nombre
Descripción
OpenBuy / OpenSell
Activa/desactiva entradas largas o cortas independientemente.
CloseBySignal
Habilita salidas en cruces de RSI opuestos.
StopLossPips
Distancia del stop-loss en pips (0 deshabilita el stop).
TakeProfitPips
Distancia del take-profit en pips (0 deshabilita el objetivo).
TrailingStopPips
Distancia del trailing stop en pips. Debe ser cero si no se desea trailing.
TrailingStepPips
Progreso adicional (en pips) requerido antes de mover el trailing stop. Debe ser positivo cuando el trailing está activo.
RsiPeriod
Longitud del indicador RSI.
RsiBuyLevel / RsiSellLevel
Umbrales para entradas/salidas largas y cortas.
UseRiskSizing
Cambia entre volumen fijo y dimensionamiento por porcentaje de riesgo.
FixedVolume
Tamaño de orden base para modo de volumen fijo o respaldo cuando no se puede calcular el dimensionamiento de riesgo.
RiskPercent
Porcentaje del patrimonio del portafolio arriesgado por operación. Usado solo cuando UseRiskSizing es verdadero y existe una distancia de stop positiva.
UseTimeControl
Habilita el filtro de ventana de trading diaria.
StartHour / EndHour
Hora de inicio inclusiva y fin exclusiva (0–23) de la ventana de trading.
CandleType
Tipo de datos de vela que impulsa los cálculos del indicador.
Notas de implementación
Usa la API de suscripción de velas de alto nivel con el binding del indicador RSI.
Convierte las distancias en pips usando la precisión del instrumento (PriceStep y Decimals) para coincidir con la lógica de 3/5 dígitos de MetaTrader.
Normaliza los volúmenes de órdenes al paso de volumen y los límites del instrumento (volumen mín/máx).
La lógica de trailing solo actualiza las referencias de stop internas; las salidas se realizan con órdenes de mercado cuando se superan los niveles.
Mantiene estado separado para posiciones largas y cortas para preservar los niveles de trailing y protectores entre velas.
Uso
Adjunte la estrategia a un conector StockSharp con metadatos de instrumento y portafolio apropiados.
Configure los umbrales, las distancias en pips y la ventana de tiempo opcional para que coincidan con el mercado deseado.
Habilite el dimensionamiento basado en riesgo si la información del portafolio está disponible; de lo contrario déjelo deshabilitado para usar un lote fijo.
Inicie la estrategia – esperará velas finalizadas, aplicará la lógica RSI y gestionará posiciones activas según las protecciones configuradas.
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;
/// <summary>
/// RSI EA v2 strategy using EMA crossover.
/// Buys when fast EMA crosses above slow EMA, sells on reverse.
/// </summary>
public class RsiEaV2Strategy : 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 RsiEaV2Strategy()
{
_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 rsi_ea_v2_strategy(Strategy):
def __init__(self):
super(rsi_ea_v2_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(rsi_ea_v2_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(rsi_ea_v2_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 rsi_ea_v2_strategy()