Multi-Timeframe Squeeze Pro/DIM/Momentum/MA
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Timmy1986 inspired by Beardy_Fred

//@version=5
indicator("Multi-Timeframe Squeeze Pro/DIM/Momentum/MA", shorttitle="MTF SQZ/DMI/MA/MOM", overlay=true)

show_mom        = input.bool(false, "Show Momentum",group = "General",inline="Visibility-1")
show_sq         = input.bool(true, "Show squeeze",group = "General",inline="Visibility-1")
show_10X        = input.bool(true, "Show 10x",group = "General",inline="Visibility-2")
show_ma         = input.bool(true, "Show Stack MA",group = "General",inline="Visibility-2")

//Timeframes
tf_1 = input.timeframe(title="Res-1",defval="60",group = "Resolution",inline="Resolution-1")
tf_2 = input.timeframe(title="Res-2",defval="240",group = "Resolution",inline="Resolution-1")
tf_3 = input.timeframe(title="Res-3",defval="D",group = "Resolution",inline="Resolution-1")
tf_4 = input.timeframe(title="Res-4",defval="2D",group = "Resolution",inline="Resolution-2")
tf_5 = input.timeframe(title="Res-5",defval="3D",group = "Resolution",inline="Resolution-2")
tf_6 = input.timeframe(title="Res-6",defval="4D",group = "Resolution",inline="Resolution-2")
tf_7 = input.timeframe(title="Res-7",defval="W",group = "Resolution",inline="Resolution-3")
tf_8 = input.timeframe(title="Res-8",defval="M",group = "Resolution",inline="Resolution-3")

tableYposInput  = input.string("top", "Panel position", options = ["top", "middle", "bottom"],group = "Table Settings",inline="Table-1")
tableXposInput  = input.string("right", "", options = ["left", "center", "right"],group = "Table Settings",inline="Table-1")

TC              = input.color(color.new(color.white, 0), "Table Text Color",group = "Table Settings",inline="Table-2")
TS              = input.string(size.small, "Table Text Size", options = [size.tiny, size.small, size.normal, size.large],group = "Table Settings",inline="Table-2")

length          = input.int(20, "TTM Squeeze Length",group="Squeeze Settings")

//BOLLINGER BANDS
BB_mult         = input.float(2.0, "Bollinger Band STD Multiplier",group="Squeeze Settings")
BB_basis        = ta.sma(close, length)
dev             = BB_mult * ta.stdev(close, length)
BB_upper        = BB_basis + dev
BB_lower        = BB_basis - dev

//KELTNER CHANNELS
KC_mult_high    = input.float(1.0, "Keltner Channel #1",group="Squeeze Settings")
KC_mult_mid     = input.float(1.5, "Keltner Channel #2",group="Squeeze Settings")
KC_mult_low     = input.float(2.0, "Keltner Channel #3",group="Squeeze Settings")
KC_basis        = ta.sma(close, length)
devKC           = ta.sma(ta.tr, length)
KC_upper_high   = KC_basis + devKC * KC_mult_high
KC_lower_high   = KC_basis - devKC * KC_mult_high
KC_upper_mid    = KC_basis + devKC * KC_mult_mid
KC_lower_mid    = KC_basis - devKC * KC_mult_mid
KC_upper_low    = KC_basis + devKC * KC_mult_low
KC_lower_low    = KC_basis - devKC * KC_mult_low

//SQUEEZE CONDITIONS
NoSqz           = BB_lower < KC_lower_low or BB_upper > KC_upper_low //NO SQUEEZE: GREEN
LowSqz          = BB_lower >= KC_lower_low or BB_upper <= KC_upper_low //LOW COMPRESSION: BLACK
MidSqz          = BB_lower >= KC_lower_mid or BB_upper <= KC_upper_mid //MID COMPRESSION: RED
HighSqz         = BB_lower >= KC_lower_high or BB_upper <= KC_upper_high //HIGH COMPRESSION: ORANGE

//MOMENTUM OSCILLATOR
mom             = ta.linreg(close - math.avg(math.avg(ta.highest(high, length), ta.lowest(low, length)), ta.sma(close, length)), length, 0)

