Magnifying Glass (LTF Candles) by SiddWolf
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © SiddWolf

//@version=5
indicator(title="Magnifying Glass (LTF Candles) by SiddWolf", shorttitle="Magnifying Glass 🔎 [SW]", overlay=false, max_labels_count=491)


candle_body = input.string(defval="🟩 🟥", title="Candle Body: ", options=["🟩 🟥", "🟢 🔴", "🔵 🟡", "🍏 🍎", "⚪ ⚪"], inline="candle_settings", group="Candle & Volume Settings")
bullish_candle = candle_body=="🟩 🟥"? "🟩" : candle_body=="🟢 🔴"? "🟢" : candle_body=="🔵 🟡"? "🔵" : candle_body=="🍏 🍎"? "🍏" : candle_body=="⚪ ⚪"? "⚪" : "🟩"
bearish_candle = candle_body=="🟩 🟥"? "🟥" : candle_body=="🟢 🔴"? "🔴" : candle_body=="🔵 🟡"? "🟡" : candle_body=="🍏 🍎"? "🍎" : candle_body=="⚪ ⚪"? "⚪" : "🟥"

highlight_oc = input.bool(defval=true, title="HighLight Open(🇴) Close(🇨)", group="Candle & Volume Settings")

volume_settings_tooltip = "➡️ Volume Color:\n\n✅ Yellow shades at the bottom represents intensity of volume.\n✅ You can change these colors on the basis of your preference.\n✅ \"Dark for High Volume, Light for Low Volume\":\n     🏿 = Highest Volume, 🏻 = Lowest Volume\n✅ \"Light for High Volume, Dark for Low Volume\":\n     🏻 = Highest Volume, 🏿 = Lowest Volume\n\n✅ Lower TimeFrame Candle's volume is also ranked, where 1 represents Highest Volume Traded and ranked in descending order"
vol_color_setting_a = "Dark for High Volume, Light for Low Volume"
vol_color_setting_b = "Light for High Volume, Dark for Low Volume"
high_vol_dark = input.string(defval=vol_color_setting_a, title="Volume Color", options=[vol_color_setting_a, vol_color_setting_b], inline="vol_settings", group="Candle & Volume Settings", tooltip=volume_settings_tooltip)


label_settings_tooltip = "➡️ Lables Settings:\n\n✅ Up: Label Color for Bullish Candles.\n✅ Down: Label Color for Bearish Candles.\n✅ Size: Label Size\n\n✅ A reminder: Move the Magnifying Glass indicator above chart and shrink it all the way up for best visual experience."
label_color_up_input = input.color(color.new(color.green, 60), title="Up", inline="Label Settings", group="Label Settings")
label_color_down_input = input.color(color.new(color.red, 60), title="Down", inline="Label Settings", group="Label Settings")
label_size_input = input.string(defval="Small", title="Size", options=["Tiny", "Small", "Big"], inline="Label Settings", group="Label Settings", tooltip=label_settings_tooltip)


