Skip to content

Connors RSI

The literature which Google offers as a reference for this indicator:

Both sources agree on the formulat, although not on the terminology (see below). The Connors RSI shall be calculated as follows:

    CRSI(3, 2, 100) = [RSI(3) + RSI(Streak, 2) + PercentRank(100)] / 3

Note

TradingView says that the ROC ("Rate of Change") has to be used instead of the PctRank ("Percentage Rank"), which is clearly wrong when looking at the definition which is provided by TradingView itself.

Streak is something which is non-standard and needs a definition, let's reference it here from the sources (called "UpDown" in the TradingView jargon)

  • Number of consecutive days the price has closed higher/lower than the previous day
  • If a days closes at the same price as the day before, the streak is reset to 0
  • Upwards streaks yield positive values and downwards streaks yield negative values

With the formula in the hand, understanding we need to use PercentRank and with a clear definition for a Streak (or UpDown), creating the ConnorsRSI indicator should be a child's play.

class Streak(bt.ind.PeriodN):
    '''
    Keeps a counter of the current upwards/downwards/neutral streak
    '''
    lines = ('streak',)
    params = dict(period=2)  # need prev/cur days (2) for comparisons

    curstreak = 0

    def next(self):
        d0, d1 = self.data[0], self.data[-1]

        if d0 > d1:
            self.l.streak[0] = self.curstreak = max(1, self.curstreak + 1)
        elif d0 < d1:
            self.l.streak[0] = self.curstreak = min(-1, self.curstreak - 1)
        else:
            self.l.streak[0] = self.curstreak = 0


class ConnorsRSI(bt.Indicator):
    '''
    Calculates the ConnorsRSI as:
        - (RSI(per_rsi) + RSI(Streak, per_streak) + PctRank(per_rank)) / 3
    '''
    lines = ('crsi',)
    params = dict(prsi=3, pstreak=2, prank=100)

    def __init__(self):
        # Calculate the components
        rsi = bt.ind.RSI(self.data, period=self.p.prsi)

        streak = Streak(self.data)
        rsi_streak = bt.ind.RSI(streak, period=self.p.pstreak)

        prank = bt.ind.PercentRank(self.data, period=self.p.prank)

        # Apply the formula
        self.l.crsi = (rsi + rsi_streak + prank) / 3.0

And here a view of how the indicator works, including also the Streak helper indicator, to have a visual verification that the actual streak is being delivered.

CRSI View