Rolling Quartiles
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © DojiEmoji

//@version=5
indicator("Quartiles with Box Plot [KL]", overlay=true)

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Settings
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
float src                   = input.source(close, title="Source data")
var int n                   = input.int(20, title="Lookback", minval=1)
var int offset              = input.int(5, minval=5, maxval=150, title="Offset: Box Plot", step=5, tooltip="Relative to recenrt bar (right hand side)")

var string GROUP_1          = "Box plot"
var color color1            = input.color(color.blue, title="Box plot", group=GROUP_1)
var int ln_width            = input.int(2, title="Width", minval=1, maxval=4, group=GROUP_1)
var bool mod_boxplot        = input.bool(false, title="Use modified boxplot (Whiskers = 1.5x IQR)")
var bool show_hline_iqr     = input.bool(true, title="Show lines for rolling IQR", group=GROUP_1)
var bool show_hline_maxmin  = input.bool(false, title="Show lines for rolling Min & Max", group=GROUP_1)

var string GROUP_2          = "Historical IQR - Moving lines"
var string _tt              = "Go to 'Style' tab to modify color/width"
var bool show_bands_iqr     = input.bool(false, title="Show moving IQR", group=GROUP_2, tooltip=_tt)
var bool show_bands_mm      = input.bool(false, title="Show moving Min & Max", group=GROUP_2, tooltip=_tt)

var string GROUP_3          = "Trend Indicator"
var bool use_barcol         = input.bool(false, title="Modify bar colors according to trend:", tooltip="Based on whether price is above/within/below IQR.", group=GROUP_3)
var color color2_up         = input.color(color.blue, title="Uptrend", group=GROUP_3)
var color color3_neutral    = input.color(color.gray, title="Neutral", group=GROUP_3)
var color color4_dn         = input.color(color.red, title="Downtrend", group=GROUP_3)

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Quartiles
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

float Q1 = ta.percentile_linear_interpolation(src, n, 25)
float Q2 = ta.percentile_linear_interpolation(src, n, 50)
float Q3 = ta.percentile_linear_interpolation(src, n, 75)

float iqr_mod = (Q3 - Q1) * 1.5
float Q0 = mod_boxplot ? Q1 - iqr_mod : ta.percentile_linear_interpolation(src, n, 0)
float Q4 = mod_boxplot ? Q3 + iqr_mod : ta.percentile_linear_interpolation(src, n, 100)

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Drawing Box Plot - to represent the quartiles
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

// Horizontal lines:
var line hline_q0 = na      //         ---      <- hline_q0
var line hline_q1 = na      //          |
var line hline_q2 = na      //          |
var line hline_q3 = na      //       -------    <- hline_q1
var line hline_q4 = na      //      |       | 
                            //      |       | 
// Box plot:                //      |-------|   <- hline_q2
var box box_iqr = na        //      |       | 
var line ln_q0 = na         //       -------    <- hline_q3
var line ln_q0q1 = na       //          |
var line ln_q2 = na         //          |
var line ln_q4 = na         //         ---      <- hline_q4
var line ln_q3q4 = na


// Helper functions: 
// {
var line[] dump_lns = array.new_line()

// Lines drawn with draw_line() will be temporaily stored in an array of line ("AOL")
draw_line(offset_x1, y1, offset_x2, y2, ln_color, ln_width, ln_style=line.style_solid) =>
    ln = line.new(bar_index+offset_x1, y1, bar_index+offset_x2, y2, xloc.bar_index, color=ln_color, width=ln_width, style=ln_style)
    array.push(dump_lns, ln)
    ln
// All lines in AOL will get deleted in the next bar-iteration through del_lines()
del_lines() =>
    while array.size(dump_lns) > 0
        line.delete(array.pop(dump_lns))   
// }

// Box and whisker
del_lines()
ln_q0   := draw_line(offset-1, Q0, offset+1, Q0, color1, ln_width)
ln_q0q1 := draw_line(offset+0, Q1, offset+0, Q0, color1, ln_width)
ln_q2   := draw_line(offset-2, Q2, offset+2, Q2, color1, ln_width)
ln_q3q4 := draw_line(offset+0, Q3, offset+0, Q4, color1, ln_width)
ln_q4   := draw_line(offset-1, Q4, offset+1, Q4, color1, ln_width)

box.delete(box_iqr[1])
box_iqr := box.new(bar_index+offset-2, Q3, bar_index+offset+2, Q1,
 border_color=color1, bgcolor=na, border_width=ln_width)

// Rolling quartiles
if show_hline_iqr
    hline_q1 := draw_line(-n, Q1, offset-4, Q1, color1, 1, line.style_dotted)
    hline_q2 := draw_line(-n, Q2, offset-4, Q2, color1, 1, line.style_dotted)
    hline_q3 := draw_line(-n, Q3, offset-4, Q3, color1, 1, line.style_dotted)
if show_hline_maxmin
    hline_q0 := draw_line(-n, Q0, offset-4, Q0, color1, 1, line.style_dotted)
    hline_q4 := draw_line(-n, Q4, offset-4, Q4, color1, 1, line.style_dotted)

// Moving quartiles
p1 = plot(Q1, color=show_bands_iqr ? color1 : na, title="Q1 Lower band")
plot(Q2, color=show_bands_iqr ? color1 : na, title="Q2 Center line")
p2 = plot(Q3, color=show_bands_iqr ? color1 : na, title="Q3 Upper band")
fill(p1, p2, color=color.new(color.gray,100), title="Moving IQR")

plot(Q0, color=show_bands_mm ? color1 : na, title="Q0 Min band")
plot(Q4, color=show_bands_mm ? color1 : na, title="Q4 Max band")

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Bar Color (Trend Indicator)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
barcolor(use_barcol and close > Q3 ? color2_up : na, editable=false)
barcolor(use_barcol and close < Q3 and close > Q1 ? color3_neutral : na, editable=false)
barcolor(use_barcol and close < Q1 ? color4_dn : na, editable=false)