mins_1 ="1 Min", mins_2 = "2 Mins", mins_3 = "3 Mins", mins_4 = "4 Mins", mins_5 = "5 Mins", mins_10 = "10 Mins", mins_15 = "15 Mins", mins_30 = "30 Mins", mins_45 = "45 Mins", mins_90 = "90 Mins", hrs_1 = "1 Hr", hrs_2 = "2 Hrs", hrs_3 = "3 Hrs", hrs_4 = "4 Hrs", hrs_6 = "6 Hrs", hrs_12 = "12 Hrs", days_1 = "1 Day", days_2 = "2 Days", weeks_1 = "1 Week", months_1 = "1 Month", months_2 = "2 Months", months_3 = "3 Months", mins_30_stocks = "30 Mins (Only for Stocks)", hrs_1_stocks = "1 Hr (Only for Stocks)"
show_special_tooltip = "➡️ Lower TimeFrame Resolution Settings:\n\n ✅ It is HIGHLY RECOMMENDED that you use One on these chart TimeFrames (given below), with Magnifying Glass indicator. The default ltf timeframe for each chart timeframe is given below. You can change it, as per your requirements.\n✅ This indicator plots Candles from Lower TimeFrame automatically. \n\n✅ This indicator magnifies candles upto 16x (due to label's limitation). For example if the Chart TimeFrame is 4 hours or 240 minutes, This value should be higher than 240/16 = 15. That means you can plot Lower timeframe candles of 15 minutes, 30 minutes, 60 minutes or 120 minutes in 4 hours chart.\n✅ Read Indicator Description for more details.\n✅ Don't forget to \"Save as Default\" when you change below ltf to save changes."
show_special = input.bool(true, title="Special LTF Settings", tooltip=show_special_tooltip, group="Lower TimeFrame Settings")
m_15_input = input.string(defval=mins_1, title="2 Minutes to 15 minutes", options=[mins_1, mins_2, mins_3, mins_5], group="Lower TimeFrame Settings")
m_30_input = input.string(defval=mins_2, title="16 Minutes to 30 Minutes Chart", options=[mins_2, mins_3, mins_5, mins_10, mins_15], group="Lower TimeFrame Settings")
m_60_input = input.string(defval=mins_5, title="1 Hour Chart", options=[mins_4, mins_5, mins_10, mins_15, mins_30], group="Lower TimeFrame Settings")
m_120_input = input.string(defval=mins_15, title="2 Hours Chart", options=[mins_10, mins_15, mins_30, hrs_1], group="Lower TimeFrame Settings")
m_240_input = input.string(defval=mins_15, title="4 Hours Chart", options=[mins_15, mins_30, hrs_1, hrs_2], group="Lower TimeFrame Settings")
m_360_input = input.string(defval=mins_30, title="6 Hours Chart", options=[mins_30, hrs_1, hrs_2], group="Lower TimeFrame Settings")
m_480_input = input.string(defval=mins_30, title="8 Hours Chart", options=[mins_30, hrs_1, hrs_2, hrs_4], group="Lower TimeFrame Settings")
m_720_input = input.string(defval=hrs_1, title="12 Hours Chart", options=[mins_45, hrs_1, hrs_2, hrs_4], group="Lower TimeFrame Settings")
d_1_input = input.string(defval=hrs_2, title="1 Day Chart", options=[mins_30_stocks, hrs_1_stocks, mins_90, hrs_2, hrs_4, hrs_6, hrs_12], tooltip="30 Minutes and 1 hour LTF Candle only works with stocks or indices that are traded less than 7 hours in a day", group="Lower TimeFrame Settings")
d_2_input = input.string(defval=hrs_4, title="2 Days Chart", options=[hrs_3, hrs_4, hrs_6, hrs_12, days_1], group="Lower TimeFrame Settings")
w_1_input = input.string(defval=hrs_12, title="1 Week Chart", options=[hrs_12, days_1], group="Lower TimeFrame Settings")
w_2_input = input.string(defval=days_1, title="2 Weeks Chart", options=[days_1, days_2], group="Lower TimeFrame Settings")
M_1_input = input.string(defval=days_2, title="1 Month Chart", options=[days_2, weeks_1], group="Lower TimeFrame Settings")
M_3_input = input.string(defval=weeks_1, title="3 Months Chart", options=[weeks_1, months_1], group="Lower TimeFrame Settings")
M_6_input = input.string(defval=months_1, title="6 Months Chart", options=[months_1, months_2, months_3], group="Lower TimeFrame Settings")
M_12_input = input.string(defval=months_1, title="12 Months Chart", options=[months_1, months_2, months_3], group="Lower TimeFrame Settings")


tf_mult = timeframe.multiplier


ltfStep_intraday() =>
    string result =
      switch
        tf_mult <= 16 => "1"
        tf_mult <= 32 => "2"
        tf_mult <= 48 => "3"
        tf_mult <= 64 => "4"
        tf_mult <= 80 => "5"
        tf_mult <= 160 => "10"
        tf_mult <= 240 => "15"
        tf_mult <= 480 => "30"
        tf_mult <= 960 => "60"
        tf_mult <= 1440 => "120"


special_tf_str(input_str) =>
    string result =
      switch
        input_str == mins_1 => "1"
        input_str == mins_2 => "2"
        input_str == mins_3 => "3"
        input_str == mins_4 => "4"
        input_str == mins_5 => "5"
        input_str == mins_10 => "10"
        input_str == mins_15 => "15"
        input_str == mins_30 or input_str == mins_30_stocks => "30"
        input_str == mins_45 => "45"
        input_str == mins_90 => "90"
        input_str == hrs_1 or input_str == hrs_1_stocks => "60"
        input_str == hrs_2 => "120"
        input_str == hrs_3 => "180"
        input_str == hrs_4 => "240"
        input_str == hrs_6 => "360"
        input_str == hrs_12 => "720"
        input_str == days_1 => "1D"
        input_str == days_2 => "2D"
        input_str == weeks_1 => "1W"
        input_str == months_1 => "1M"
        input_str == months_2 => "2M"
        input_str == months_3 => "3M"


