Regression Channel Alternative MTF V2
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © RozaniGhani-RG

//@version=5
indicator('Regression Channel Alternative MTF V2', shorttitle = 'RCAMV2', overlay = true)

// 0. Inputs
// 1. Types
// 2. Variables
// 3. Arrays
// 4. Switches
// 5. Custom Functions
// 6. Final Calculations
// 7. Constructs

//#region ———————————————————— 0. Inputs
G0       = 'Enable Regression Channel'
G1       = 'Timeframe Table'
G2       = 'Helper'
T1       = 'TF reference applicable to HIGHER, PRIMARY, LOWER and LOWEST'
T2       = 'Otherwise use TF color'
T3       = 'Affect both label and table'
bool0    = input.bool(      true,                   '1', group = G0, inline = '0')
bool1    = input.bool(      true,                   '2', group = G0, inline = '0')
bool2    = input.bool(      true,                   '3', group = G0, inline = '0', tooltip = T1)
i_c_dir  = input.bool(     false, 'Use Direction Color', group = G0, tooltip = T2)
i_s_dir  = input.string(  'Full',      'Direction text', group = G0, tooltip = T3, options = ['Up-Down', 'Up-Dn', 'Arrow', 'Full'])
i_s_font = input.string('normal',           'Font size', group = G0, tooltip = T3, options = ['tiny', 'small',  'normal', 'large', 'huge'])
i_b_TF   = input.bool(      true,                    '', group = G1, inline = '1')
i_s_Y    = input.string('bottom',                    '', group = G1, inline = '1', options = ['top',  'middle', 'bottom'])
i_s_X    = input.string(  'left',                    '', group = G1, inline = '1', options = ['left', 'center', 'right'])      
helpLine = input.bool(     false,                'Line', group = G2, inline = '2')
helpTbl  = input.bool(     false,               'Table', group = G2, inline = '2')
//#endregion

//#region ———————————————————— 1. Types
// @type     Used for int
// @field hi Int value for hi
// @field md Int value for md
// @field lo Int value for lo
type length
    int hi = na
    int md = na
    int lo = na

// @type     Used for string
// @field hi String value for hi
// @field md String value for md
// @field lo String value for lo
type name
    string hi = na
    string md = na
    string lo = na

// @type     Used for color
// @field hi Color value for hi
// @field md Color value for md
// @field lo Color value for lo
type pastel
    color hi = na
    color md = na
    color lo = na

// @type            Used for color
// @field scenario  Values such as POSITION, SWING and INTRADAY
// @field period    Values such as HIGHER, PRIMARY, LOW and LOWEST
// @field timeframe Values sudh W, D, 60, 15, 5 and 1
// @field len       Int values based on timeframe
// @field thick     Linewidth thickness based on timeframe
// @field palette   Color for line
type found
    string scenario  = na
    name   period    = na
    name   timeframe = na
    length len       = na
    length thick     = na
    pastel palette   = na

// @type         Regression Type
// @field startY Price for Start Regression
// @field endY   Price for End Regression
// @field dev    Standard Deviation
// @field slope  Regression Slope
type reg
    float startY = na
    float endY   = na
    float dev    = na
    float slope  = na
//#endregion

//#region ———————————————————— 2. Variables
clockW  = name.new( 'W', 'D','60'), LW  = length.new( 100, 20,  4), HPL = name.new( 'HIGHER', 'PRIMARY', 'LOWER'), W123 = length.new(1, 2, 3), FBP = pastel.new(chart.fg_color, color.blue, color.purple)
clockD  = name.new( 'W', 'D','60'), LD  = length.new( 465,100, 12)
clock60 = name.new( 'D','60','15'), L60 = length.new( 700,100, 28) 
clock15 = name.new('60','15', '5'), L15 = length.new( 356,100, 33)
clock05 = name.new('15', '5', '1'), L05 = length.new( 300,100, 22),                                                W234 = length.new(2, 3, 4), BPY = pastel.new(  color.blue, color.purple, color.yellow)
clock01 = name.new('15', '5', '1'), L01 = length.new(1461,479,100), PLL = name.new('PRIMARY', 'LOWER', 'LOWEST'),                              NBN = pastel.new(color.new(color.blue, 100), color.blue, na)
clock00 = name.new(  na,  na,  na), L00 = length.new( 100,100,100), NNA = name.new(       na, 'PRIMARY', na),      W111 = length.new(1, 1, 1)
var tbl   = table.new(i_s_Y + '_' + i_s_X, 20, 10, border_width = 1)
var hpTbl = table.new(position.middle_center,  4,  4, border_width = 1)
//#endregion