//MOMENTUM HISTOGRAM COLOR
mom_up1_col     = input.color(color.new(color.aqua, 0), title = "+ive Rising Momentum", group = "Squeeze Histogram Color",inline="Color-1")
mom_up2_col     = input.color(color.new(#2962ff, 0), title = "+ive Falling Momentum", group = "Squeeze Histogram Color",inline="Color-1")
mom_down1_col   = input.color(color.new(color.red, 0), title = "-ive Rising Momentum", group = "Squeeze Histogram Color",inline="Color-2")
mom_down2_col   = input.color(color.new(color.yellow, 0), title = "-ive Falling Momentum", group = "Squeeze Histogram Color",inline="Color-2")

iff_1           = mom > nz(mom[1]) ? mom_up1_col : mom_up2_col
iff_2           = mom < nz(mom[1]) ? mom_down1_col : mom_down2_col
mom_color       = mom > 0 ? iff_1 : iff_2

//SQUEEZE DOTS COLOR
NoSqz_Col       = input.color(color.new(color.green, 0), title = "No Squeeze", group = "Squeeze Dot Color",inline="Color-3")
LowSqz_Col      = input.color(color.new(color.black, 0), title = "Low Compression", group = "Squeeze Dot Color",inline="Color-3")
MidSqz_Col      = input.color(color.new(color.red, 0), title = "Medium Compression", group = "Squeeze Dot Color",inline="Color-4")
HighSqz_Col     = input.color(color.new(color.orange, 0), title = "High Compression", group = "Squeeze Dot Color",inline="Color-4")

sq_color        = HighSqz ? HighSqz_Col : MidSqz ? MidSqz_Col : LowSqz ? LowSqz_Col : NoSqz_Col


//DI computation
len             = input.int(14, "Directional Length",group="10X Settings")
ADX_T           = input.int(20, "ADX Length",group="10X Settings")

//DMI CALCULATIONS - Tradingview Built-In DMI Indicator
up              = ta.change(high)
down            = -ta.change(low)
plusDM          = na(up) ? na : (up > down and up > 0 ? up : 0)
minusDM         = na(down) ? na : (down > up and down > 0 ? down : 0)
trur            = ta.rma(ta.tr, len)
plus            = fixnan(100 * ta.rma(plusDM, len) / trur)
minus           = fixnan(100 * ta.rma(minusDM, len) / trur)
sum             = plus + minus
adx             = 100 * ta.rma(math.abs(plus - minus) / (sum == 0 ? 1 : sum), len)

//10X CALCULATIONS
D_Up            = (plus > minus) and (adx > ADX_T)
D_Down          = (minus > plus) and (adx > ADX_T)
Neutral         = (adx < ADX_T)

D_Up_Color      = input.color(color.new(color.green, 0), title = "Uptrend",group="10X Colors",inline="Color-5")
D_Down_Color    = input.color(color.new(color.red, 0), title = "Downtrend",group="10X Colors",inline="Color-5")
Neutral_Color   = input.color(color.new(color.yellow, 0), title = "Neutral",group="10X Colors",inline="Color-5")

MTF10_Color     = Neutral ? Neutral_Color : D_Up ? D_Up_Color : D_Down ? D_Down_Color : na

//Get the stacked EM
// length option
//Code from © dvinales

len1 = input.int(title="MA-1", defval=8, minval=1,group="Stack MA Lengths",inline="stack-2")
len2 = input.int(title="MA-2", defval=21, minval=1,group="Stack MA Lengths",inline="stack-2")
len3 = input.int(title="MA-3", defval=34, minval=1,group="Stack MA Lengths",inline="stack-2")
len4 = input.int(title="MA-4", defval=55, minval=0,group="Stack MA Lengths",inline="stack-2")
len5 = input.int(title="MA-5", defval=89, minval=0,group="Stack MA Lengths",inline="stack-2")
source = input.source(title="Source", defval=close,group="Stack MA",inline="stack-1")
mafn = input.string(title="Moving Average", defval="EMA", options=["SMA", "EMA", "DEMA"],group="Stack MA",inline="stack-1")


D_Stack_Color        = input.color(color.new(color.green, 0), title = "Stacked +",group="Stack MA Colors",inline="Color-6")
D_ReverseStack_Color = input.color(color.new(color.red, 0), title = "Stacked -",group="Stack MA Colors",inline="Color-6")
D_UnStacked_Color    = input.color(color.new(color.yellow, 0), title = "Unstacked",group="Stack MA Colors",inline="Color-6")

fnMA(series, length) =>
    if (mafn == "EMA")
        ta.ema(series, length)
    else
        if (mafn == "SMA")
            ta.sma(series, length)
        else
            if (length > 0)
                emaValue = ta.ema(series, length)
                2 * emaValue - ta.ema(emaValue, length)
            else
                na

ma1 = fnMA(source, len1)
ma2 = fnMA(source, len2)
ma3 = fnMA(source, len3)
ma4 = len4 > 0 ? fnMA(source, len4) : na
ma5 = len5 > 0 ? fnMA(source, len5) : na

//get whether the MA are stacked 
is_uptrend = ma1 > ma2 and ma2 > ma3 and (ma3 > ma4 or len4 == 0)  and (ma4 > ma5 or len4 == 0 or len5 == 0)
is_downtrend = (ma5 > ma4 or len4 == 0 or len5 == 0) and (ma4 > ma3 or len4 == 0) and ma3 > ma2 and ma2 > ma1

stacked_ma_color = is_uptrend ? D_Stack_Color : (is_downtrend ? D_ReverseStack_Color : D_UnStacked_Color)

// —————————— PineCoders MTF Selection Framework functions
// ————— Converts current "timeframe.multiplier" plus the TF into minutes of type float.
f_resInMinutes() => 
    _resInMinutes = timeframe.multiplier * (
      timeframe.isseconds ? 1. / 60. :
      timeframe.isminutes ? 1.       :
      timeframe.isdaily   ? 1440.    :
      timeframe.isweekly  ? 10080.   :
      timeframe.ismonthly ? 43800.   : na)

// ————— Returns resolution of _resolution period in minutes.
f_tfResInMinutes(_res) =>
    // _res: resolution of any TF (in "timeframe.period" string format).
    request.security(syminfo.tickerid, _res, f_resInMinutes())

//Array to store the original timeframes
string[] timeframes_arr = array.from(tf_1,tf_2,tf_3,tf_4,tf_5,tf_6,tf_7,tf_8)
//Array with timeframe in minutes for comparaison purposes
float[] timeframes_arr_mn = array.from(f_tfResInMinutes(tf_1),f_tfResInMinutes(tf_2),f_tfResInMinutes(tf_3),f_tfResInMinutes(tf_4),f_tfResInMinutes(tf_5),f_tfResInMinutes(tf_6),f_tfResInMinutes(tf_7),f_tfResInMinutes(tf_8))


//MULTI TIMEFRAME HISTOGRAM COLOR
[HC_1]         = request.security(syminfo.tickerid, tf_1, [mom_color])
[HC_2]         = request.security(syminfo.tickerid, tf_2, [mom_color])
[HC_3]          = request.security(syminfo.tickerid, tf_3  , [mom_color])
[HC_4]         = request.security(syminfo.tickerid, tf_4  , [mom_color])
[HC_5]         = request.security(syminfo.tickerid, tf_5  , [mom_color])
[HC_6]         = request.security(syminfo.tickerid, tf_6  , [mom_color])
[HC_7]          = request.security(syminfo.tickerid, tf_7  , [mom_color])
[HC_8]          = request.security(syminfo.tickerid, tf_8  , [mom_color])

//since i cannot dynamically use security request using a table index same way as the timeframe to build the table
color[] color_hc = array.from(HC_1,HC_2,HC_3,HC_4,HC_5,HC_6,HC_7,HC_8)

//MULTI TIMEFRAME SQUEEZE COLOR
[SC_1]         = request.security(syminfo.tickerid, tf_1, [sq_color])
[SC_2]         = request.security(syminfo.tickerid, tf_2, [sq_color])
[SC_3]          = request.security(syminfo.tickerid, tf_3  , [sq_color])
[SC_4]         = request.security(syminfo.tickerid, tf_4  , [sq_color])
[SC_5]         = request.security(syminfo.tickerid, tf_5  , [sq_color])
[SC_6]         = request.security(syminfo.tickerid, tf_6  , [sq_color])
[SC_7]          = request.security(syminfo.tickerid, tf_7  , [sq_color])
[SC_8]          = request.security(syminfo.tickerid, tf_8  , [sq_color])

color[] color_sc = array.from(SC_1,SC_2,SC_3,SC_4,SC_5,SC_6,SC_7,SC_8)

//MULTI TIMEFRAME DMI
[MTF10_1]      = request.security(syminfo.tickerid, tf_1, [MTF10_Color])
[MTF10_2]      = request.security(syminfo.tickerid, tf_2, [MTF10_Color])
[MTF10_3]       = request.security(syminfo.tickerid, tf_3, [MTF10_Color])
[MTF10_4]      = request.security(syminfo.tickerid, tf_4  , [MTF10_Color])
[MTF10_5]      = request.security(syminfo.tickerid, tf_5  , [MTF10_Color])
[MTF10_6]      = request.security(syminfo.tickerid, tf_6  , [MTF10_Color])
[MTF10_7]       = request.security(syminfo.tickerid, tf_7, [MTF10_Color])
[MTF10_8]       = request.security(syminfo.tickerid, tf_8, [MTF10_Color])

color[] color_mft10 = array.from(MTF10_1,MTF10_2,MTF10_3,MTF10_4,MTF10_5,MTF10_6,MTF10_7,MTF10_8)

//MUTLTI TIMEFRAME STACK MA
[SM_1]      = request.security(syminfo.tickerid, tf_1, [stacked_ma_color])
[SM_2]      = request.security(syminfo.tickerid, tf_2, [stacked_ma_color])
[SM_3]       = request.security(syminfo.tickerid, tf_3, [stacked_ma_color])
[SM_4]      = request.security(syminfo.tickerid, tf_4  , [stacked_ma_color])
[SM_5]      = request.security(syminfo.tickerid, tf_5  , [stacked_ma_color])
[SM_6]      = request.security(syminfo.tickerid, tf_6  , [stacked_ma_color])
[SM_7]       = request.security(syminfo.tickerid, tf_7, [stacked_ma_color])
[SM_8]       = request.security(syminfo.tickerid, tf_8, [stacked_ma_color])

color[] color_sm = array.from(SM_1,SM_2,SM_3,SM_4,SM_5,SM_6,SM_7,SM_8)

//use table length to get the number of column
var table TTM   = table.new(tableYposInput + "_" + tableXposInput, 9, 4, border_width = 1)

//create header
table.cell(TTM, 0, 0, "MOM", text_color = color.new(color.white, 0), bgcolor = color.new(color.gray, 0), text_size = TS)
table.cell(TTM, 0, 1, "SQZ", text_color = color.new(color.white, 0), bgcolor = color.new(color.gray, 0), text_size = TS)
table.cell(TTM, 0, 2, "10X", text_color = color.new(color.white, 0), bgcolor = color.new(color.gray, 0), text_size = TS)
table.cell(TTM, 0, 3, str.tostring("STK "+mafn), text_color = color.new(color.white, 0), bgcolor = color.new(color.gray, 0), text_size = TS)


i = 0
//resolution in min
float res = f_resInMinutes()
        
           
//use loop because of the array
//create the table
//Execute the loop only on the last candle (otherwise the loop execute on each candle causing an error)
if barstate.islastconfirmedhistory
    while i < array.size(timeframes_arr)
            //cannot use this code because the security call require an simple string 
            // while an array.get return a string serie. I cannot cast this. Otherwise will have done dynamic security call...
            // [HC] = request.security(syminfo.tickerid, res, [mom_color])
        if  res <= array.get(timeframes_arr_mn,i) 
            table.cell(TTM, i+1, 0, array.get(timeframes_arr,i), text_color = TC, bgcolor = array.get(color_hc,i), text_size = TS)
            table.cell(TTM, i+1, 1, array.get(timeframes_arr,i), text_color = TC, bgcolor = array.get(color_sc,i), text_size = TS)
            table.cell(TTM, i+1, 2, array.get(timeframes_arr,i), text_color = TC, bgcolor = array.get(color_mft10,i), text_size = TS)
            table.cell(TTM, i+1, 3, array.get(timeframes_arr,i), text_color = TC, bgcolor = array.get(color_sm,i), text_size = TS)
        i+=1
    
  //Stacked MA
if not show_ma
    table.clear(TTM,0,3,8,3)  
if not show_mom 
    table.clear(TTM,0,0,8,0)
if not show_sq 
    table.clear(TTM,0,1,8,1)
if not show_10X 
    table.clear(TTM,0,2,8,2)
        
  
    