Volume Histogram [SpiritualHealer117]
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © spiritualhealer117

//@version=5
indicator("Volume Histogram", overlay=true)
src = input(hl2, "Source")
src2 = volume
bins = input(20, "Number of bins")
len = input(50, "Length")
offset = input(5, "Histogram Offset")
direction = input(false,"Plot Histogram Left")
sml = input(false,"Draw major levels")
sensitivity = input(80, "Major Sensitivity")
show_gap = input(true,"Show Gaps Between Bins")
adj_daily = input(true,"Adjust volume for length?")
shw_label = input(true,"Show Label")


kl_color = input.color(color.rgb(255, 20, 147, 0), "Key Level Line Color")
ml_color = input.color(color.rgb(255, 20, 147, 75), "Major Level Line Color")
fill_clr_1 = input.color(color.rgb(255,182,193, 80), "Histogram Fill Color")
outline_clr = input.color(color.rgb(255,182,193, 0), "Bar Outline Color")

fill_clr_2 = input.color(color.rgb(255,182,255, 60), "Major Histogram Bar Fill Color")
outline_clr_2 = input.color(color.rgb(255,182,255, 0), "Major Histogram Bar Outline Color")

// Calculate Range of Source Data {
max = ta.highest(src,len)
min = ta.lowest(src,len)
range_ = max - min
ma = ta.sma(src2,len)
//} 

// Find Volume in each bin and store to array
volumes = array.new<float>(bins,0)
bin_tops = array.new<float>(bins,0)
bin_bots = array.new<float>(bins,0)

if barstate.islast
    for i=0 to bins-1
        bin_bottom = min + ((range_/bins) * i)
        bin_top = bin_bottom + ((range_/bins) * 1)
        
        array.set(bin_bots, i, bin_bottom)
        array.set(bin_tops, i, bin_top)
        
        for z=0 to len
            if src[z] >= bin_bottom and src[z] < bin_top
                old_arr_val = array.get(volumes, i)
                new_arr_val = old_arr_val + src2[z]
                array.set(volumes, i, new_arr_val)


// Draw Histogram {
if barstate.islast
    for i=0 to bins-1
        v_at_i = array.get(volumes,i)
        bt_at_i = array.get(bin_tops,i)
        bb_at_i = array.get(bin_bots,i)
        v_max = array.max(volumes)
        
        v_major = array.percentrank(volumes,i) > sensitivity

        if v_max == v_at_i
            line.new(bar_index, ((bt_at_i+bb_at_i)/2), bar_index+offset, ((bt_at_i+bb_at_i)/2), extend=extend.left, color=kl_color)
        
        if v_major and sml
            line.new(bar_index, ((bt_at_i+bb_at_i)/2), bar_index+offset, ((bt_at_i+bb_at_i)/2), extend=extend.left, color=ml_color)
        
        adjusted_volume = math.round(v_at_i/ma)
        
        v_ati_corrected = adj_daily?v_at_i/len:v_at_i
        v_at_i_label = (v_ati_corrected > 1000? (v_ati_corrected>1000000?v_ati_corrected>1000000000?str.tostring(math.round(v_ati_corrected/1000000000))+"B":str.tostring(math.round(v_ati_corrected/1000000))+"M":str.tostring(math.round(v_ati_corrected/1000))+"K"):str.tostring(math.round(v_ati_corrected)))
        box.new(bar_index-(direction?adjusted_volume:-adjusted_volume)+offset,bt_at_i, bar_index+offset, bb_at_i+(show_gap?(range_/bins)*0.1:0), border_color=v_major?outline_clr_2:outline_clr, bgcolor=v_major?fill_clr_2:fill_clr_1,xloc=xloc.bar_index)
        if shw_label
            label.new(bar_index+math.round(1.5*offset),bb_at_i+(range_/bins)*0.1,text=v_at_i_label,xloc=xloc.bar_index, style=label.style_none, size=size.small)
//}