ltfStep_special() =>
    string result =
      switch
        timeframe.isintraday and tf_mult <= 15 => special_tf_str(m_15_input)
        timeframe.isintraday and tf_mult <= 30 => special_tf_str(m_30_input)
        timeframe.isintraday and tf_mult==60 => special_tf_str(m_60_input)
        timeframe.isintraday and tf_mult==120 => special_tf_str(m_120_input)
        timeframe.isintraday and tf_mult==240 => special_tf_str(m_240_input)
        timeframe.isintraday and tf_mult==360 => special_tf_str(m_360_input)
        timeframe.isintraday and tf_mult==480 => special_tf_str(m_480_input)
        timeframe.isintraday and tf_mult==720 => special_tf_str(m_720_input)
        (timeframe.isdaily and tf_mult == 1) or (timeframe.isintraday and tf_mult == 1440) == 1 => special_tf_str(d_1_input)
        timeframe.isdaily and tf_mult == 2 => special_tf_str(d_2_input)
        (timeframe.isdaily and tf_mult == 7) or (timeframe.isweekly and tf_mult == 1) => special_tf_str(w_1_input)
        timeframe.isweekly and tf_mult == 2 => special_tf_str(w_2_input)
        timeframe.ismonthly and tf_mult == 1 => special_tf_str(M_1_input)
        timeframe.ismonthly and tf_mult == 3 => special_tf_str(M_3_input)
        timeframe.ismonthly and tf_mult == 6 => special_tf_str(M_6_input)
        timeframe.ismonthly and tf_mult == 12 => special_tf_str(M_12_input)


ltf_special_ret_val = ltfStep_special()
var string sidd_reso_var = show_special and ltf_special_ret_val!=""? ltf_special_ret_val : timeframe.isintraday ? ltfStep_intraday() : ""


ltf_res = str.tonumber(sidd_reso_var)


sidd_matrix = matrix.new<string>(22, 19, "⬛")


[ret_open_arr, ret_high_arr, ret_low_arr, ret_close_arr, ret_volume_arr] = request.security_lower_tf(syminfo.tickerid, sidd_reso_var, [open, high, low, close, volume])

main_candle_high = high
main_candle_low = low
main_candle_volume = volume


main_candle_range = main_candle_high-main_candle_low

close_arr_size = array.size(ret_close_arr)

if close_arr_size>16 and (d_1_input==mins_30_stocks or d_1_input==hrs_1_stocks)
    runtime.error("30 Minutes and 1 Hours LTF candle in a daily chart only works for the securities that are traded 7 hours or less. Change TimeFrame in Settings")

for j=0 to 19
    matrix.set(sidd_matrix, j, 1, highlight_oc ? "       " : "")
    matrix.set(sidd_matrix, j, close_arr_size-1+3, "")
matrix.set(sidd_matrix, 20, 0, "🇸")
matrix.set(sidd_matrix, 20, 1, highlight_oc ? " 🇮" : "")
matrix.set(sidd_matrix, 21, 0, "🇩")
matrix.set(sidd_matrix, 21, 1, highlight_oc ? " 🇩" : "")
matrix.set(sidd_matrix, 20, close_arr_size-1+3, " ")
matrix.set(sidd_matrix, 21, close_arr_size-1+3, " ")

