Estrategia Parabolic RSI
Estrategia que aplica Parabolic SAR al RSI para detectar cambios de tendencia. La estrategia entra cuando el SAR gira respecto a la línea del RSI y puede filtrar operaciones mediante umbrales de RSI.
Detalles
- Criterios de entrada:
- Largo:
SARgira por debajo del RSI y (opcional)RSI ≥ LongRsiMin - Corto:
SARgira por encima del RSI y (opcional)RSI ≤ ShortRsiMax
- Largo:
- Largo/Corto: Configurable
- Criterios de salida: Giro contrario del SAR
- Stops: Ninguno
- Valores predeterminados:
RsiLength= 14SarStart= 0.02SarIncrement= 0.02SarMax= 0.2LongRsiMin= 50ShortRsiMax= 50CandleType= TimeSpan.FromMinutes(5).TimeFrame()
- Filtros:
- Categoría: Seguimiento de tendencia
- Dirección: Configurable
- Indicadores: Parabolic SAR, RSI
- Stops: No
- Complejidad: Intermedio
- Marco temporal: Medio plazo
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
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()