Visible Range Linear Regression Channel [vnhilton]
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © vnhilton
// Inspired by TradingView's Linear Regression Channel & Visible Average Price Code

//@version=5
indicator("Visible Range Linear Regression Channel [vnhilton]", "VRLRC", true)

//Getting access to functions from PineCoders' "VisibleChart" library
import PineCoders/VisibleChart/4 as PCVC

//Parameters
source           = input(close,          "Source")

extendRightInput = input.bool(false,     "Extend Lines Right",                                                                         group="Display Settings")
showBasis        = input.bool(true,      "Show Basis Line",                                                                            group="Display Settings")
showBands1       = input.bool(true,      "",                               inline="Band toggles",                                      group="Display Settings")
showBands2       = input.bool(true,      "",                               inline="Band toggles",                                      group="Display Settings")
showBands3       = input.bool(true,      "Show Bands 1, 2, 3",             inline="Band toggles",                                      group="Display Settings")
showFill1        = input.bool(true,      "",                               inline="Fill toggles",                                      group="Display Settings")
showFill2        = input.bool(true,      "",                               inline="Fill toggles",                                      group="Display Settings")
showFill3        = input.bool(true,      "Show Band Fills 1, 2, 3",        inline="Fill toggles",                                      group="Display Settings")
multi1           = input.float(1,        "Standard Deviations 1, 2, 3", 0, inline="Standard Deviations",                               group="Display Settings")
multi2           = input.float(2,        "",                            0, inline="Standard Deviations",                               group="Display Settings")
multi3           = input.float(3,        "",                            0, inline="Standard Deviations",                               group="Display Settings")
basisLineStyle   = input.string("Solid", "Basis Line style", ["Arrow Right", "Dashed", "Dotted", "Solid"],                             group="Display Settings")       
bandsLineStyle   = input.string("Solid", "Bands Line style", ["Arrow Right", "Dashed", "Dotted", "Solid"],                             group="Display Settings")     

widthBasis       = input.int(1, "Basis Line Width",    1,                          group="Width Settings")
widthBands1      = input.int(1, "Band Lines 1, 2, 3",  1, inline="Width Settings", group="Width Settings")
widthBands2      = input.int(1, "",                    1, inline="Width Settings", group="Width Settings")
widthBands3      = input.int(1, "",                    1, inline="Width Settings", group="Width Settings")

colorBasis       = input.color(color.purple,   "Basis Line",                                         group="Color Settings")
colorUpper1      = input.color(color.yellow,  "Upper Lines 1, 2, 3", inline="Upper Color Settings", group="Color Settings")
colorUpper2      = input.color(color.orange,  "",                    inline="Upper Color Settings", group="Color Settings")
colorUpper3      = input.color(color.red,     "",                    inline="Upper Color Settings", group="Color Settings")
colorLower1      = input.color(color.yellow,  "Lower Lines 1, 2, 3", inline="Lower Color Settings", group="Color Settings")
colorLower2      = input.color(color.orange,  "",                    inline="Lower Color Settings", group="Color Settings")
colorLower3      = input.color(color.red,     "",                    inline="Lower Color Settings", group="Color Settings")

upperFill1       = input.color(color.new(color.yellow, 95), "Upper Band Fills 1, 2, 3",                                                 inline="Upper Fill Settings", group="Fill Settings")
upperFill2       = input.color(color.new(color.orange, 95), "",                                                                         inline="Upper Fill Settings", group="Fill Settings")
upperFill3       = input.color(color.new(color.red, 95),    "", "Ensure successive multiples are increasing for non-overlapping fills", inline="Upper Fill Settings", group="Fill Settings")
lowerFill1       = input.color(color.new(color.yellow, 95), "Lower Band Fills 1, 2, 3",                                                 inline="Lower Fill Settings", group="Fill Settings")
lowerFill2       = input.color(color.new(color.orange, 95), "",                                                                         inline="Lower Fill Settings", group="Fill Settings")
lowerFill3       = input.color(color.new(color.red, 95),    "",                                                                         inline="Lower Fill Settings", group="Fill Settings")

//Extending lines
extendStyle = switch
    extendRightInput => extend.right
    => extend.none

//Line Style Selection
basisStyling(choice) =>
    switch basisLineStyle
        "Arrow Right" => line.style_arrow_right
        "Dashed"      => line.style_dashed
        "Dotted"      => line.style_dotted
        "Solid" => line.style_solid