if close_arr_size>0
    for i = 0 to close_arr_size-1


        open_ltf_candle = array.get(ret_open_arr, i)
        high_ltf_candle = array.get(ret_high_arr, i)
        low_ltf_candle = array.get(ret_low_arr, i)
        close_ltf_candle = array.get(ret_close_arr, i)

        pcent_by_5_openee = int(((main_candle_high-open_ltf_candle)/main_candle_range)*20)
        pcent_by_5_openee := pcent_by_5_openee<1 ? 0 : pcent_by_5_openee>19 ? 19 : pcent_by_5_openee

        pcent_by_5_highee = int(((main_candle_high-high_ltf_candle)/main_candle_range)*20)
        pcent_by_5_highee := pcent_by_5_highee<1 ? 0 : pcent_by_5_highee>19 ? 19 : pcent_by_5_highee

        pcent_by_5_lowee = int(((main_candle_high-low_ltf_candle)/main_candle_range)*20)
        pcent_by_5_lowee := pcent_by_5_lowee<1 ? 0 : pcent_by_5_lowee>19 ? 19 : pcent_by_5_lowee

        pcent_by_5_closeee = int(((main_candle_high-close_ltf_candle)/main_candle_range)*20)
        pcent_by_5_closeee := pcent_by_5_closeee<1 ? 0 : pcent_by_5_closeee>19 ? 19 : pcent_by_5_closeee

        is_green_candle = close_ltf_candle>open_ltf_candle


        if i==0
            matrix.set(sidd_matrix, pcent_by_5_openee, i+1, highlight_oc ? "🇴 " : "")
        if i==close_arr_size-1
            matrix.set(sidd_matrix, pcent_by_5_closeee, i+3, highlight_oc ? "🇨" : "")

        if is_green_candle
            if pcent_by_5_openee != pcent_by_5_closeee
                for green_wicks_up = pcent_by_5_closeee to pcent_by_5_highee
                    matrix.set(sidd_matrix, green_wicks_up, i+2, "  │ ")
                for green_wicks_down = pcent_by_5_lowee to pcent_by_5_openee
                    matrix.set(sidd_matrix, green_wicks_down, i+2, "  │ ")
            for green_candles = pcent_by_5_openee to pcent_by_5_closeee
                matrix.set(sidd_matrix, green_candles, i+2, bullish_candle)
        else
            if pcent_by_5_closeee != pcent_by_5_openee
                for red_wicks_up = pcent_by_5_openee to pcent_by_5_highee
                    matrix.set(sidd_matrix, red_wicks_up, i+2, "  │ ")
                for red_wicks_down = pcent_by_5_closeee to pcent_by_5_lowee
                    matrix.set(sidd_matrix, red_wicks_down, i+2, "  │ ")
            for red_candles = pcent_by_5_closeee to pcent_by_5_openee
                matrix.set(sidd_matrix, red_candles, i+2, bearish_candle)

vol_shade_str(rank) =>
    string result =
      switch
        rank==1 => "⒈ "
        rank==2 => "⒉ "
        rank==3 => "⒊ "
        rank==4 => "⒋ "
        rank==5 => "⒌ "
        rank==6 => "⒍ "
        rank==7 => "⒎ "
        rank==8 => "⒏ "
        rank==9 => "⒐ "
        rank==10 => "⒑ "
        rank==11 => "⒒ "
        rank==12 => "⒓ "
        rank==13 => "⒔ "
        rank==14 => "⒕ "
        rank==15 => "⒖ "
        rank==16 => "⒗ "
        rank==17 => "⒘ "
        rank==18 => "⒙ "
        rank==19 => "⒚ "
        rank==20 => "⒛ "


volume_arr_size = array.size(ret_volume_arr)

ret_volume_arr_copy = array.copy(ret_volume_arr)

array.sort(ret_volume_arr_copy, order.descending)


if volume_arr_size>0
    for i = 0 to volume_arr_size-1
        sort_rank = array.indexof(ret_volume_arr_copy, array.get(ret_volume_arr, i))+1
        ranknum_str = vol_shade_str(sort_rank)
        if i==0
            matrix.set(sidd_matrix, 21, i+2, "  "+ranknum_str)
        else
            matrix.set(sidd_matrix, 21, i+2, ranknum_str)

        if sort_rank==1
            matrix.set(sidd_matrix, 20, i+2, high_vol_dark==vol_color_setting_a ? "🏿" : "🏻")
        else if sort_rank>1 and sort_rank<=int(close_arr_size/3)
            matrix.set(sidd_matrix, 20, i+2, high_vol_dark==vol_color_setting_a ? "🏾" : "🏼")
        else if sort_rank>int(close_arr_size/3) and sort_rank<=int(close_arr_size*2/3)
            matrix.set(sidd_matrix, 20, i+2, high_vol_dark==vol_color_setting_a ? "🏽" : "🏽")
        else if sort_rank>int(close_arr_size*2/3) and sort_rank<close_arr_size
            matrix.set(sidd_matrix, 20, i+2, high_vol_dark==vol_color_setting_a ? "🏼" : "🏾")
        else if sort_rank==close_arr_size
            matrix.set(sidd_matrix, 20, i+2, high_vol_dark==vol_color_setting_a ? "🏻" : "🏿")

sidd_vol_profile_arr = array.new_float(20)


