RSRS (Resistance Support Relative Strength)
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © algotraderdev

// @version=5
indicator('RSRS', max_bars_back = 600, explicit_plot_zorder = true)

import voided/regress/1 as regress

// Inputs and Constants.
int OLS_WINDOW_SIZE = input.int(
  18,
  'Linear Regression Window Size',
  minval = 1,
  tooltip = 'The size of the rolling window to calculate the RSRS value via linear regression.')
int Z_SCORE_WINDOW_SIZE = math.min(input.int(
  600,
  'Z-Score Window Size',
  minval = 50,
  step = 50,
  tooltip = 'The size of the rolling window to calculate the standard score of RSRS.'), bar_index + 1)
float BUY_SELL_THRESHOLD = input.float(
  0.7,
  'Buy / Sell Threshold',
  minval = 0,
  step = 0.1,
  tooltip = 'If the RSRS Z-Score is above the threshold, trigger a buy signal. ' +
      'If the RSRS Z-Score is below the negative threshold, trigger a sell signal.')
color BUY_COLOR = input.color(color.green, 'Buy Color')
color SELL_COLOR = input.color(color.red, 'Sell Color')
color NEUTRAL_COLOR = #777777

// Use OLS (Ordinary Least Squares) to calculate the slope (beta) such that `high = alpha + beta * low`.
[alpha, beta, r2] = regress.regress(low, high, OLS_WINDOW_SIZE)
// The RSRS value is essentially the slope (beta).
rsrs = nz(beta)
// Calculate the standard score for RSRS.
float mean = ta.sma(rsrs, Z_SCORE_WINDOW_SIZE)
float sigma = ta.stdev(rsrs, Z_SCORE_WINDOW_SIZE)
float zscore = (rsrs - mean) / sigma

// Plot the graphs.
plot(0, color = NEUTRAL_COLOR, style = plot.style_circles)
plot(BUY_SELL_THRESHOLD, color = NEUTRAL_COLOR, style = plot.style_circles)
plot(-BUY_SELL_THRESHOLD, color = NEUTRAL_COLOR, style = plot.style_circles)
color c = switch
    zscore[1] >= BUY_SELL_THRESHOLD and zscore >= BUY_SELL_THRESHOLD => BUY_COLOR
    zscore[1] <= -BUY_SELL_THRESHOLD and zscore <= -BUY_SELL_THRESHOLD => SELL_COLOR
    => NEUTRAL_COLOR
plot(zscore, color = c)

alertcondition(ta.crossover(zscore, BUY_SELL_THRESHOLD), 'BUY', 'RSRS Z-Score crossed over buy threshold.')
alertcondition(ta.crossunder(zscore, -BUY_SELL_THRESHOLD), 'SELL', 'RSRS Z-Score crossed under sell threshold.')