[_ParkF]Linreg & Trendlines
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © ParkF

//@version=5
indicator("[_ParkF]Linreg & Trendlines", overlay=true)

// LINEAR REGRESSION
// input
group1 = "LINEAR REGRESSION SETTING"
lengthInput = input.int(100, title="Length", minval = 1, maxval = 5000, group=group1)
sourceInput = input.source(close, title="Source", group=group1)

group2 = "LINEAR REGRESSION SWITCH"
useDev1Input = input.bool(true, title="Large LinReg On / Off", group=group2)
useDev2Input = input.bool(true, title="Medium LinReg On / Off", group=group2)
useDev3Input = input.bool(true, title="Small LinReg On / Off", group=group2)

useUpperDevInput = true
upperMultInput = 3.0
useLowerDevInput = true
lowerMultInput = 3.0
useUpperDevInput2 = true
upperMultInput2 = 2.0
useLowerDevInput2 = true
lowerMultInput2 = 2.0
useUpperDevInput3 = true
upperMultInput3 = 1.0
useLowerDevInput3 = true
lowerMultInput3 = 1.0

group3 = "LINEAR REGRESSION EXTEND LINES"
extendLeftInput = input.bool(false, "Extend Lines Left", group=group3)
extendRightInput = input.bool(true, "Extend Lines Right", group=group3)
extendStyle = switch
    extendLeftInput and extendRightInput => extend.both
    extendLeftInput => extend.left
    extendRightInput => extend.right
    => extend.none

group4 = "LINEAR REGRESSION WIDTH"
a_lwidth = input.int(2, title='Large LinReg Width', minval=0, maxval=10, group=group4)
b_lwidth = input.int(1, title='Medium LinReg Width', minval=0, maxval=10, group=group4)
c_lwidth = input.int(1, title='Small LinReg Width', minval=0, maxval=10, group=group4)
d_lwidth = input.int(2, title='Base Line LinReg Width', minval=0, maxval=10, group=group4)

group5 = "LINEAR REGRESSION STYLE"
A_LineStyle_Type = input.string(title='Large LinReg Type', defval='Dotted', options=['Dashed', 'Dotted', 'Solid'], group=group5)
A_Line_Type = A_LineStyle_Type == 'Dashed' ? line.style_dashed : A_LineStyle_Type == 'Dotted' ? line.style_dotted : line.style_solid
B_LineStyle_Type = input.string(title='Medium LinReg Type', defval='Solid', options=['Dashed', 'Dotted', 'Solid'], group=group5)
B_Line_Type = B_LineStyle_Type == 'Dashed' ? line.style_dashed : B_LineStyle_Type == 'Dotted' ? line.style_dotted : line.style_solid
C_LineStyle_Type = input.string(title='Small LinReg Type', defval='Dashed', options=['Dashed', 'Dotted', 'Solid'], group=group5)
C_Line_Type = C_LineStyle_Type == 'Dashed' ? line.style_dashed : C_LineStyle_Type == 'Dotted' ? line.style_dotted : line.style_solid
D_LineStyle_Type = input.string(title='Base Line LinReg Type', defval='Solid', options=['Dashed', 'Dotted', 'Solid'], group=group5)
D_Line_Type = D_LineStyle_Type == 'Dashed' ? line.style_dashed : D_LineStyle_Type == 'Dotted' ? line.style_dotted : line.style_solid

