Price Profile
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © efe_akm
//@version=5
indicator("Price Profile", overlay = true, max_boxes_count = 500)

// INPUTS
STARTTIME = input.time(title = "Start Time", defval = timestamp("01 Sep 2022 00:00"), confirm = true)
STOPTIME = input.time(title = "Stop Time", defval = timestamp("10 Sep 2022 00:00"), confirm = true)
BOX_POSITION = input.int(title = "Horizontal Position of the Bars", defval = 5, tooltip = "Shifts position of the horizontal bars to left/right")
BOX_COLORR = input.color(title = "Bar Color", defval = color.new(color.orange, 30))
SHOW_CANDLE_DIFFS = input.bool(true, "Show Candle Differences", tooltip = 'Calculates difference between upper and lower adjacent boxes')
GRANULARITY = input.int(title = "Number of Horizontal Bars", defval = 10, minval = 2 , maxval = 500, group = 'Constant Number of Boxes')
TICK_PER_BOX = input.bool(false, 'Calculate Box Size per Ticks', group = 'Ticks per Box', tooltip = 'Calculates Box Size by a defined number of ticks. Be sure that it creates less than 500 boxes otherwise the indicator may lead to error.')
NUMBER_OF_TICKS = input.int(1000, "Number of Ticks per Box", group = 'Ticks per Box')


// Draw starttime and stoptime
line.new(STARTTIME, low*0.999, STARTTIME , high * 1.0001, extend = extend.both, xloc = xloc.bar_time, color = color.orange)
line.new(STOPTIME, low*0.999, STOPTIME , high * 1.0001, extend = extend.both, xloc = xloc.bar_time, color = color.orange)


// Get bar_index for the left origin of the horizontal bars
var left_bar_index = 999999999999
if time >= STARTTIME
    left_bar_index := math.min(left_bar_index, bar_index)


// Create required arrays
var highs = array.new_float(0) // highs of the candles in time window
var lows = array.new_float(0) // lows of the candles in time window
var top_boundaries = array.new_float(GRANULARITY) // top boundaries of horizontal bars
var bottom_boundaries = array.new_float(GRANULARITY) // bottom boundaries of horizontal bars
var candle_counts = array.new_int(GRANULARITY) // candle counts for each horizontal bar
var candle_diffs = array.new_int(GRANULARITY)


// Collect all highs and lows in time window
if time >= STARTTIME and time <= STOPTIME
    array.push(highs, high)
    array.push(lows, low)

maxx = array.max(highs)
minn = array.min(lows)


// Calculate the indicator
if barstate.islast
    // step between horizontal boundaries
    step = TICK_PER_BOX ? syminfo.mintick * NUMBER_OF_TICKS : (maxx - minn) / GRANULARITY 
    granularity = TICK_PER_BOX ? (maxx - minn) / syminfo.mintick / NUMBER_OF_TICKS : GRANULARITY
    
    // Calculate top and bottom of horizontal bars
    for i = 0 to granularity - 1
        bottom = minn + (i*step)
        top = minn + ( (i+1)*step )
        
        array.insert(bottom_boundaries, i, bottom)
        array.insert(top_boundaries, i, top)
        
        //Calculate number of candles in that specific horizontal bar
        num_of_candles_in_bucket = 0
        for j = 0 to array.size(highs) - 1
            candle_above_hbar = array.get(lows,j) > top
            candle_below_hbar = array.get(highs,j) < bottom
            
            is_candle_in_bucket = not (candle_above_hbar or candle_below_hbar)
            num_of_candles_in_bucket += is_candle_in_bucket ? 1 : 0
        
        array.insert(candle_counts, i, num_of_candles_in_bucket)
            
    for i = 0 to granularity - 2
        candle_diff = array.get(candle_counts, i+1) - array.get(candle_counts, i)
        array.insert(candle_diffs, i, math.abs(candle_diff) )
    
    // Draw Boxes
    for i = 0 to granularity - 1

        // Draw Candle Counts
        horizontal_shift = int(array.size(highs)/10) * BOX_POSITION //to specify horizontal location of the bars
        box_left = left_bar_index - horizontal_shift
        box_top = array.get(top_boundaries, i)
        box_right = box_left + array.get(candle_counts,i)
        box_bottom = array.get(bottom_boundaries, i)
    
        box.new(box_left, box_top, box_right, box_bottom, xloc = xloc.bar_index, border_style = line.style_solid, border_color = color.black, border_width = 1, bgcolor = BOX_COLORR) 

    if SHOW_CANDLE_DIFFS
        for i = 0 to granularity - 2  
            // Draw Candle Diffs
            horizontal_shift_1 = int(array.size(highs)/10) * BOX_POSITION //to specify horizontal location of the bars
            box_right_1 = left_bar_index - horizontal_shift_1
            box_left_1 = box_right_1 - array.get(candle_diffs,i)
            box_top_1 = array.get(top_boundaries, i)
            box_bottom_1 = array.get(bottom_boundaries, i)
        
            box.new(box_left_1, box_top_1, box_right_1, box_bottom_1, xloc = xloc.bar_index, border_style = line.style_solid, border_color = color.black, border_width = 1, bgcolor = BOX_COLORR) 