for i=0 to 19
    row_vol_profile = 0.0
    if volume_arr_size>0
        for j = 0 to volume_arr_size-1
            volume_ltf = array.get(ret_volume_arr, j)
            matrix_get = matrix.get(sidd_matrix, i, j+2)
            if matrix_get=="  │ "
                row_vol_profile := row_vol_profile+(0.6*volume_ltf)
            else if matrix_get!="⬛"
                row_vol_profile := row_vol_profile+(1*volume_ltf)
    array.set(sidd_vol_profile_arr, i, row_vol_profile)


vol_profile_arr_copy = array.copy(sidd_vol_profile_arr)
array.sort(vol_profile_arr_copy, order.descending)
for i = 0 to 19
    sort_rank = array.indexof(vol_profile_arr_copy, array.get(sidd_vol_profile_arr, i))+1
    if sort_rank==1
        matrix.set(sidd_matrix, i, 0, high_vol_dark==vol_color_setting_a ? "🏿" : "🏻")
    else if sort_rank>1 and sort_rank<=int(20/3)
        matrix.set(sidd_matrix, i, 0, high_vol_dark==vol_color_setting_a ? "🏾" : "🏼")
    else if sort_rank>int(20/3) and sort_rank<=int(20*2/3)
        matrix.set(sidd_matrix, i, 0, high_vol_dark==vol_color_setting_a ? "🏽" : "🏽")
    else if sort_rank>int(20*2/3) and sort_rank<20
        matrix.set(sidd_matrix, i, 0, high_vol_dark==vol_color_setting_a ? "🏼" : "🏾")
    else if sort_rank==20
        matrix.set(sidd_matrix, i, 0, high_vol_dark==vol_color_setting_a ? "🏻" : "🏿")


sidd_tooltip_str = ""

for i=0 to 21
    if close_arr_size>0
        for j = 0 to close_arr_size-1+3
            matrix_get = matrix.get(sidd_matrix, i, j)
            sidd_tooltip_str := sidd_tooltip_str+matrix_get
    sidd_tooltip_str := sidd_tooltip_str+"\n"


exact_time_func() =>
    string result =
      switch
        timeframe.isintraday and tf_mult <= 15 => m_15_input
        timeframe.isintraday and tf_mult <= 30 => m_30_input
        timeframe.isintraday and tf_mult==60 => m_60_input
        timeframe.isintraday and tf_mult==120 => m_120_input
        timeframe.isintraday and tf_mult==240 => m_240_input
        timeframe.isintraday and tf_mult==360 => m_360_input
        timeframe.isintraday and tf_mult==480 => m_480_input
        timeframe.isintraday and tf_mult==720 => m_720_input
        (timeframe.isdaily and tf_mult == 1) or (timeframe.isintraday and tf_mult == 1440) == 1 => (d_1_input==mins_30_stocks ? mins_30 : d_1_input==hrs_1_stocks ? hrs_1 : d_1_input)
        timeframe.isdaily and tf_mult == 2 => d_2_input
        (timeframe.isdaily and tf_mult == 7) or (timeframe.isweekly and tf_mult == 1) => w_1_input
        timeframe.isweekly and tf_mult == 2 => w_2_input
        timeframe.ismonthly and tf_mult == 1 => M_1_input
        timeframe.ismonthly and tf_mult == 3 => M_3_input
        timeframe.ismonthly and tf_mult == 6 => M_6_input
        timeframe.ismonthly and tf_mult == 12 => M_12_input

time_exact = exact_time_func()

new_line_or_space = close_arr_size>10? " ": (highlight_oc ? "\n            " : "\n        ")
main_candle_range_pcent = math.round(((main_candle_high-main_candle_low)/(close>open?main_candle_low:main_candle_high))*100, 2)

wolf_pic = highlight_oc ? "   🐺" : "🐺"
sidd_tooltip_str := sidd_tooltip_str+ wolf_pic +"   ✳️ Candle: "+ time_exact + new_line_or_space + "✳️ Range:" + str.tostring(main_candle_range_pcent) + "%"


label_size = label_size_input=="Tiny" ? size.tiny : label_size_input=="Small" ? size.small : size.normal
label_color = close>open ? label_color_up_input : label_color_down_input
label_id = label.new(bar_index, 1, text="", color=label_color, size=label_size, style=label.style_label_down)
label.set_tooltip(label_id, sidd_tooltip_str)

plot(1, style=plot.style_columns, color=label_color)

 
if (timeframe.isintraday and tf_mult/ltf_res>16) or (timeframe.isdaily and close_arr_size>16) or sidd_reso_var==""
    runtime.error("Custom Lower TF Resolution Too Short. Use this indicator with one of the timeframes given in the Indicator Settings and check \"Special LTF Settings\".")


