Estrategia RSI Trader
Descripción general
Esta estrategia replica el asesor experto de MetaTrader "RSI trader v0.15" en la API de alto nivel de StockSharp. Alinea la dirección de la tendencia entre la acción del precio y un Índice de Fuerza Relativa (RSI) suavizado. El trading se realiza en un único instrumento usando velas de una hora por defecto, pero el marco temporal es configurable a través del parámetro CandleType.
Lógica de trading
- Calcular un RSI estándar con un período configurable.
- Suavizar el RSI con dos medias móviles simples (SMA): un promedio de señal rápido y uno de confirmación más lento.
- Rastrear dos medias móviles del precio de cierre: una media móvil simple corta y una media móvil ponderada larga para aproximar el par SMA/LWMA del MQL original.
- Generar estados de tendencia en cada vela terminada:
- Alineación alcista: SMA de precio corta por encima de la larga y SMA RSI rápida por encima de la lenta.
- Alineación bajista: SMA de precio corta por debajo de la larga y SMA RSI rápida por debajo de la lenta.
- Lateral / desacuerdo: las medias móviles apuntan en direcciones opuestas, señalando que no hay tendencia clara.
- Actuar sobre el estado detectado:
- Abrir una posición larga cuando aparece alineación alcista y no hay posición actualmente abierta.
- Abrir una posición corta cuando aparece alineación bajista y no hay posición actualmente abierta.
- Cerrar inmediatamente cualquier posición abierta cuando se detecta el estado lateral, reflejando la salida protectora de la versión MQL.
- El modo de reversión opcional invierte todas las direcciones de entrada, permitiendo al usuario operar contra la tendencia con respecto a las señales detectadas.
La estrategia respeta el manejo de protección incorporado de StockSharp y requiere velas completadas antes de tomar cualquier acción.
Parámetros
| Parámetro |
Descripción |
Predeterminado |
RsiPeriod |
Período de retrospección usado para el cálculo del RSI. |
14 |
ShortRsiMaPeriod |
Longitud de la SMA rápida aplicada a los valores del RSI. |
9 |
LongRsiMaPeriod |
Longitud de la SMA lenta aplicada a los valores del RSI. |
45 |
ShortPriceMaPeriod |
Longitud de la SMA corta aplicada a los precios de cierre. |
9 |
LongPriceMaPeriod |
Longitud de la media móvil ponderada larga aplicada a los precios. |
45 |
Reverse |
Cuando es true, las órdenes de compra y venta se intercambian (refleja la entrada "Reverse" original). |
false |
CandleType |
Tipo de datos para velas de precio. Predeterminado es marco temporal de una hora. |
1h |
Todos los parámetros enteros exponen rangos de optimización que reflejan la flexibilidad de los ajustes de entrada del experto de MetaTrader.
Gestión de riesgo
- Las posiciones se cierran tan pronto como los trends de precio y RSI no coinciden (estado lateral), reproduciendo el comportamiento de salida inmediata del EA.
StartProtection() se habilita al inicio para cooperar con la infraestructura protectora de StockSharp.
Notas
- La estrategia depende de la propiedad base
Volume de Strategy para definir el tamaño de la operación.
- Solo se procesan velas completadas; las actualizaciones parciales se ignoran para evitar señales prematuras.
- Se usa la media móvil ponderada para coincidir con la LWMA larga original aplicada a los cierres de precio.
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using StockSharp.Algo.Candles;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// RSI trader strategy aligning price and RSI moving-average trends.
/// </summary>
public class RsiTraderStrategy : Strategy
{
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _shortRsiMaPeriod;
private readonly StrategyParam<int> _longRsiMaPeriod;
private readonly StrategyParam<int> _shortPriceMaPeriod;
private readonly StrategyParam<int> _longPriceMaPeriod;
private readonly StrategyParam<bool> _reverse;
private readonly StrategyParam<DataType> _candleType;
private readonly List<decimal> _closes = new();
private readonly List<decimal> _rsiValues = new();
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public int ShortRsiMaPeriod { get => _shortRsiMaPeriod.Value; set => _shortRsiMaPeriod.Value = value; }
public int LongRsiMaPeriod { get => _longRsiMaPeriod.Value; set => _longRsiMaPeriod.Value = value; }
public int ShortPriceMaPeriod { get => _shortPriceMaPeriod.Value; set => _shortPriceMaPeriod.Value = value; }
public int LongPriceMaPeriod { get => _longPriceMaPeriod.Value; set => _longPriceMaPeriod.Value = value; }
public bool Reverse { get => _reverse.Value; set => _reverse.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public RsiTraderStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 14).SetDisplay("RSI Period", "RSI calculation length", "RSI").SetGreaterThanZero();
_shortRsiMaPeriod = Param(nameof(ShortRsiMaPeriod), 12).SetDisplay("Short RSI MA", "Short moving average on RSI", "RSI").SetGreaterThanZero();
_longRsiMaPeriod = Param(nameof(LongRsiMaPeriod), 60).SetDisplay("Long RSI MA", "Long moving average on RSI", "RSI").SetGreaterThanZero();
_shortPriceMaPeriod = Param(nameof(ShortPriceMaPeriod), 12).SetDisplay("Short Price MA", "Short simple moving average", "Price").SetGreaterThanZero();
_longPriceMaPeriod = Param(nameof(LongPriceMaPeriod), 60).SetDisplay("Long Price MA", "Long weighted moving average", "Price").SetGreaterThanZero();
_reverse = Param(nameof(Reverse), false).SetDisplay("Reverse", "Flip buy/sell signals", "Trading");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame()).SetDisplay("Candle Type", "Primary candle type", "Data");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, CandleType);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_closes.Clear();
_rsiValues.Clear();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_closes.Add(candle.ClosePrice);
var maxCache = Math.Max(LongPriceMaPeriod, Math.Max(LongRsiMaPeriod + RsiPeriod, 300));
if (_closes.Count > maxCache)
_closes.RemoveAt(0);
var rsi = CalculateRsi();
if (rsi is null)
return;
_rsiValues.Add(rsi.Value);
if (_rsiValues.Count > maxCache)
_rsiValues.RemoveAt(0);
if (_rsiValues.Count < LongRsiMaPeriod || _closes.Count < LongPriceMaPeriod)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var shortRsi = AverageLast(_rsiValues, ShortRsiMaPeriod);
var longRsi = AverageLast(_rsiValues, LongRsiMaPeriod);
var shortPrice = AverageLast(_closes, ShortPriceMaPeriod);
var longPrice = WeightedAverageLast(_closes, LongPriceMaPeriod);
var goLong = shortPrice > longPrice && shortRsi > longRsi;
var goShort = shortPrice < longPrice && shortRsi < longRsi;
var sideways = !goLong && !goShort;
if (sideways && Position != 0)
{
if (Position > 0)
SellMarket(Position);
else
BuyMarket(Math.Abs(Position));
return;
}
if (Position != 0)
return;
if (goLong)
{
if (Reverse)
SellMarket();
else
BuyMarket();
}
else if (goShort)
{
if (Reverse)
BuyMarket();
else
SellMarket();
}
}
private decimal? CalculateRsi()
{
if (_closes.Count <= RsiPeriod)
return null;
decimal gainSum = 0m;
decimal lossSum = 0m;
var start = _closes.Count - RsiPeriod;
for (var i = start; i < _closes.Count; i++)
{
var change = _closes[i] - _closes[i - 1];
if (change > 0m)
gainSum += change;
else
lossSum -= change;
}
var averageGain = gainSum / RsiPeriod;
var averageLoss = lossSum / RsiPeriod;
if (averageLoss == 0m)
return 100m;
var rs = averageGain / averageLoss;
return 100m - 100m / (1m + rs);
}
private static decimal AverageLast(IReadOnlyList<decimal> values, int length)
{
decimal sum = 0m;
var start = values.Count - length;
for (var i = start; i < values.Count; i++)
sum += values[i];
return sum / length;
}
private static decimal WeightedAverageLast(IReadOnlyList<decimal> values, int length)
{
decimal weightedSum = 0m;
decimal weightSum = 0m;
var start = values.Count - length;
var weight = 1m;
for (var i = start; i < values.Count; i++)
{
weightedSum += values[i] * weight;
weightSum += weight;
weight += 1m;
}
return weightSum > 0m ? weightedSum / weightSum : values[^1];
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
class rsi_trader_strategy(Strategy):
def __init__(self):
super(rsi_trader_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 14)
self._short_rsi_ma_period = self.Param("ShortRsiMaPeriod", 12)
self._long_rsi_ma_period = self.Param("LongRsiMaPeriod", 60)
self._short_price_ma_period = self.Param("ShortPriceMaPeriod", 12)
self._long_price_ma_period = self.Param("LongPriceMaPeriod", 60)
self._reverse = self.Param("Reverse", False)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4)))
self._closes = []
self._rsi_values = []
@property
def RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
@property
def ShortRsiMaPeriod(self):
return self._short_rsi_ma_period.Value
@ShortRsiMaPeriod.setter
def ShortRsiMaPeriod(self, value):
self._short_rsi_ma_period.Value = value
@property
def LongRsiMaPeriod(self):
return self._long_rsi_ma_period.Value
@LongRsiMaPeriod.setter
def LongRsiMaPeriod(self, value):
self._long_rsi_ma_period.Value = value
@property
def ShortPriceMaPeriod(self):
return self._short_price_ma_period.Value
@ShortPriceMaPeriod.setter
def ShortPriceMaPeriod(self, value):
self._short_price_ma_period.Value = value
@property
def LongPriceMaPeriod(self):
return self._long_price_ma_period.Value
@LongPriceMaPeriod.setter
def LongPriceMaPeriod(self, value):
self._long_price_ma_period.Value = value
@property
def Reverse(self):
return self._reverse.Value
@Reverse.setter
def Reverse(self, value):
self._reverse.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(rsi_trader_strategy, self).OnStarted2(time)
self._closes = []
self._rsi_values = []
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
self._closes.append(close)
max_cache = max(int(self.LongPriceMaPeriod), max(int(self.LongRsiMaPeriod) + int(self.RsiPeriod), 300))
while len(self._closes) > max_cache:
self._closes.pop(0)
rsi = self._calculate_rsi()
if rsi is None:
return
self._rsi_values.append(rsi)
while len(self._rsi_values) > max_cache:
self._rsi_values.pop(0)
if len(self._rsi_values) < int(self.LongRsiMaPeriod) or len(self._closes) < int(self.LongPriceMaPeriod):
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
short_rsi = self._average_last(self._rsi_values, int(self.ShortRsiMaPeriod))
long_rsi = self._average_last(self._rsi_values, int(self.LongRsiMaPeriod))
short_price = self._average_last(self._closes, int(self.ShortPriceMaPeriod))
long_price = self._weighted_average_last(self._closes, int(self.LongPriceMaPeriod))
go_long = short_price > long_price and short_rsi > long_rsi
go_short = short_price < long_price and short_rsi < long_rsi
sideways = not go_long and not go_short
if sideways and self.Position != 0:
if self.Position > 0:
self.SellMarket()
elif self.Position < 0:
self.BuyMarket()
return
if self.Position != 0:
return
if go_long:
if self.Reverse:
self.SellMarket()
else:
self.BuyMarket()
elif go_short:
if self.Reverse:
self.BuyMarket()
else:
self.SellMarket()
def _calculate_rsi(self):
rsi_period = int(self.RsiPeriod)
if len(self._closes) <= rsi_period:
return None
gain_sum = 0.0
loss_sum = 0.0
start = len(self._closes) - rsi_period
for i in range(start, len(self._closes)):
change = self._closes[i] - self._closes[i - 1]
if change > 0.0:
gain_sum += change
else:
loss_sum -= change
average_gain = gain_sum / rsi_period
average_loss = loss_sum / rsi_period
if average_loss == 0.0:
return 100.0
rs = average_gain / average_loss
return 100.0 - 100.0 / (1.0 + rs)
def _average_last(self, values, length):
total = 0.0
start = len(values) - length
for i in range(start, len(values)):
total += values[i]
return total / length
def _weighted_average_last(self, values, length):
weighted_sum = 0.0
weight_sum = 0.0
start = len(values) - length
weight = 1.0
for i in range(start, len(values)):
weighted_sum += values[i] * weight
weight_sum += weight
weight += 1.0
if weight_sum > 0.0:
return weighted_sum / weight_sum
return values[-1]
def OnReseted(self):
super(rsi_trader_strategy, self).OnReseted()
self._closes = []
self._rsi_values = []
def CreateClone(self):
return rsi_trader_strategy()