Estratégia Parabolic RSI
Estratégia que aplica Parabolic SAR ao RSI para detectar mudanças de tendência. A estratégia entra quando o SAR vira em relação à linha do RSI e pode filtrar operações por limiares de RSI.
Detalhes
- Critérios de entrada:
- Comprado:
SARvira abaixo do RSI e (opcional)RSI ≥ LongRsiMin - Vendido:
SARvira acima do RSI e (opcional)RSI ≤ ShortRsiMax
- Comprado:
- Comprado/Vendido: Configurável
- Critérios de saída: Virada oposta do SAR
- Stops: Nenhum
- Valores padrão:
RsiLength= 14SarStart= 0.02SarIncrement= 0.02SarMax= 0.2LongRsiMin= 50ShortRsiMax= 50CandleType= TimeSpan.FromMinutes(5).TimeFrame()
- Filtros:
- Categoria: Seguidor de tendência
- Direção: Configurável
- Indicadores: Parabolic SAR, RSI
- Stops: Não
- Complexidade: Intermediário
- Período: Médio prazo
- Sazonalidade: Não
- Redes neurais: Não
- Divergência: Não
- Nível de risco: Médio
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>
/// Parabolic RSI strategy.
/// A Parabolic SAR (SarStart, SarIncrement, SarMax) is run on the RSI line instead of on price. When the SAR flips below the RSI and RSI
/// is at least LongRsiMin the strategy goes long, when it flips above the RSI and RSI is at most ShortRsiMax it goes short. An opposite
/// flip always closes the current position and reverses it when its own RSI condition holds.
/// </summary>
public class ParabolicRsiStrategy : Strategy
{
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<decimal> _sarStart;
private readonly StrategyParam<decimal> _sarIncrement;
private readonly StrategyParam<decimal> _sarMax;
private readonly StrategyParam<decimal> _longRsiMin;
private readonly StrategyParam<decimal> _shortRsiMax;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevRsi;
private decimal? _prevRsi2;
private bool _sarReady;
private bool _isUpTrend;
private decimal _sar;
private decimal _extreme;
private decimal _af;
/// <summary>
/// RSI period.
/// </summary>
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
/// <summary>
/// Initial SAR acceleration factor.
/// </summary>
public decimal SarStart
{
get => _sarStart.Value;
set => _sarStart.Value = value;
}
/// <summary>
/// SAR acceleration factor increment.
/// </summary>
public decimal SarIncrement
{
get => _sarIncrement.Value;
set => _sarIncrement.Value = value;
}
/// <summary>
/// Maximum SAR acceleration factor.
/// </summary>
public decimal SarMax
{
get => _sarMax.Value;
set => _sarMax.Value = value;
}
/// <summary>
/// Minimum RSI for a long entry.
/// </summary>
public decimal LongRsiMin
{
get => _longRsiMin.Value;
set => _longRsiMin.Value = value;
}
/// <summary>
/// Maximum RSI for a short entry.
/// </summary>
public decimal ShortRsiMax
{
get => _shortRsiMax.Value;
set => _shortRsiMax.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public ParabolicRsiStrategy()
{
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "Indicators");
_sarStart = Param(nameof(SarStart), 0.02m)
.SetGreaterThanZero()
.SetDisplay("SAR Start", "Initial SAR acceleration factor", "Indicators");
_sarIncrement = Param(nameof(SarIncrement), 0.02m)
.SetGreaterThanZero()
.SetDisplay("SAR Increment", "SAR acceleration factor increment", "Indicators");
_sarMax = Param(nameof(SarMax), 0.2m)
.SetGreaterThanZero()
.SetDisplay("SAR Max", "Maximum SAR acceleration factor", "Indicators");
_longRsiMin = Param(nameof(LongRsiMin), 50m)
.SetDisplay("Long RSI Min", "Minimum RSI for a long entry", "Signals");
_shortRsiMax = Param(nameof(ShortRsiMax), 50m)
.SetDisplay("Short RSI Max", "Maximum RSI for a short entry", "Signals");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
private void ResetState()
{
_prevRsi = null;
_prevRsi2 = null;
_sarReady = false;
_isUpTrend = false;
_sar = 0m;
_extreme = 0m;
_af = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
DrawIndicator(oscillators, rsi);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsi)
{
if (candle.State != CandleStates.Finished)
return;
var flip = UpdateSar(rsi);
if (flip == 0 || !IsFormedAndOnlineAndAllowTrading())
return;
if (flip > 0)
{
if (rsi >= LongRsiMin && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (Position < 0)
BuyMarket(-Position);
}
else
{
if (rsi <= ShortRsiMax && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
else if (Position > 0)
SellMarket(Position);
}
}
// Parabolic SAR on a single series; returns 1 on a flip below the series, -1 on a flip above it, 0 otherwise.
private int UpdateSar(decimal value)
{
var prev = _prevRsi;
var prev2 = _prevRsi2;
_prevRsi2 = _prevRsi;
_prevRsi = value;
if (prev is not decimal p)
return 0;
if (!_sarReady)
{
_isUpTrend = value >= p;
_sar = _isUpTrend ? Math.Min(p, value) : Math.Max(p, value);
_extreme = _isUpTrend ? Math.Max(p, value) : Math.Min(p, value);
_af = SarStart;
_sarReady = true;
return 0;
}
var sar = _sar + _af * (_extreme - _sar);
if (_isUpTrend)
{
sar = Math.Min(sar, p);
if (prev2 is decimal p2)
sar = Math.Min(sar, p2);
if (value < sar)
{
_isUpTrend = false;
_sar = _extreme;
_extreme = value;
_af = SarStart;
return -1;
}
if (value > _extreme)
{
_extreme = value;
_af = Math.Min(_af + SarIncrement, SarMax);
}
}
else
{
sar = Math.Max(sar, p);
if (prev2 is decimal p2)
sar = Math.Max(sar, p2);
if (value > sar)
{
_isUpTrend = true;
_sar = _extreme;
_extreme = value;
_af = SarStart;
return 1;
}
if (value < _extreme)
{
_extreme = value;
_af = Math.Min(_af + SarIncrement, SarMax);
}
}
_sar = sar;
return 0;
}
}
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
from StockSharp.Algo.Strategies import Strategy
class parabolic_rsi_strategy(Strategy):
"""
Parabolic RSI strategy.
A Parabolic SAR (SarStart, SarIncrement, SarMax) is run on the RSI line instead of on price. When the SAR flips below the RSI and RSI
is at least LongRsiMin the strategy goes long, when it flips above the RSI and RSI is at most ShortRsiMax it goes short. An opposite
flip always closes the current position and reverses it when its own RSI condition holds.
"""
def __init__(self):
super(parabolic_rsi_strategy, self).__init__()
self._rsi_length = self.Param("RsiLength", 14).SetGreaterThanZero().SetDisplay("RSI Length", "RSI period", "Indicators")
self._sar_start = self.Param("SarStart", 0.02).SetGreaterThanZero().SetDisplay("SAR Start", "Initial SAR acceleration factor", "Indicators")
self._sar_increment = self.Param("SarIncrement", 0.02).SetGreaterThanZero().SetDisplay("SAR Increment", "SAR acceleration factor increment", "Indicators")
self._sar_max = self.Param("SarMax", 0.2).SetGreaterThanZero().SetDisplay("SAR Max", "Maximum SAR acceleration factor", "Indicators")
self._long_rsi_min = self.Param("LongRsiMin", 50.0).SetDisplay("Long RSI Min", "Minimum RSI for a long entry", "Signals")
self._short_rsi_max = self.Param("ShortRsiMax", 50.0).SetDisplay("Short RSI Max", "Maximum RSI for a short entry", "Signals")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._prev_rsi = None
self._prev_rsi2 = None
self._sar_ready = False
self._is_up_trend = False
self._sar = Decimal(0)
self._extreme = Decimal(0)
self._af = Decimal(0)
def OnReseted(self):
super(parabolic_rsi_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(parabolic_rsi_strategy, self).OnStarted2(time)
self._reset_state()
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(rsi, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, rsi)
def _process_candle(self, candle, rsi):
if candle.State != CandleStates.Finished:
return
flip = self._update_sar(rsi)
if flip == 0 or not self.IsFormedAndOnlineAndAllowTrading():
return
if flip > 0:
if rsi >= Decimal(self._long_rsi_min.Value) and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif self.Position < 0:
self.BuyMarket(-self.Position)
else:
if rsi <= Decimal(self._short_rsi_max.Value) and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self.Position > 0:
self.SellMarket(self.Position)
# Parabolic SAR on a single series; returns 1 on a flip below the series, -1 on a flip above it, 0 otherwise.
def _update_sar(self, value):
prev = self._prev_rsi
prev2 = self._prev_rsi2
self._prev_rsi2 = self._prev_rsi
self._prev_rsi = value
if prev is None:
return 0
start = Decimal(self._sar_start.Value)
increment = Decimal(self._sar_increment.Value)
maximum = Decimal(self._sar_max.Value)
if not self._sar_ready:
self._is_up_trend = value >= prev
self._sar = min(prev, value) if self._is_up_trend else max(prev, value)
self._extreme = max(prev, value) if self._is_up_trend else min(prev, value)
self._af = start
self._sar_ready = True
return 0
sar = self._sar + self._af * (self._extreme - self._sar)
if self._is_up_trend:
sar = min(sar, prev)
if prev2 is not None:
sar = min(sar, prev2)
if value < sar:
self._is_up_trend = False
self._sar = self._extreme
self._extreme = value
self._af = start
return -1
if value > self._extreme:
self._extreme = value
self._af = min(self._af + increment, maximum)
else:
sar = max(sar, prev)
if prev2 is not None:
sar = max(sar, prev2)
if value > sar:
self._is_up_trend = True
self._sar = self._extreme
self._extreme = value
self._af = start
return 1
if value < self._extreme:
self._extreme = value
self._af = min(self._af + increment, maximum)
self._sar = sar
return 0
def CreateClone(self):
return parabolic_rsi_strategy()