// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © julzen2

//@version=5
indicator("Past Regression Deviated", overlay=true)

// Input parameters
var int lrLength = input.int(500, "LR Length (bars back)", minval=1)
var float stdChannel1 = input.float(1.0, "Std Channel 1 Multiplier", minval=0.0)
var float stdChannel2 = input.float(2.0, "Std Channel 2 Multiplier", minval=0.0)
var float stdChannel3 = input.float(3.0, "Std Channel 3 Multiplier", minval=0.0)

// Function to calculate Linear Regression coefficients (a and b)
// Returns [a, b] for y = a + b*x, where x=0 is the *oldest* bar in the window
// and x=length-1 is the *current* bar in the window.
f_linreg_coeffs(source, length) =>
    if length <= 1
        [float(na), float(na), float(na)] // Return a, b, and a flag for denominator == 0
    else
        sumY = 0.0
        sumX = 0.0
        sumXY = 0.0
        sumX2 = 0.0
        for i = 0 to length - 1
            val = source[i]
            // 'x' here represents the position within the current `length` window,
            // with 0 being the oldest bar in the window, and `length - 1` being the newest (current bar).
            x = float(length - 1 - i)
            sumY += val
            sumXY += val * x
            sumX += x
            sumX2 += x * x
       
        denominator = length * sumX2 - sumX * sumX
       
        if denominator == 0
            [float(na), float(na), float(na)]
        else
            b = (length * sumXY - sumX * sumY) / denominator
            a = (sumY - b * sumX) / length
            [a, b, 1.0] // Return coefficients and success flag

// Function to calculate Standard Deviation
// Takes source series, regression coefficients (a, b), and length
calc_stddev(source, a_coeff, b_coeff, length) =>
    sumSquaredDev = 0.0
    count = 0
   
    if na(a_coeff) or na(b_coeff)
        0.0 // Cannot calculate if coefficients are NA

    for i = 0 to length - 1
        // 'x_val' is the position within the current `length` window for this bar `i`
        // where 0 is the oldest bar and `length - 1` is the current bar.
        x_val = float(length - 1 - i)
       
        // Calculate the LR value at this specific bar using the computed 'a' and 'b'
        lr_val_at_i = a_coeff + b_coeff * x_val
       
        dev = math.abs(source[i] - lr_val_at_i)
        sumSquaredDev += dev * dev
        count += 1
   
    if count <= 1
        0.0
    else
        math.sqrt(sumSquaredDev / (count - 1))

// Declare variables to store the calculated values
var float meanBuffer = na
var float lrPrice1 = na // This corresponds to the start of the LR line (oldest point in range)
var float currentStdDev = na

// Calculate on each historical bar and the last bar
if barstate.islast or barstate.isconfirmed
    // Get the regression coefficients for the current window
    [a_lr, b_lr, success_flag] = f_linreg_coeffs(close, lrLength)

    if not na(success_flag) // Only proceed if coefficients were calculated successfully
        // meanBuffer corresponds to the LR value at the current bar (newest point in range)
        meanBuffer := a_lr + b_lr * (lrLength - 1)
       
        // lrPrice1 corresponds to the LR value at the oldest bar in the range
        lrPrice1 := a_lr
       
        // Calculate standard deviation using the obtained coefficients
        currentStdDev := calc_stddev(close, a_lr, b_lr, lrLength)
    else
        meanBuffer := na
        lrPrice1 := na
        currentStdDev := na


// Plotting
plot(meanBuffer, "Mean", color.white)
//plot(meanBuffer + (stdChannel1 * currentStdDev), "1st Std up", color.white)
//plot(meanBuffer - (stdChannel1 * currentStdDev), "1st Std down", color.white)
//plot(meanBuffer + (stdChannel2 * currentStdDev), "2nd Std up", color.white)
//plot(meanBuffer - (stdChannel2 * currentStdDev), "2nd Std down", color.white)
plot(meanBuffer + (stdChannel3 * currentStdDev), "3rd Std up", color.white)
plot(meanBuffer - (stdChannel3 * currentStdDev), "3rd Std down", color.white)