//#region ———————————————————— 3. Arrays
var lineTF0 = array.new<line>(3), var labelTF0 = array.new<label>(1), var lineHelp0 = array.new<line>(3), var labelHelp0 = array.new<label>(1)
var lineTF1 = array.new<line>(3), var labelTF1 = array.new<label>(1), var lineHelp1 = array.new<line>(3), var labelHelp1 = array.new<label>(1)
var lineTF2 = array.new<line>(3), var labelTF2 = array.new<label>(1), var lineHelp2 = array.new<line>(3), var labelHelp2 = array.new<label>(1)
boolTF = array.new_bool(6)
for i = 0 to 5
    boolTF.set(0, timeframe.period == 'W')
    boolTF.set(1, timeframe.period == 'D')
    boolTF.set(2, timeframe.period == '60')
    boolTF.set(3, timeframe.period == '15')
    boolTF.set(4, timeframe.period == '5')
    boolTF.set(5, timeframe.period == '1')
PSI = array.from('POSITION',   'SWING',  'INTRADAY')
TFF = array.from(  'HIGHER', 'PRIMARY',     'LOWER')
DCP = array.from(  'DISTAL',   'CLEAR', 'PROXIMATE')
WDS = array.from(       'W',       'D',        '60')
DSF = array.from(       'D',      '60',        '15')
SFF = array.from(      '60',      '15',         '5')
m = matrix.new<string>(4, 4), m.set(0, 0, '')
for i = 0 to 2
    m.set(i + 1,     0, PSI.get(i))
    m.set(i + 1,     1, WDS.get(i))
    m.set(i + 1,     2, DSF.get(i))
    m.set(i + 1,     3, SFF.get(i))
    m.set(    0, i + 1, TFF.get(i) + ' / ' +  DCP.get(i))
//#endregion

//#region ———————————————————— 4. Switches
[scenarioTF, periodTF, clockTF, lenTF, widthTF, colorTF] = switch timeframe.period
    'W'  => ['POSITION', HPL, clockW , LW,  W123, FBP] 
    'D'  => ['POSITION', HPL, clockD , LD,  W123, FBP] 
    '60' => [   'SWING', HPL, clock60, L60, W123, FBP] 
    '15' => ['INTRADAY', HPL, clock15, L15, W123, FBP] 
    '5'  => ['INTRADAY', HPL, clock05, L05, W234, BPY] 
    '1'  => ['INTRADAY', PLL, clock01, L01, W234, BPY]
    =>      [        na, PLL, clock00, L00, W111, NBN]
[strUP, strDN] = switch i_s_dir
    'Up-Down' => [   'UP ',   'DOWN']
    'Up-Dn'   => [   'UP ',     'DN']
    'Arrow'   => [     '▲',      '▼']
    'Full'    => ['  UP ▲', 'DOWN ▼']
//#endregion

//#region ———————————————————— 5. Custom Functions
// @function calculateRegression
// @param    Int value of len
// @returns  Channel values of reg type
calculateRegression(int len = 100) =>
    mid    = math.sum( close, len) / len
    slope  = ta.linreg(close, len, 0) - ta.linreg(close, len, 1)
    startY =    mid - slope * math.floor(len / 2) + (1 - len % 2) / 2 * slope
    endY   = startY + slope * (len - 1)
    dev    = 0.0
    for x = 0 to len - 1
        dev += math.pow(close[x] - (slope * (len - x) + startY), 2)
        dev
    dev := math.sqrt(dev / len)
    channel = reg.new(startY, endY, dev, slope)
    channel

