TF Segmented Polynomial Regression [LuxAlgo]
// This work is licensed under a Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) https://creativecommons.org/licenses/by-nc-sa/4.0/
// © LuxAlgo

//@version=5
indicator("TF Segmented Polynomial Regression [LuxAlgo]"
  , "Segmented PolyReg [LuxAlgo]"
  , overlay = true)
//-----------------------------------------------------------------------------}
//Settings
//-----------------------------------------------------------------------------{
degree = input.int(3, "Polynomial Degree"
  , minval = 0
  , maxval = 10)
  
mult = input.float(2., "Width"
  , minval = 0)

tf = input.timeframe("1D", "Timeframe")

show = input(false, "Show fit for new bars")

src = input(close)

//-----------------------------------------------------------------------------}
//Define variables
//-----------------------------------------------------------------------------{
var coefficients = matrix.new<float>(degree + 1, 1)
var b            = matrix.new<float>(0, 0)

var float out = na
var sse = 0.
var k   = 0.

n = bar_index
dt = timeframe.change(tf)

//-----------------------------------------------------------------------------}
//Request future prices
//-----------------------------------------------------------------------------{
y = request.security(syminfo.tickerid
  , tf
  , request.security_lower_tf(syminfo.tickerid, timeframe.period, src)
  , lookahead = barmerge.lookahead_on)

K = array.size(y)

//-----------------------------------------------------------------------------}
//Compute polynomial regression point and intervals
//-----------------------------------------------------------------------------{

//Solve system
if dt and K > 1
    design = matrix.new<float>(0, 0)
    response = matrix.new<float>(0, 0)
    
    for i = 0 to degree
        column = array.new_float(0)
        
        for j = 0 to K - 1
            array.push(column, math.pow(j,i))
        
        matrix.add_col(design, i, column)
        
    a = matrix.inv(matrix.mult(matrix.transpose(design), design))
    b := matrix.mult(a, matrix.transpose(design))

    matrix.add_col(response, 0, y)
    
    coefficients := matrix.mult(b, response)
    
    sse := 0
    for i = 0 to K - 1
        y_ = 0.
        for j = 0 to degree
            y_ += math.pow(i, j)*matrix.get(coefficients, j, 0)
        
        sse += math.pow(array.get(y, i) - y_, 2)
    
    sse := math.sqrt(sse / (K - 1)) * mult
    
    k := 0

//Compute best fit and upper/lower intervals
reg = 0.
for i = 0 to degree
    reg += math.pow(k, i)*matrix.get(coefficients, i, 0)

if barstate.isrealtime
    if show
        out := reg
else
    out := reg
    
upper = out + sse
lower = out - sse

k += 1

//-----------------------------------------------------------------------------}
//Plot
//-----------------------------------------------------------------------------{
css = dt ? na : out > out[1] ? #0cb51a : #ff1100

plot_0 = plot(upper, "Upper"
  , color = color.new(#2157f3, 100))

plot(out, "PolyReg"
  , color = css)

plot_1 = plot(lower, "Lower"
  , color = color.new(#ff1100, 100))

fill(plot_0, plot_1
  , dt ? na : color.new(#2157f3, 80)
  , "Interval Fill")

//-----------------------------------------------------------------------------}