Estratégia de Cruzamento de Stochastic RSI
Este método converte o clássico Relative Strength Index em um Stochastic RSI, e então suaviza o resultado nas linhas %K e %D. Quando %K cruza %D dentro de zonas cuidadosamente escolhidas, o movimento implica uma mudança de curto prazo no momentum. O algoritmo só opera quando uma estrutura EMA de três camadas confirma a direção da tendência mais ampla, ajudando a filtrar falsos sinais.
Uma vez que um cruzamento aparece, o preço de fechamento também deve ficar acima ou abaixo da EMA rápida dependendo do sinal. Isso protege contra agir em oscilações que ocorrem contra a tendência prevalecente e mantém a atenção nos momentos quando o momentum se alinha com a direção. Os traders podem ajustar os períodos de suavização e o comprimento do RSI para ajustar como o sistema reage a picos de volatilidade.
O risco é referenciado por meio de uma leitura de Average True Range. Multiplicadores do ATR atual propõem stops de perda e alvos de lucro, fornecendo um nível dinâmico que se expande em mercados voláteis e se contrai quando a atividade se acalma. Embora o script não envie automaticamente ordens protetoras, esses níveis calculados auxiliam no gerenciamento manual ou podem ser vinculados a módulos de risco adicionais.
Detalhes
- Critérios de entrada:
- Comprado:
%Kcruza acima de%D,%Kem[10,60], EMAs alinhadas altistamente, preço acima de EMA1. - Vendido:
%Kcruza abaixo de%D,%Kem[40,95], EMAs alinhadas baixistamente, preço abaixo de EMA1.
- Comprado:
- Comprado/Vendido: Ambos os lados.
- Critérios de saída: Nenhum integrado.
- Stops: Múltiplos de ATR sugeridos mas não colocados automaticamente.
- Valores padrão:
SmoothK= 3,SmoothD= 3.RsiLength= 14,StochLength= 14.Ema1Length= 20,Ema2Length= 50,Ema3Length= 100.AtrLength= 14,AtrLossMultiplier= 1.5,AtrProfitMultiplier= 2.0.
- Filtros:
- Categoria: Momentum
- Direção: Ambos
- Indicadores: Múltiplos
- Stops: Opcional
- Complexidade: Moderado
- Período: Intradiário
- Sazonalidade: Não
- Redes neurais: Não
- Divergência: Sim
- Nível de risco: Médio
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>
/// Stoch RSI Crossover Strategy.
/// RSI is turned into a Stochastic RSI over StochLength bars and smoothed into %K (SmoothK) and %D (SmoothD).
/// A long opens when %K crosses above %D with %K in [10, 60], EMA1 > EMA2 > EMA3 and the close above EMA1.
/// A short opens when %K crosses below %D with %K in [40, 95], EMA1 < EMA2 < EMA3 and the close below EMA1.
/// There is no built-in exit: an opposite signal reverses the position. The ATR multipliers only describe suggested levels.
/// </summary>
public class StochRsiCrossoverStrategy : Strategy
{
private readonly StrategyParam<int> _smoothK;
private readonly StrategyParam<int> _smoothD;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _stochLength;
private readonly StrategyParam<int> _ema1Length;
private readonly StrategyParam<int> _ema2Length;
private readonly StrategyParam<int> _ema3Length;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<decimal> _atrLossMultiplier;
private readonly StrategyParam<decimal> _atrProfitMultiplier;
private readonly StrategyParam<DataType> _candleType;
private Highest _rsiHigh;
private Lowest _rsiLow;
private SimpleMovingAverage _kMa;
private SimpleMovingAverage _dMa;
private decimal? _prevK;
private decimal? _prevD;
/// <summary>
/// %K smoothing.
/// </summary>
public int SmoothK
{
get => _smoothK.Value;
set => _smoothK.Value = value;
}
/// <summary>
/// %D smoothing.
/// </summary>
public int SmoothD
{
get => _smoothD.Value;
set => _smoothD.Value = value;
}
/// <summary>
/// RSI period.
/// </summary>
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
/// <summary>
/// Stochastic period applied to RSI.
/// </summary>
public int StochLength
{
get => _stochLength.Value;
set => _stochLength.Value = value;
}
/// <summary>
/// Fast EMA period.
/// </summary>
public int Ema1Length
{
get => _ema1Length.Value;
set => _ema1Length.Value = value;
}
/// <summary>
/// Medium EMA period.
/// </summary>
public int Ema2Length
{
get => _ema2Length.Value;
set => _ema2Length.Value = value;
}
/// <summary>
/// Slow EMA period.
/// </summary>
public int Ema3Length
{
get => _ema3Length.Value;
set => _ema3Length.Value = value;
}
/// <summary>
/// ATR period of the suggested levels.
/// </summary>
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <summary>
/// ATR multiplier of the suggested stop loss.
/// </summary>
public decimal AtrLossMultiplier
{
get => _atrLossMultiplier.Value;
set => _atrLossMultiplier.Value = value;
}
/// <summary>
/// ATR multiplier of the suggested profit target.
/// </summary>
public decimal AtrProfitMultiplier
{
get => _atrProfitMultiplier.Value;
set => _atrProfitMultiplier.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public StochRsiCrossoverStrategy()
{
_smoothK = Param(nameof(SmoothK), 3)
.SetGreaterThanZero()
.SetDisplay("Smooth K", "%K smoothing", "Stoch RSI");
_smoothD = Param(nameof(SmoothD), 3)
.SetGreaterThanZero()
.SetDisplay("Smooth D", "%D smoothing", "Stoch RSI");
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "Stoch RSI");
_stochLength = Param(nameof(StochLength), 14)
.SetGreaterThanZero()
.SetDisplay("Stoch Length", "Stochastic period applied to RSI", "Stoch RSI");
_ema1Length = Param(nameof(Ema1Length), 20)
.SetGreaterThanZero()
.SetDisplay("EMA1 Length", "Fast EMA period", "Trend");
_ema2Length = Param(nameof(Ema2Length), 50)
.SetGreaterThanZero()
.SetDisplay("EMA2 Length", "Medium EMA period", "Trend");
_ema3Length = Param(nameof(Ema3Length), 100)
.SetGreaterThanZero()
.SetDisplay("EMA3 Length", "Slow EMA period", "Trend");
_atrLength = Param(nameof(AtrLength), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Length", "ATR period of the suggested levels", "Risk");
_atrLossMultiplier = Param(nameof(AtrLossMultiplier), 1.5m)
.SetGreaterThanZero()
.SetDisplay("ATR Loss Multiplier", "ATR multiplier of the suggested stop loss", "Risk");
_atrProfitMultiplier = Param(nameof(AtrProfitMultiplier), 2.0m)
.SetGreaterThanZero()
.SetDisplay("ATR Profit Multiplier", "ATR multiplier of the suggested profit target", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevK = null;
_prevD = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevK = null;
_prevD = null;
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var ema1 = new ExponentialMovingAverage { Length = Ema1Length };
var ema2 = new ExponentialMovingAverage { Length = Ema2Length };
var ema3 = new ExponentialMovingAverage { Length = Ema3Length };
_rsiHigh = new Highest { Length = StochLength };
_rsiLow = new Lowest { Length = StochLength };
_kMa = new SimpleMovingAverage { Length = SmoothK };
_dMa = new SimpleMovingAverage { Length = SmoothD };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(rsi, ema1, ema2, ema3, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema1);
DrawIndicator(area, ema2);
DrawIndicator(area, ema3);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue rsiValue, IIndicatorValue ema1Value, IIndicatorValue ema2Value, IIndicatorValue ema3Value)
{
if (candle.State != CandleStates.Finished)
return;
if (!rsiValue.IsFormed)
return;
var rsi = rsiValue.GetValue<decimal>();
var high = _rsiHigh.Process(rsi, candle.OpenTime, true).ToDecimal();
var low = _rsiLow.Process(rsi, candle.OpenTime, true).ToDecimal();
if (!_rsiHigh.IsFormed || !_rsiLow.IsFormed)
return;
var stoch = high - low > 0m ? 100m * (rsi - low) / (high - low) : 0m;
var kValue = _kMa.Process(stoch, candle.OpenTime, true);
if (!_kMa.IsFormed)
return;
var k = kValue.ToDecimal();
var dValue = _dMa.Process(k, candle.OpenTime, true);
if (!_dMa.IsFormed)
return;
var d = dValue.ToDecimal();
var prevK = _prevK;
var prevD = _prevD;
_prevK = k;
_prevD = d;
if (prevK is not decimal pk || prevD is not decimal pd)
return;
if (!ema1Value.IsFormed || !ema2Value.IsFormed || !ema3Value.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var ema1 = ema1Value.GetValue<decimal>();
var ema2 = ema2Value.GetValue<decimal>();
var ema3 = ema3Value.GetValue<decimal>();
var close = candle.ClosePrice;
var crossUp = pk <= pd && k > d;
var crossDown = pk >= pd && k < d;
var longSignal = crossUp && k >= 10m && k <= 60m && ema1 > ema2 && ema2 > ema3 && close > ema1;
var shortSignal = crossDown && k >= 40m && k <= 95m && ema1 < ema2 && ema2 < ema3 && close < ema1;
if (longSignal && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (shortSignal && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import RelativeStrengthIndex, ExponentialMovingAverage, SimpleMovingAverage, Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class stoch_rsi_crossover_strategy(Strategy):
"""
Stoch RSI Crossover Strategy.
RSI is turned into a Stochastic RSI over StochLength bars and smoothed into %K (SmoothK) and %D (SmoothD).
A long opens when %K crosses above %D with %K in [10, 60], EMA1 > EMA2 > EMA3 and the close above EMA1.
A short opens when %K crosses below %D with %K in [40, 95], EMA1 < EMA2 < EMA3 and the close below EMA1.
There is no built-in exit: an opposite signal reverses the position. The ATR multipliers only describe suggested levels.
"""
def __init__(self):
super(stoch_rsi_crossover_strategy, self).__init__()
self._smooth_k = self.Param("SmoothK", 3).SetGreaterThanZero().SetDisplay("Smooth K", "%K smoothing", "Stoch RSI")
self._smooth_d = self.Param("SmoothD", 3).SetGreaterThanZero().SetDisplay("Smooth D", "%D smoothing", "Stoch RSI")
self._rsi_length = self.Param("RsiLength", 14).SetGreaterThanZero().SetDisplay("RSI Length", "RSI period", "Stoch RSI")
self._stoch_length = self.Param("StochLength", 14).SetGreaterThanZero().SetDisplay("Stoch Length", "Stochastic period applied to RSI", "Stoch RSI")
self._ema1_length = self.Param("Ema1Length", 20).SetGreaterThanZero().SetDisplay("EMA1 Length", "Fast EMA period", "Trend")
self._ema2_length = self.Param("Ema2Length", 50).SetGreaterThanZero().SetDisplay("EMA2 Length", "Medium EMA period", "Trend")
self._ema3_length = self.Param("Ema3Length", 100).SetGreaterThanZero().SetDisplay("EMA3 Length", "Slow EMA period", "Trend")
self._atr_length = self.Param("AtrLength", 14).SetGreaterThanZero().SetDisplay("ATR Length", "ATR period of the suggested levels", "Risk")
self._atr_loss_multiplier = self.Param("AtrLossMultiplier", 1.5).SetGreaterThanZero().SetDisplay("ATR Loss Multiplier", "ATR multiplier of the suggested stop loss", "Risk")
self._atr_profit_multiplier = self.Param("AtrProfitMultiplier", 2.0).SetGreaterThanZero().SetDisplay("ATR Profit Multiplier", "ATR multiplier of the suggested profit target", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._rsi_high = None
self._rsi_low = None
self._k_ma = None
self._d_ma = None
self._prev_k = None
self._prev_d = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(stoch_rsi_crossover_strategy, self).OnReseted()
self._prev_k = None
self._prev_d = None
def OnStarted2(self, time):
super(stoch_rsi_crossover_strategy, self).OnStarted2(time)
self._prev_k = None
self._prev_d = None
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
ema1 = ExponentialMovingAverage()
ema1.Length = self._ema1_length.Value
ema2 = ExponentialMovingAverage()
ema2.Length = self._ema2_length.Value
ema3 = ExponentialMovingAverage()
ema3.Length = self._ema3_length.Value
self._rsi_high = Highest()
self._rsi_high.Length = self._stoch_length.Value
self._rsi_low = Lowest()
self._rsi_low.Length = self._stoch_length.Value
self._k_ma = SimpleMovingAverage()
self._k_ma.Length = self._smooth_k.Value
self._d_ma = SimpleMovingAverage()
self._d_ma.Length = self._smooth_d.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(rsi, ema1, ema2, ema3, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema1)
self.DrawIndicator(area, ema2)
self.DrawIndicator(area, ema3)
self.DrawOwnTrades(area)
def _process_candle(self, candle, rsi_value, ema1_value, ema2_value, ema3_value):
if candle.State != CandleStates.Finished:
return
if not rsi_value.IsFormed:
return
rsi_dec = rsi_value.GetValue[Decimal](None)
rsi = float(rsi_dec)
high = float(process_float(self._rsi_high, rsi_dec, candle.OpenTime, True).GetValue[Decimal](None))
low = float(process_float(self._rsi_low, rsi_dec, candle.OpenTime, True).GetValue[Decimal](None))
if not self._rsi_high.IsFormed or not self._rsi_low.IsFormed:
return
stoch = 100.0 * (rsi - low) / (high - low) if high - low > 0 else 0.0
k_value = process_float(self._k_ma, stoch, candle.OpenTime, True)
if not self._k_ma.IsFormed:
return
k_dec = k_value.GetValue[Decimal](None)
k = float(k_dec)
d_value = process_float(self._d_ma, k_dec, candle.OpenTime, True)
if not self._d_ma.IsFormed:
return
d = float(d_value.GetValue[Decimal](None))
prev_k = self._prev_k
prev_d = self._prev_d
self._prev_k = k
self._prev_d = d
if prev_k is None or prev_d is None:
return
if not ema1_value.IsFormed or not ema2_value.IsFormed or not ema3_value.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
ema1 = float(ema1_value.GetValue[Decimal](None))
ema2 = float(ema2_value.GetValue[Decimal](None))
ema3 = float(ema3_value.GetValue[Decimal](None))
close = float(candle.ClosePrice)
cross_up = prev_k <= prev_d and k > d
cross_down = prev_k >= prev_d and k < d
long_signal = cross_up and 10.0 <= k <= 60.0 and ema1 > ema2 and ema2 > ema3 and close > ema1
short_signal = cross_down and 40.0 <= k <= 95.0 and ema1 < ema2 and ema2 < ema3 and close < ema1
if long_signal and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif short_signal and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return stoch_rsi_crossover_strategy()