// @function createLine
// @param    id, TF, foundTF, i, ref
// @returns  regression line
method createLine(line[] id = na, bool enable = na, reg TF = na, found foundTF = na, int i = na, int ref = na, bool trendcolor = na) =>
    if enable
        [x, c, w] = switch ref
            0 => [foundTF.len.hi, foundTF.palette.hi, foundTF.thick.hi]
            1 => [foundTF.len.md, foundTF.palette.md, foundTF.thick.md]
            2 => [foundTF.len.lo, foundTF.palette.lo, foundTF.thick.lo]
        col = switch trendcolor
            true  => TF.slope > 0 ? color.lime : color.red
            false => c
        line.delete(id.get(i))
        id.set(i, line.new(
         x1    = bar_index - (x - 1),
         y1    = TF.startY + TF.dev * 2 * (i - 1), 
         x2    = bar_index, 
         y2    = TF.endY   + TF.dev * 2 * (i - 1),
         style = i % 2 == 1 ? line.style_dashed : line.style_solid,
         color = col,
         width = w))
        id

// @function createHelpLine
// @param    id, TF, foundTF, ref
// @returns  Helper Line
method createHelpLine(line[] id = na, bool enable = na, reg TF = na, found foundTF = na, int ref = na) =>
    if enable
        [x, c] = switch ref
            0 => [foundTF.len.hi, foundTF.palette.hi]
            1 => [foundTF.len.md, foundTF.palette.md]
            2 => [foundTF.len.lo, foundTF.palette.lo]
        line.delete(id.get(0))
        id.set(0, line.new(
         x1    = bar_index - (x - 1),
         y1    = TF.startY + TF.dev * -2, 
         x2    = bar_index, 
         y2    = TF.startY + TF.dev * -2,
         color = c,
         style = line.style_arrow_both))
        id

// @function createLabel
// @param    id, TF, foundTF, ref, size
// @returns  regression label
method createLabel(label[] id = na, bool enable = na, reg TF = na, found foundTF = na, int ref = na, string size = na) =>
    if enable
        [x, strPeriod, strTF] = switch ref
            0 => [foundTF.len.hi, foundTF.period.hi, foundTF.timeframe.hi]
            1 => [foundTF.len.md, foundTF.period.md, foundTF.timeframe.md]
            2 => [foundTF.len.lo, foundTF.period.lo, foundTF.timeframe.lo]
        label.delete(id.get(0))
        id.set(0, label.new(
         x         = bar_index - (x - 1),
         y         = TF.startY + TF.dev * 2 * (TF.slope > 0 ? 1 : -1),
         text      = strPeriod + '\n'+ strTF + '\n'+ (TF.slope > 0 ? strUP : strDN),
         color     = color.new(color.blue, 100),
         style     = TF.slope > 0 ? label.style_label_lower_right : label.style_label_upper_right,
         textcolor = TF.slope > 0 ? color.lime : color.red,
         size      = size))
        id

// @function createHelpLabel
// @param    id, TF, foundTF, ref, size
// @returns  Helper label
method createHelpLabel(label[] id = na, bool enable = na, reg TF = na, found foundTF = na, int ref = na, string size = na) =>
    if enable
        [x, strPeriod, strTF, bgTF] = switch ref
            0 => [foundTF.len.hi, foundTF.period.hi, foundTF.timeframe.hi, foundTF.palette.hi]
            1 => [foundTF.len.md, foundTF.period.md, foundTF.timeframe.md, foundTF.palette.md]
            2 => [foundTF.len.lo, foundTF.period.lo, foundTF.timeframe.lo, foundTF.palette.lo]
        label.delete(id.get(0))
        id.set(0, label.new(
         x         = int(bar_index - x/2 + 1),
         y         = TF.startY + TF.dev * -2,
         color     = color.new(color.blue, 100),
         text      = strPeriod + '\n'+ strTF + '\n' + str.tostring(x) + ' BARS',
         textcolor = bgTF,
         size      = size))
        id

// @function row
// @param    id, column, row, _text, size, col, bg
// @returns  cell properties
method row(table id = na, int column = na, int row = na, string _text = na,
             string size = na, color col = chart.bg_color, color bg = chart.fg_color)=>
    id.cell(column, row, _text, text_color = col, text_size = i_s_font, bgcolor = bg)