bandsStyling(choice) =>
    switch bandsLineStyle
        "Arrow Right" => line.style_arrow_right
        "Dashed"      => line.style_dashed
        "Dotted"      => line.style_dotted
        "Solid" => line.style_solid

basisStyle = basisStyling(basisLineStyle)
bandsStyle = bandsStyling(bandsLineStyle)

//Getting indices
leftIndex  = PCVC.leftBarIndex()
rightIndex = PCVC.rightBarIndex()
length     = (rightIndex - leftIndex) + 1

//Getting prices
var float[] prices = array.new<float>()

leftTime  = chart.left_visible_bar_time
rightTime = chart.right_visible_bar_time

if time >= leftTime and time <= rightTime
    array.push(prices, source)

//Standard deviation (Sample)
stddev = array.stdev(prices, false)

//Least Squares Moving Average formulae function
LSMA() =>
    if not barstate.islast or length == 1
        [float(na), float(na)]
    else
        sumX    = 0.0
        sumY    = array.sum(prices)
        sumXSqr = 0.0
        sumXY   = 0.0
    
        for i = 0 to (length - 1)
            x        = i + 1.0
            sumX    += x
            sumXSqr += x * x
            sumXY   += x * (array.get(prices, i))

        gradient  = ((length * sumXY) - (sumX * sumY)) / ((length * sumXSqr) - (sumX * sumX))
        intercept = (array.avg(prices)) - (gradient * (sumX / length))

        [gradient, intercept]

//Getting gradient and intercept
[m, c] = LSMA()

//Y basis values
leftY  = c
rightY = (m * (length - 1)) + c

//Plot lines
line basisLine  = na
line upperLine1 = na
line upperLine2 = na
line upperLine3 = na
line lowerLine1 = na
line lowerLine2 = na
line lowerLine3 = na

if not na(leftY)
    if showBasis
        basisLine  := line.new(leftIndex, leftY,                     rightIndex, rightY,                     extend=extendStyle, color=colorBasis,  style=basisStyle, width=widthBasis)

    if showBands1
        upperLine1 := line.new(leftIndex, leftY + (stddev * multi1), rightIndex, rightY + (stddev * multi1), extend=extendStyle, color=colorUpper1, style=bandsStyle, width=widthBands1)
        lowerLine1 := line.new(leftIndex, leftY - (stddev * multi1), rightIndex, rightY - (stddev * multi1), extend=extendStyle, color=colorLower1, style=bandsStyle, width=widthBands1)

    if showBands2
        upperLine2 := line.new(leftIndex, leftY + (stddev * multi2), rightIndex, rightY + (stddev * multi2), extend=extendStyle, color=colorUpper2, style=bandsStyle, width=widthBands2)
        lowerLine2 := line.new(leftIndex, leftY - (stddev * multi2), rightIndex, rightY - (stddev * multi2), extend=extendStyle, color=colorLower2, style=bandsStyle, width=widthBands2)

    if showBands3
        upperLine3 := line.new(leftIndex, leftY + (stddev * multi3), rightIndex, rightY + (stddev * multi3), extend=extendStyle, color=colorUpper3, style=bandsStyle, width=widthBands3)
        lowerLine3 := line.new(leftIndex, leftY - (stddev * multi3), rightIndex, rightY - (stddev * multi3), extend=extendStyle, color=colorLower3, style=bandsStyle, width=widthBands3)
    
//Plot fills
linefill upperBand1Fill = na
linefill upperBand2Fill = na
linefill upperBand3Fill = na
linefill lowerBand1Fill = na
linefill lowerBand2Fill = na
linefill lowerBand3Fill = na

if showFill1
    upperBand1Fill := linefill.new(basisLine,  upperLine1, upperFill1)
    lowerBand1Fill := linefill.new(basisLine,  lowerLine1, lowerFill1)

if showFill2
    upperBand2Fill := linefill.new(upperLine1, upperLine2, upperFill2)
    lowerBand2Fill := linefill.new(lowerLine1, lowerLine2, lowerFill2)

if showFill3
    upperBand3Fill := linefill.new(upperLine2, upperLine3, upperFill3)
    lowerBand3Fill := linefill.new(lowerLine2, lowerLine3, lowerFill3)