Parabolic Sar Rsi Strategy
Strategy that combines Parabolic SAR for trend direction and RSI for entry confirmation with oversold/overbought conditions.
Testing indicates an average annual return of about 166%. It performs best in the stocks market.
Here the Parabolic SAR outlines the prevailing trend and RSI measures exhaustion. Trades are opened once both indicators signal the same direction.
The combination is appealing to those who like trailing stops, since SAR also provides a dynamic exit. The stop placement follows the SAR curve.
Details
- Entry Criteria:
- Long:
Close > SAR && RSI < RsiOversold - Short:
Close < SAR && RSI > RsiOverbought
- Long:
- Long/Short: Both
- Exit Criteria:
- Long:
Close < SAR - Short:
Close > SAR
- Long:
- Stops: Uses Parabolic SAR as a trailing stop
- Default Values:
SarAf= 0.02mSarMaxAf= 0.2mRsiPeriod= 14RsiOversold= 30mRsiOverbought= 70mCandleType= TimeSpan.FromMinutes(5).TimeFrame()
- Filters:
- Category: Mean reversion
- Direction: Both
- Indicators: Parabolic SAR, Parabolic SAR, RSI
- Stops: Yes
- Complexity: Intermediate
- Timeframe: Mid-term
- Seasonality: No
- Neural Networks: No
- Divergence: No
- Risk Level: Medium
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>
/// Strategy combining Parabolic SAR for trend direction
/// and RSI for entry confirmation.
/// </summary>
public class ParabolicSarRsiStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _rsiOversold;
private readonly StrategyParam<decimal> _rsiOverbought;
private readonly StrategyParam<int> _cooldownBars;
private decimal _sarValue;
private int _cooldown;
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// RSI period.
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// RSI oversold level.
/// </summary>
public decimal RsiOversold
{
get => _rsiOversold.Value;
set => _rsiOversold.Value = value;
}
/// <summary>
/// RSI overbought level.
/// </summary>
public decimal RsiOverbought
{
get => _rsiOverbought.Value;
set => _rsiOverbought.Value = value;
}
/// <summary>
/// Cooldown bars between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Strategy constructor.
/// </summary>
public ParabolicSarRsiStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetRange(7, 21)
.SetDisplay("RSI Period", "Period of the RSI indicator", "Indicators");
_rsiOversold = Param(nameof(RsiOversold), 30m)
.SetDisplay("RSI Oversold", "RSI oversold level", "Indicators");
_rsiOverbought = Param(nameof(RsiOverbought), 70m)
.SetDisplay("RSI Overbought", "RSI overbought level", "Indicators");
_cooldownBars = Param(nameof(CooldownBars), 130)
.SetDisplay("Cooldown Bars", "Bars between trades", "General")
.SetRange(5, 500);
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_sarValue = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var parabolicSar = new ParabolicSar();
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
// ParabolicSar takes candle input - use BindEx
subscription.BindEx(parabolicSar, OnSar);
// RSI for main logic
subscription
.Bind(rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, parabolicSar);
DrawOwnTrades(area);
var rsiArea = CreateChartArea();
if (rsiArea != null)
DrawIndicator(rsiArea, rsi);
}
}
private void OnSar(ICandleMessage candle, IIndicatorValue sarValue)
{
if (sarValue.IsFormed)
_sarValue = sarValue.ToDecimal();
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_sarValue == 0)
return;
var close = candle.ClosePrice;
if (_cooldown > 0)
{
_cooldown--;
return;
}
// Long: price above SAR + RSI not overbought
if (close > _sarValue && rsiValue < RsiOverbought && Position == 0)
{
BuyMarket();
_cooldown = CooldownBars;
}
// Short: price below SAR + RSI not oversold
else if (close < _sarValue && rsiValue > RsiOversold && Position == 0)
{
SellMarket();
_cooldown = CooldownBars;
}
// Exit long: SAR flips above price
if (Position > 0 && close < _sarValue)
{
SellMarket();
_cooldown = CooldownBars;
}
// Exit short: SAR flips below price
else if (Position < 0 && close > _sarValue)
{
BuyMarket();
_cooldown = CooldownBars;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ParabolicSar, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
class parabolic_sar_rsi_strategy(Strategy):
"""
Strategy combining Parabolic SAR for trend direction
and RSI for entry confirmation.
"""
def __init__(self):
super(parabolic_sar_rsi_strategy, self).__init__()
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetRange(7, 21) \
.SetDisplay("RSI Period", "Period of the RSI indicator", "Indicators")
self._rsi_oversold = self.Param("RsiOversold", 30.0) \
.SetDisplay("RSI Oversold", "RSI oversold level", "Indicators")
self._rsi_overbought = self.Param("RsiOverbought", 70.0) \
.SetDisplay("RSI Overbought", "RSI overbought level", "Indicators")
self._cooldown_bars = self.Param("CooldownBars", 130) \
.SetDisplay("Cooldown Bars", "Bars between trades", "General") \
.SetRange(5, 500)
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
@property
def rsi_period(self):
return self._rsi_period.Value
@rsi_period.setter
def rsi_period(self, value):
self._rsi_period.Value = value
@property
def rsi_oversold(self):
return self._rsi_oversold.Value
@rsi_oversold.setter
def rsi_oversold(self, value):
self._rsi_oversold.Value = value
@property
def rsi_overbought(self):
return self._rsi_overbought.Value
@rsi_overbought.setter
def rsi_overbought(self, value):
self._rsi_overbought.Value = value
@property
def cooldown_bars(self):
return self._cooldown_bars.Value
@cooldown_bars.setter
def cooldown_bars(self, value):
self._cooldown_bars.Value = value
def OnStarted2(self, time):
super(parabolic_sar_rsi_strategy, self).OnStarted2(time)
self._cooldown = 0
self._sar_value = 0
parabolic_sar = ParabolicSar()
rsi = RelativeStrengthIndex()
rsi.Length = self.rsi_period
subscription = self.SubscribeCandles(self.candle_type)
# ParabolicSar takes candle input - use BindEx
subscription.BindEx(parabolic_sar, self.OnSar)
# RSI for main logic
subscription.Bind(rsi, self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, parabolic_sar)
self.DrawOwnTrades(area)
rsi_area = self.CreateChartArea()
if rsi_area is not None:
self.DrawIndicator(rsi_area, rsi)
def OnSar(self, candle, sar_value):
if sar_value.IsFormed:
self._sar_value = float(sar_value)
def ProcessCandle(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
sv = self._sar_value
if sv == 0:
return
if self._cooldown > 0:
self._cooldown -= 1
return
# Long: price above SAR + RSI not overbought
if close > sv and rsi_value < self.rsi_overbought and self.Position == 0:
self.BuyMarket()
self._cooldown = self.cooldown_bars
# Short: price below SAR + RSI not oversold
elif close < sv and rsi_value > self.rsi_oversold and self.Position == 0:
self.SellMarket()
self._cooldown = self.cooldown_bars
# Exit long: SAR flips above price
if self.Position > 0 and close < sv:
self.SellMarket()
self._cooldown = self.cooldown_bars
# Exit short: SAR flips below price
elif self.Position < 0 and close > sv:
self.BuyMarket()
self._cooldown = self.cooldown_bars
def OnReseted(self):
super(parabolic_sar_rsi_strategy, self).OnReseted()
self._cooldown = 0
self._sar_value = 0
def CreateClone(self):
return parabolic_sar_rsi_strategy()