// @function rowTF
// @param    id, row, foundTF, TF, size, ref
// @returns  3 rows of selected TF
method rowTF(table id = na, int row = na, found foundTF = na, reg TF = na, string size = na, int ref = na) =>
    [strPeriod, strTF, bgTF] = switch ref
        0 => [foundTF.period.hi, foundTF.timeframe.hi, foundTF.palette.hi]
        1 => [foundTF.period.md, foundTF.timeframe.md, foundTF.palette.md]
        2 => [foundTF.period.lo, foundTF.timeframe.lo, foundTF.palette.lo]
    id.row(0, row,                    strPeriod, size, TF.slope > 0 ? color.lime : color.red, bgTF)
    id.row(1, row,                        strTF, size, TF.slope > 0 ? color.lime : color.red, bgTF)
    id.row(2, row, TF.slope > 0 ? strUP : strDN, size, TF.slope > 0 ? color.lime : color.red, bgTF)
//#endregion

//#region ———————————————————— 6. Final Calculations
foundTF = found.new(scenarioTF, periodTF, clockTF, lenTF, widthTF, colorTF)
TF0     = calculateRegression(lenTF.hi)
TF1     = calculateRegression(lenTF.md)
TF2     = calculateRegression(lenTF.lo)
//#endregion

//#region ———————————————————— 7. Contructs
for i = 0 to 2
    lineTF0.createLine(  bool0, TF0, foundTF, i, 0, i_c_dir)
    lineTF1.createLine(  bool1, TF1, foundTF, i, 1, i_c_dir)
    lineTF2.createLine(  bool2, TF2, foundTF, i, 2, i_c_dir)

if boolTF.includes(true)
    labelTF0.createLabel(bool0, TF0, foundTF, 0, i_s_font)
    labelTF1.createLabel(bool1, TF1, foundTF, 1, i_s_font)
    labelTF2.createLabel(bool2, TF2, foundTF, 2, i_s_font)
    lineHelp0.createHelpLine(helpLine, TF0, foundTF, 0)
    lineHelp1.createHelpLine(helpLine, TF1, foundTF, 1)
    lineHelp2.createHelpLine(helpLine, TF2, foundTF, 2)
    labelHelp0.createHelpLabel(helpLine, TF0, foundTF, 0, i_s_font)
    labelHelp1.createHelpLabel(helpLine, TF1, foundTF, 1, i_s_font)
    labelHelp2.createHelpLabel(helpLine, TF2, foundTF, 2, i_s_font)

if barstate.islast
    if i_b_TF
        if boolTF.includes(true)
            tbl.row(0, 0, foundTF.scenario + ' TRADER', i_s_font), tbl.merge_cells(0, 0, 2, 0)
            tbl.row(0, 1,                  'TIMEFRAME', i_s_font), tbl.merge_cells(0, 1, 1, 1)
            tbl.row(2, 1,                       'ITEM', i_s_font)
            tbl.rowTF(2, foundTF, TF0, i_s_font, 0)
            tbl.rowTF(3, foundTF, TF1, i_s_font, 1)
            tbl.rowTF(4, foundTF, TF2, i_s_font, 2)
            if timeframe.period == '1'
                tbl.row(0, 5, 'CHECK PRIMARY', i_s_font, bg = color.new(color.blue, 100)), tbl.merge_cells(0, 5, 2, 5)
                tbl.row(0, 6,              '', i_s_font, bg = color.new(color.blue, 100))
            else
                tbl.row(0, 5,              '', i_s_font, bg = color.new(color.blue, 100))
        else
            tbl.row(0, 0, 'TIMEFRAME NOT SUPPORTED FOR MTF', i_s_font, color.red, color.new(color.blue, 100))
            tbl.row(0, 1,                                '', i_s_font, bg = color.new(color.blue, 100))

    if helpTbl
        for x = 0 to 3
            for y = 0 to 3
                hpTbl.cell(y, x, m.get(y, x), text_color = color.white, text_size = i_s_font, bgcolor = color.black)
        for y = 0 to 3
            hpTbl.cell_set_bgcolor(y, 1, color.gray)
            hpTbl.cell_set_bgcolor(y, 2, color.blue)
            hpTbl.cell_set_bgcolor(y, 3, color.purple)
        hpTbl.cell_set_bgcolor(0, 0, color.new(color.blue, 100))
//#endregion