HTF Candle Boxes for LTF Charts
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// Kevin Rollo

//@version = 5
indicator(title = 'HTF Candle Boxes for LTF Charts', shorttitle = "HTF Candles", overlay = true, max_boxes_count = 200)
//v1 24/04/22, 25/04/22, 02/05/22 [Published]
//v2 10/06/22, 22/09/22 // Updated the last candle painting error, a bit of a code cleanup too.  Still a glitch if the chart TF >= HTF, but that's a nonesense situation
//v3 23/07/23 // added the pip range label


// User Inputs
Interval = input.timeframe('D', 'Box Time Interval')
BodyCandle = input.bool(true, 'Box the Candle')
BodyCandleBox = input.bool(true, 'Box the Candle Body')
BoxCurrentCandle = input.bool(true, 'Box the Current Candle') //v2
BoxRangePips = input.bool(true, 'Show H/L Range Pips', inline = '1')  //v2
BoxRangePercentage = input.bool(false, 'Show O/C Pips', inline = '1')  //v3

// Colour Selection
BgLong = input.color(color.new(#66bb6a,80), 'Long Boxes') 
BgShort = input.color(color.new(#f7525f,80), 'Short Boxes')
BordCol = input.color(color.new(#9598a1,50), 'Border Color')

// interval valiables
var float htfHigh = na // htf Candle High
var float htfLow = na
var float htfOpen = na 
var int htfBarIndex = na  // Bar Index for start of HTF candle
var float div = 1

var box currentBox1 = na  // need a place holder boxes
var box currentBox2 = na  // need a place holder boxes
var label currentLabel = na

if bar_index == 1
    //First Bar Setup
    if syminfo.type == 'forex' 
        div := 0.1 / syminfo.mintick 
    
changePeriod = ta.change(time(Interval))
candleLong = close > htfOpen
boxCol = candleLong ? BgLong : BgShort   // colour for direction  (e.g. Red or Green box)

r1 = math.abs(htfHigh - htfLow) * div
r2 = math.abs(htfOpen - close[1]) * div
r3 = r2 / r1

//lableText = BoxRangePips ? str.tostring(math.abs(htfHigh - htfLow) * div, '#,##0.0#') : ""
lableText = str.tostring(r1, '#,##0.0#')
if BoxRangePercentage
    lableText := str.tostring(r2, '#,##0.0#') + ' / ' + str.tostring(r1, '#,##0.0#') + ' = ' + str.tostring(r3, '0%')

// this was a bug in the last version when working in a live chart.  Need to use temp boxes
if barstate.islast and BoxCurrentCandle // now on the last bar
    box.delete(currentBox1)
    box.delete(currentBox2)
    label.delete(currentLabel)

    if BodyCandle
        currentBox1 := box.new(left = htfBarIndex, top = htfHigh, right = bar_index, bottom = htfLow, border_width = 1, border_style = line.style_solid, border_color = BordCol, bgcolor = BodyCandleBox ? na : boxCol)
        if BodyCandleBox 
            if candleLong
                currentBox2 := box.new(left = htfBarIndex, top = close, right = bar_index, bottom = htfOpen, border_width = 1, border_style = line.style_solid, border_color = BordCol, bgcolor = boxCol)
            else
                currentBox2 := box.new(left = htfBarIndex, top = htfOpen, right = bar_index, bottom = close, border_width = 1, border_style = line.style_solid, border_color = BordCol, bgcolor = boxCol)

    if BoxRangePips
        currentLabel := label.new(htfBarIndex + (bar_index - htfBarIndex) / 2 , htfHigh + syminfo.mintick * 30, lableText, style = label.style_none, textcolor = color.black, textalign = text.align_right) 


if changePeriod // new interval has occured

    if BodyCandle
        //paint outside box HTF High to HTF Low
        box.new(left = htfBarIndex, top = htfHigh, right = bar_index - 1, bottom = htfLow, border_width = 1, border_style = line.style_solid, border_color = BordCol, bgcolor = BodyCandleBox ? na : boxCol) //, text = lableText, text_valign = text.align_top, text_size = size.small)
    
        // paint Body HTF Open to last bar Close    
        if BodyCandleBox 
            if candleLong
                box.new(left = htfBarIndex, top = close[1], right = bar_index - 1, bottom = htfOpen, border_width = 1, border_style = line.style_solid, border_color = BordCol, bgcolor = boxCol)
            else
                box.new(left = htfBarIndex, top = htfOpen, right = bar_index - 1, bottom = close[1], border_width = 1, border_style = line.style_solid, border_color = BordCol, bgcolor = boxCol)

    if BoxRangePips
        label.new(htfBarIndex + (bar_index - htfBarIndex) / 2 , htfHigh + syminfo.mintick * 30, lableText, style = label.style_none, textcolor = color.black, textalign = text.align_right) 


    //store next data points
    htfOpen := open    
    htfBarIndex := bar_index
    htfHigh := high
    htfLow := low

else
    // increment data points for HH or LL
    if high > htfHigh
        htfHigh := high
    if low < htfLow
        htfLow := low

// end