Estrategia Stochastic RSI SuperTrend
Este sistema combina las oscilaciones rápidas del Stochastic RSI con un filtro de
tendencia y un modelo SuperTrend simplificado. El oscilador resalta los extremos de
momentum a corto plazo, mientras que la media móvil y las bandas ATR definen la
tendencia dominante. Las operaciones se abren solo cuando la línea %K cruza %D dentro
de la zona relevante y la tendencia más amplia está alineada, reduciendo las señales
falsas en condiciones laterales.
La configuración predeterminada se centra en operaciones largas, pero opcionalmente
puede habilitar entradas cortas. La estrategia está diseñada para marcos temporales
intradía o swing, donde las señales del Stochastic RSI aparecen con frecuencia y las
bandas basadas en ATR proporcionan un sesgo adaptativo a la volatilidad. Las salidas
ocurren en cruces opuestos, permitiendo que el mercado corra hasta que el momentum
se debilite.
Detalles
- Criterios de entrada:
- Largo: cierre por encima de la MA de tendencia, %K < 20, %K cruza por encima de %D, SuperTrend muestra tendencia alcista.
- Corto: cierre por debajo de la MA de tendencia, %K > 80, %K cruza por debajo de %D, SuperTrend muestra tendencia bajista.
- Largo/Corto: Largo por defecto, corto opcional.
- Criterios de salida:
- Largo: %K > 80 y cruza por debajo de %D.
- Corto: %K < 20 y cruza por encima de %D.
- Stops: Ninguno por defecto; se pueden añadir externamente.
- Valores predeterminados:
- Período RSI = 14, longitud Stochastic = 14.
- Tipo MA = EMA, longitud MA = 100.
- Período ATR = 10, factor ATR = 3.0.
- Filtros:
- Categoría: Momentum + Tendencia
- Dirección: Principalmente largo
- Indicadores: RSI, ATR, Media Móvil
- Stops: No
- Complejidad: Moderado
- Marco temporal: Corto/medio
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Stochastic RSI + Supertrend Strategy.
/// Uses RSI levels with SuperTrend direction and EMA trend filter.
/// Buys when RSI is oversold, SuperTrend is bullish, and price above EMA.
/// Sells when RSI is overbought, SuperTrend is bearish, and price below EMA.
/// </summary>
public class StochRsiSupertrendStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _supertrendLength;
private readonly StrategyParam<decimal> _supertrendMultiplier;
private readonly StrategyParam<int> _cooldownBars;
private RelativeStrengthIndex _rsi;
private ExponentialMovingAverage _ema;
private SuperTrend _supertrend;
private decimal _prevRsi;
private int _cooldownRemaining;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public int SupertrendLength
{
get => _supertrendLength.Value;
set => _supertrendLength.Value = value;
}
public decimal SupertrendMultiplier
{
get => _supertrendMultiplier.Value;
set => _supertrendMultiplier.Value = value;
}
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
public StochRsiSupertrendStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "RSI");
_emaLength = Param(nameof(EmaLength), 50)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "Trend EMA period", "Moving Average");
_supertrendLength = Param(nameof(SupertrendLength), 11)
.SetGreaterThanZero()
.SetDisplay("SuperTrend Length", "SuperTrend ATR period", "SuperTrend");
_supertrendMultiplier = Param(nameof(SupertrendMultiplier), 2.0m)
.SetDisplay("SuperTrend Multiplier", "SuperTrend ATR multiplier", "SuperTrend");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_rsi = null;
_ema = null;
_supertrend = null;
_prevRsi = 0;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_rsi = new RelativeStrengthIndex { Length = RsiLength };
_ema = new ExponentialMovingAverage { Length = EmaLength };
_supertrend = new SuperTrend { Length = SupertrendLength, Multiplier = SupertrendMultiplier };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_rsi, _ema, _supertrend, OnProcess)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _ema);
DrawIndicator(area, _supertrend);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle, IIndicatorValue rsiValue, IIndicatorValue emaValue, IIndicatorValue stValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_rsi.IsFormed || !_ema.IsFormed || !_supertrend.IsFormed)
return;
if (rsiValue.IsEmpty || emaValue.IsEmpty || stValue.IsEmpty)
return;
var rsiVal = rsiValue.ToDecimal();
var emaVal = emaValue.ToDecimal();
var stTyped = (SuperTrendIndicatorValue)stValue;
var isUpTrend = stTyped.IsUpTrend;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevRsi = rsiVal;
return;
}
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevRsi = rsiVal;
return;
}
if (_prevRsi == 0)
{
_prevRsi = rsiVal;
return;
}
// RSI crossovers
var rsiCrossUpOversold = rsiVal > 40 && _prevRsi <= 40;
var rsiCrossDownOverbought = rsiVal < 60 && _prevRsi >= 60;
// Buy: RSI crosses above oversold + SuperTrend bullish + price above EMA
if (rsiCrossUpOversold && isUpTrend && candle.ClosePrice > emaVal && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Sell: RSI crosses below overbought + SuperTrend bearish + price below EMA
else if (rsiCrossDownOverbought && !isUpTrend && candle.ClosePrice < emaVal && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Exit long: SuperTrend turns bearish
else if (Position > 0 && !isUpTrend)
{
SellMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
// Exit short: SuperTrend turns bullish
else if (Position < 0 && isUpTrend)
{
BuyMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
_prevRsi = rsiVal;
}
}
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
from StockSharp.Algo.Indicators import RelativeStrengthIndex, ExponentialMovingAverage, SuperTrend, IndicatorHelper
from StockSharp.Algo.Strategies import Strategy
class stoch_rsi_supertrend_strategy(Strategy):
"""Stochastic RSI + Supertrend Strategy."""
def __init__(self):
super(stoch_rsi_supertrend_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period", "RSI")
self._ema_length = self.Param("EmaLength", 50) \
.SetDisplay("EMA Length", "Trend EMA period", "Moving Average")
self._supertrend_length = self.Param("SupertrendLength", 11) \
.SetDisplay("SuperTrend Length", "SuperTrend ATR period", "SuperTrend")
self._supertrend_multiplier = self.Param("SupertrendMultiplier", 2.0) \
.SetDisplay("SuperTrend Multiplier", "SuperTrend ATR multiplier", "SuperTrend")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk")
self._rsi = None
self._ema = None
self._supertrend = None
self._prev_rsi = 0.0
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(stoch_rsi_supertrend_strategy, self).OnReseted()
self._rsi = None
self._ema = None
self._supertrend = None
self._prev_rsi = 0.0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(stoch_rsi_supertrend_strategy, self).OnStarted2(time)
self._rsi = RelativeStrengthIndex()
self._rsi.Length = int(self._rsi_length.Value)
self._ema = ExponentialMovingAverage()
self._ema.Length = int(self._ema_length.Value)
self._supertrend = SuperTrend()
self._supertrend.Length = int(self._supertrend_length.Value)
self._supertrend.Multiplier = float(self._supertrend_multiplier.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(self._rsi, self._ema, self._supertrend, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._ema)
self.DrawIndicator(area, self._supertrend)
self.DrawOwnTrades(area)
def _on_process(self, candle, rsi_value, ema_value, st_value):
if candle.State != CandleStates.Finished:
return
if not self._rsi.IsFormed or not self._ema.IsFormed or not self._supertrend.IsFormed:
return
if rsi_value.IsEmpty or ema_value.IsEmpty or st_value.IsEmpty:
return
rsi_val = float(IndicatorHelper.ToDecimal(rsi_value))
ema_val = float(IndicatorHelper.ToDecimal(ema_value))
is_up_trend = st_value.IsUpTrend
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_rsi = rsi_val
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_rsi = rsi_val
return
if self._prev_rsi == 0.0:
self._prev_rsi = rsi_val
return
close = float(candle.ClosePrice)
cooldown = int(self._cooldown_bars.Value)
rsi_cross_up_oversold = rsi_val > 40 and self._prev_rsi <= 40
rsi_cross_down_overbought = rsi_val < 60 and self._prev_rsi >= 60
if rsi_cross_up_oversold and is_up_trend and close > ema_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._cooldown_remaining = cooldown
elif rsi_cross_down_overbought and not is_up_trend and close < ema_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._cooldown_remaining = cooldown
elif self.Position > 0 and not is_up_trend:
self.SellMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
elif self.Position < 0 and is_up_trend:
self.BuyMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
self._prev_rsi = rsi_val
def CreateClone(self):
return stoch_rsi_supertrend_strategy()