group6 = "LINEAR REGRESSION COLOR"
colorLarge = input.color(color.new(#0c3299, 0), "Large Linreg", inline=group6, group=group6)
colorMedium = input.color(color.new(#0c3299, 0), "Medium Linreg", inline=group6, group=group6)
colorSmall = input.color(color.new(#0c3299, 0), "Small Linreg", inline=group6, group=group6)
colorBase = input.color(color.new(#0c3299, 0), "Base Line", inline=group6, group=group6)

// function
calcSlope(source, length) =>
    max_bars_back(source, 5000)
    if not barstate.islast or length <= 1
        [float(na), float(na), float(na)]
    else
        sumX = 0.0
        sumY = 0.0
        sumXSqr = 0.0
        sumXY = 0.0
        for i = 0 to length - 1 by 1
            val = source[i]
            per = i + 1.0
            sumX += per
            sumY += val
            sumXSqr += per * per
            sumXY += val * per
        slope = (length * sumXY - sumX * sumY) / (length * sumXSqr - sumX * sumX)
        average = sumY / length
        intercept = average - slope * sumX / length + slope
        [slope, average, intercept]
        
[s, a, i] = calcSlope(sourceInput, lengthInput)

// linreg base function
startPrice = i + s * (lengthInput - 1)
endPrice = i
var line baseLine = na
if na(baseLine) and not na(startPrice)
    baseLine := line.new(bar_index - lengthInput + 1, startPrice, bar_index, endPrice, width=d_lwidth, style=D_Line_Type, extend=extendStyle, color=colorBase)
else
    line.set_xy1(baseLine, bar_index - lengthInput + 1, startPrice)
    line.set_xy2(baseLine, bar_index, endPrice)
    na
    
calcDev(source, length, slope, average, intercept) =>
    upDev = 0.0
    dnDev = 0.0
    stdDevAcc = 0.0
    dsxx = 0.0
    dsyy = 0.0
    dsxy = 0.0
    periods = length - 1
    daY = intercept + slope * periods / 2
    val = intercept
    for j = 0 to periods by 1
        price = high[j] - val
        if price > upDev
            upDev := price
        price := val - low[j]
        if price > dnDev
            dnDev := price
        price := source[j]
        dxt = price - average
        dyt = val - daY
        price -= val
        stdDevAcc += price * price
        dsxx += dxt * dxt
        dsyy += dyt * dyt
        dsxy += dxt * dyt
        val += slope
    stdDev = math.sqrt(stdDevAcc / (periods == 0 ? 1 : periods))
    pearsonR = dsxx == 0 or dsyy == 0 ? 0 : dsxy / math.sqrt(dsxx * dsyy)
    [stdDev, pearsonR, upDev, dnDev]
    
[stdDev, pearsonR, upDev, dnDev] = calcDev(sourceInput, lengthInput, s, a, i)

// large linreg function
upperStartPrice = startPrice + (useUpperDevInput ? upperMultInput * stdDev : upDev)
upperEndPrice = endPrice + (useUpperDevInput ? upperMultInput * stdDev : upDev)
var line upper = na
lowerStartPrice = startPrice + (useLowerDevInput ? -lowerMultInput * stdDev : -dnDev)
lowerEndPrice = endPrice + (useLowerDevInput ? -lowerMultInput * stdDev : -dnDev)
var line lower = na
if na(upper) and not na(upperStartPrice)
    upper := useDev1Input ? line.new(bar_index - lengthInput + 1, upperStartPrice, bar_index, upperEndPrice, width=a_lwidth, style=A_Line_Type, extend=extendStyle, color=colorLarge) : na
else
    line.set_xy1(upper, bar_index - lengthInput + 1, upperStartPrice)
    line.set_xy2(upper, bar_index, upperEndPrice)
    na
if na(lower) and not na(lowerStartPrice)
    lower := useDev1Input ? line.new(bar_index - lengthInput + 1, lowerStartPrice, bar_index, lowerEndPrice, width=a_lwidth, style=A_Line_Type, extend=extendStyle, color=colorLarge) : na
else
    line.set_xy1(lower, bar_index - lengthInput + 1, lowerStartPrice)
    line.set_xy2(lower, bar_index, lowerEndPrice)
    na

// medium linreg function
upperStartPrice2 = startPrice + (useUpperDevInput2 ? upperMultInput2 * stdDev : upDev)
upperEndPrice2 = endPrice + (useUpperDevInput2 ? upperMultInput2 * stdDev : upDev)
var line upper2 = na
lowerStartPrice2 = startPrice + (useLowerDevInput2 ? -lowerMultInput2 * stdDev : -dnDev)
lowerEndPrice2 = endPrice + (useLowerDevInput2 ? -lowerMultInput2 * stdDev : -dnDev)
var line lower2 = na
if na(upper2) and not na(upperStartPrice2)
    upper2 := useDev2Input ? line.new(bar_index - lengthInput + 1, upperStartPrice2, bar_index, upperEndPrice2, width=b_lwidth, style=B_Line_Type, extend=extendStyle, color=colorMedium) : na
else
    line.set_xy1(upper2, bar_index - lengthInput + 1, upperStartPrice2)
    line.set_xy2(upper2, bar_index, upperEndPrice2)
    na
if na(lower2) and not na(lowerStartPrice2)
    lower2 := useDev2Input ? line.new(bar_index - lengthInput + 1, lowerStartPrice2, bar_index, lowerEndPrice2, width=b_lwidth, style=B_Line_Type, extend=extendStyle, color=colorMedium) : na
else
    line.set_xy1(lower2, bar_index - lengthInput + 1, lowerStartPrice2)
    line.set_xy2(lower2, bar_index, lowerEndPrice2)
    na

// small linreg fuction
upperStartPrice3 = startPrice + (useUpperDevInput3 ? upperMultInput3 * stdDev : upDev)
upperEndPrice3 = endPrice + (useUpperDevInput3 ? upperMultInput3 * stdDev : upDev)
var line upper3 = na
lowerStartPrice3 = startPrice + (useLowerDevInput3 ? -lowerMultInput3 * stdDev : -dnDev)
lowerEndPrice3 = endPrice + (useLowerDevInput3 ? -lowerMultInput3 * stdDev : -dnDev)
var line lower3 = na
if na(upper3) and not na(upperStartPrice3)
    upper3 := useDev3Input ? line.new(bar_index - lengthInput + 1, upperStartPrice3, bar_index, upperEndPrice3, width=c_lwidth, style=C_Line_Type, extend=extendStyle, color=colorSmall) : na
else
    line.set_xy1(upper3, bar_index - lengthInput + 1, upperStartPrice3)
    line.set_xy2(upper3, bar_index, upperEndPrice3)
    na
if na(lower3) and not na(lowerStartPrice3)
    lower3 := useDev3Input ? line.new(bar_index - lengthInput + 1, lowerStartPrice3, bar_index, lowerEndPrice3, width=c_lwidth, style=C_Line_Type, extend=extendStyle, color=colorSmall) : na
else
    line.set_xy1(lower3, bar_index - lengthInput + 1, lowerStartPrice3)
    line.set_xy2(lower3, bar_index, lowerEndPrice3)
    na



// TRENDLINES
// input
group7 = 'TRENDLINES'
o = open[1]
h = high
l = low
c = close
log_chart = input(true, title='Use Log Chart', group=group1)
a_Color_Type = input.string(defval='Monochrome', title='Use Trendlines Color Scheme', options=['Colored', 'Monochrome'], group=group7)

group8 = 'PRIMARY TRENDLINES'
a_Show_Primary = input(true, title='Primary Trendlines On / Off', group=group8)
a_len = input(25, title='Primary Lookback Length', group=group8)
a_Extensions = input.string(title='Primary Trendlines Extensions', defval='50', options=["Infinate", "25", "50", "75", "100", "150", "200", "300", "400", "500", "750", "1000"], group=group8)
a_width = input.int(2, title='Primary Line Width', minval=0, maxval=10, group=group8)
a_LineStyle_Type = input.string(title='Primary Line Type', defval='Solid', options=['Dashed', 'Dotted', 'Solid'], group=group8)
a_Line_Type = a_LineStyle_Type == 'Dashed' ? line.style_dashed : a_LineStyle_Type == 'Dotted' ? line.style_dotted : line.style_solid
a_Rising_Upper_Falling_Lower = false

group9 = 'SECONDARY TRENDLINES'
b_Show_Secondary = input(true, title='Secondary Trendlines On / Off', group=group9)
b_len = input(10, title='Secondary Lookback Length', group=group9)
b_Extensions = input.string(title='Secondary Trendlines Extensions', defval='25', options=["Infinate", "25", "50", "75", "100", "150", "200", "300", "400", "500", "750", "1000"], group=group9)
b_LineStyle_Type = input.string(title='Secondary Line Type', defval='Dashed', options=['Dashed', 'Dotted', 'Solid'], group=group9)
b_Line_Type = b_LineStyle_Type == 'Dashed' ? line.style_dashed : b_LineStyle_Type == 'Dotted' ? line.style_dotted : line.style_solid
b_width = input.int(2, title='Secondary Line Width', minval=0, maxval=10, group=group9)
b_Rising_Upper_Falling_Lower = false

group10 = 'TRENDLINES COLOR'
a_Line_Color = input.color(#ffffff, title="Primary Trendline Color", group=group10)
b_Line_Color = input.color(#ffffff, title="Secondary Trendline Color", group=group10)

a_bar_time = time - time[1]
b_bar_time = time - time[1]

//primary trendline
// trendline extension
a_Extension_Multiplier=
 a_Extensions=="25"?  1 :
 a_Extensions=="50"?  2 :
 a_Extensions=="75"?  3 :
 a_Extensions=="100"?  4 :
 a_Extensions=="150"?  6 :
 a_Extensions=="200"?  8 :
 a_Extensions=="300"? 12 :
 a_Extensions=="400"? 16 :
 a_Extensions=="500"? 20 :
 a_Extensions=="750"? 30 :
 a_Extensions=="1000"? 40 :
 a_Extensions=="Infinate"? 0 : na
 
// trendline function
a_f_trendline(a__input_function, a__delay, a__only_up, a__extend) =>
    var int a_Ax = 1
    var int a_Bx = 1
    var float a_By = 0
    var float a_slope = 0
    a_Ay = fixnan(a__input_function)
    if ta.change(a_Ay) != 0
        a_Ax := time[a__delay]
        a_By := a_Ay[1]
        a_Bx := a_Ax[1]
        a_slope := log_chart ? (math.log(a_Ay) - math.log(a_By)) / (a_Ax - a_Bx) : (a_Ay - a_By) / (a_Ax - a_Bx)
        a_slope
    else
        a_Ax := a_Ax[1]
        a_Bx := a_Bx[1]
        a_By := a_By[1]
        a_By
    // draw trendline
    var line a_trendline = na
    var int a_Axbis = 0
    var float a_Aybis = 0
    var bool a__xtend = true
    a_extension_time = a_Extension_Multiplier * a_bar_time * 25
    a_Axbis := a_Ax + a_extension_time
    a_Aybis := log_chart ? a_Ay * math.exp(a_extension_time * a_slope) : a_Ay + a_extension_time * a_slope
    if a_Extension_Multiplier != 0
        a__xtend := false
        a__xtend
    if ta.change(a_Ay) != 0

        a_line_color_Rising_Falling = a_slope * time < 0 ? a__only_up ? a_Rising_Upper_Falling_Lower ? a_Color_Type == 'Colored' ? color.gray : color.teal : na : a_Color_Type == 'Colored' ? #cf0a83 : color.teal : a__only_up ? a_Color_Type == 'Colored' ? #027521 : color.teal : a_Rising_Upper_Falling_Lower ? a_Color_Type == 'Colored' ? color.gray : color.teal : na
        a_line_color_Not_Rising_Falling = a_slope * time < 0 ? a__only_up ? na : a_Color_Type == 'Colored' ? #cf0a83 : color.teal : a__only_up ? a_Color_Type == 'Colored' ? #027521 : color.teal : na

        a_line_color = a_Show_Primary and not a_Rising_Upper_Falling_Lower ? a_line_color_Not_Rising_Falling : a_Show_Primary and a_Rising_Upper_Falling_Lower ? a_line_color_Rising_Falling : na

        if not na(a_line_color)
            a_trendline := line.new(a_Bx, a_By, a_Axbis, a_Aybis, xloc.bar_time, extend=a__xtend ? extend.right : extend.none, color=a_Line_Color, style=a_Line_Type, width=a_width)
            a_trendline
    [a_Bx, a_By, a_Axbis, a_Aybis, a_slope]

// calc pivot points 
a_high_point = ta.pivothigh(c > o ? h : l, a_len, a_len / 2)
a_low_point = ta.pivotlow(c > o ? l : h, a_len, a_len / 2)

// call trendline function
[a_phx1, a_phy1, a_phx2, a_phy2, a_slope_high] = a_f_trendline(a_high_point, a_len / 2, false, true)
[a_plx1, a_ply1, a_plx2, a_ply2, a_slope_low] = a_f_trendline(a_low_point, a_len / 2, true, true)

// secondary trendline
// trendline extension
b_Extension_Multiplier=
 b_Extensions=="25"?  1 :
 b_Extensions=="50"?  2 :
 b_Extensions=="75"?  3 :
 b_Extensions=="100"?  4 :
 b_Extensions=="150"?  6 :
 b_Extensions=="200"?  8 :
 b_Extensions=="300"? 12 :
 b_Extensions=="400"? 16 :
 b_Extensions=="500"? 20 :
 b_Extensions=="750"? 30 :
 b_Extensions=="1000"? 40 :
 b_Extensions=="Infinate"? 0 : na
 
// trendline function
b_f_trendline(b__input_function, b__delay, b__only_up, b__extend) =>
    var int b_Ax = 1
    var int b_Bx = 1
    var float b_By = 0
    var float b_slope = 0
    b_Ay = fixnan(b__input_function)
    if ta.change(b_Ay) != 0
        b_Ax := time[b__delay]
        b_By := b_Ay[1]
        b_Bx := b_Ax[1]
        b_slope := log_chart ? (math.log(b_Ay) - math.log(b_By)) / (b_Ax - b_Bx) : (b_Ay - b_By) / (b_Ax - b_Bx)
        b_slope
    else
        b_Ax := b_Ax[1]
        b_Bx := b_Bx[1]
        b_By := b_By[1]
        b_By
    // draw trendlines
    var line b_trendline = na
    var int b_Axbis = 0
    var float b_Aybis = 0
    var bool b__xtend = true
    b_extension_time = b_Extension_Multiplier * b_bar_time * 25
    b_Axbis := b_Ax + b_extension_time
    b_Aybis := log_chart ? b_Ay * math.exp(b_extension_time * b_slope) : b_Ay + b_extension_time * b_slope
    if b_Extension_Multiplier != 0
        b__xtend := false
        b__xtend
    if ta.change(b_Ay) != 0

        b_line_color_Rising_Falling = b_slope * time < 0 ? b__only_up ? b_Rising_Upper_Falling_Lower ? a_Color_Type == 'Colored' ? color.gray : color.teal : na : a_Color_Type == 'Colored' ? color.red : color.teal : b__only_up ? a_Color_Type == 'Colored' ? color.green : color.teal : b_Rising_Upper_Falling_Lower ? a_Color_Type == 'Colored' ? color.gray : color.teal : na
        b_line_color_Not_Rising_Falling = b_slope * time < 0 ? b__only_up ? na : a_Color_Type == 'Colored' ? color.red : color.teal : b__only_up ? a_Color_Type == 'Colored' ? color.green : color.teal : na

        b_line_color = b_Show_Secondary and not b_Rising_Upper_Falling_Lower ? b_line_color_Not_Rising_Falling : b_Show_Secondary and b_Rising_Upper_Falling_Lower ? b_line_color_Rising_Falling : na

        if not na(b_line_color)
            b_trendline := line.new(b_Bx, b_By, b_Axbis, b_Aybis, xloc.bar_time, extend=b__xtend ? extend.right : extend.none, color=b_Line_Color, style=b_Line_Type, width=b_width)
            b_trendline
    [b_Bx, b_By, b_Axbis, b_Aybis, b_slope]

// calc pivot points
b_high_point = ta.pivothigh(c > o ? h : l, b_len, b_len / 2)
b_low_point = ta.pivotlow(c > o ? l : h, b_len, b_len / 2)

// call trendline function
[b_phx1, b_phy1, b_phx2, b_phy2, b_slope_high] = b_f_trendline(b_high_point, b_len / 2, false, true)
[b_plx1, b_ply1, b_plx2, b_ply2, b_slope_low] = b_f_trendline(b_low_point, b_len / 2, true, true)

// plot
b_color_high = b_slope_high * time < 0 ? color.green : na
b_color_low = b_slope_low * time > 0 ? color.red : na

