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

//@version=5
indicator("CPR",overlay=true)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////Central Pivot Range/////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////

AUTO = "Auto"
DAILY = "Daily"
WEEKLY = "Weekly"
MONTHLY = "Monthly"
QUARTERLY = "Quarterly"
YEARLY = "Yearly"
BIYEARLY = "Biyearly"
TRIYEARLY = "Triyearly"
QUINQUENNIALLY = "Quinquennially"
DECENNIALLY = "Decennially"

TRADITIONAL = "Traditional"
FIBONACCI = "Fibonacci"
WOODIE = "Woodie"
CLASSIC = "Classic"
DEMARK = "DM"
CAMARILLA = "Camarilla"

kind = input.string(title="Type", defval="Traditional", options=[TRADITIONAL, FIBONACCI, WOODIE, CLASSIC, DEMARK, CAMARILLA])
pivot_time_frame = input.string(title="Pivots Timeframe", defval=AUTO, options=[AUTO, DAILY, WEEKLY, MONTHLY, QUARTERLY, YEARLY, BIYEARLY, TRIYEARLY, QUINQUENNIALLY, DECENNIALLY])
look_back = input.int(title="Number of Pivots Back", defval=15, minval=1, maxval=5000)
is_daily_based = input.bool(title="Use Daily-based Values", defval=true, tooltip="When this option is unchecked, Pivot Points will use intraday data while calculating on intraday charts. If Extended Hours are displayed on the chart, they will be taken into account during the pivot level calculation. If intraday OHLC values are different from daily-based values (normal for stocks), the pivot levels will also differ.")
show_labels = input.bool(title="Show Labels", defval=false, group="labels")
show_prices = input.bool(title="Show Prices", defval=false, group="labels")
position_labels = input.string("Left", "Labels Position", options=["Left", "Right"], group="labels")
line_width = input.int(title="Line Width", defval=1, minval=1, maxval=100, group="levels")

var DEF_COLOR = #FB8C00
var arr_time = array.new_int()
var p = array.new_float()
var bc = array.new_float()
var tc = array.new_float()
p_color = input.color(color.white, "P‏  ‏  ‏", inline="P", group="levels")
p_show = input.bool(true, "", inline="P", group="levels")
var r1 = array.new_float()
var s1 = array.new_float()
s1_color = input.color(DEF_COLOR, "S1", inline="S1/R1" , group="levels")
s1_show = input.bool(true, "", inline="S1/R1", group="levels")
r1_color = input.color(DEF_COLOR, "‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏R1", inline="S1/R1", group="levels")
r1_show = input.bool(true, "", inline="S1/R1", group="levels")
var r2 = array.new_float()
var s2 = array.new_float()
s2_color = input.color(DEF_COLOR, "S2", inline="S2/R2", group="levels")
s2_show = input.bool(true, "", inline="S2/R2", group="levels")
r2_color = input.color(DEF_COLOR, "‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏R2", inline="S2/R2", group="levels")
r2_show = input.bool(true, "", inline="S2/R2", group="levels")
var r3 = array.new_float()
var s3 = array.new_float()
s3_color = input.color(DEF_COLOR, "S3", inline="S3/R3", group="levels")
s3_show = input.bool(true, "", inline="S3/R3", group="levels")
r3_color = input.color(DEF_COLOR, "‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏R3", inline="S3/R3", group="levels")
r3_show = input.bool(true, "", inline="S3/R3", group="levels")
var r4 = array.new_float()
var s4 = array.new_float()
s4_color = input.color(DEF_COLOR, "S4", inline="S4/R4", group="levels")
s4_show = input.bool(false, "", inline="S4/R4", group="levels")
r4_color = input.color(DEF_COLOR, "‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏R4", inline="S4/R4", group="levels")
r4_show = input.bool(false, "", inline="S4/R4", group="levels")
var r5 = array.new_float()
var s5 = array.new_float()
s5_color = input.color(DEF_COLOR, "S5", inline="S5/R5", group="levels")
s5_show = input.bool(false, "", inline="S5/R5", group="levels")
r5_color = input.color(DEF_COLOR, "‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏R5", inline="S5/R5", group="levels")
r5_show = input.bool(false, "", inline="S5/R5", group="levels")
pivotX_open = float(na)
pivotX_open := nz(pivotX_open[1], open)
pivotX_high = float(na)
pivotX_high := nz(pivotX_high[1], high)
pivotX_low = float(na)
pivotX_low := nz(pivotX_low[1], low)
pivotX_prev_open = float(na)
pivotX_prev_open := nz(pivotX_prev_open[1])
pivotX_prev_high = float(na)
pivotX_prev_high := nz(pivotX_prev_high[1])
pivotX_prev_low = float(na)
pivotX_prev_low := nz(pivotX_prev_low[1])
pivotX_prev_close = float(na)
pivotX_prev_close := nz(pivotX_prev_close[1])

get_pivot_resolution() =>
    resolution = "M"
    if pivot_time_frame == AUTO
        if timeframe.isintraday
            resolution := timeframe.multiplier <= 30 ? "D" : "W"
        else if timeframe.isweekly or timeframe.ismonthly
            resolution := "12M"
    else if pivot_time_frame == DAILY
        resolution := "D"
    else if pivot_time_frame == WEEKLY
        resolution := "W"
    else if pivot_time_frame == MONTHLY
        resolution := "M"
    else if pivot_time_frame == QUARTERLY
        resolution := "3M"
    else if pivot_time_frame == YEARLY or pivot_time_frame == BIYEARLY or pivot_time_frame == TRIYEARLY or pivot_time_frame == QUINQUENNIALLY or pivot_time_frame == DECENNIALLY
        resolution := "12M"
    resolution

var lines = array.new_line()
var labels = array.new_label()

draw_line(i, pivot, col,transp,sty) =>
    if array.size(arr_time) > 1
        array.push(lines, line.new(array.get(arr_time, i), array.get(pivot, i), array.get(arr_time, i + 1), array.get(pivot, i), color=color.new(col,transp) ,xloc=xloc.bar_time, width=line_width,style=sty))

sol=line.style_solid
das=line.style_dashed


calc_pivot() =>
    pivotX_Median = (pivotX_prev_high + pivotX_prev_low + pivotX_prev_close) / 3
    pivotBC=(pivotX_prev_high+pivotX_prev_low)/2
    array.push(p, pivotX_Median)
    array.push(bc, pivotBC)
    array.push(tc,  pivotX_Median-pivotBC+ pivotX_Median )
    array.push(r1, (pivotX_Median * 2) - pivotX_prev_low)
    array.push(s1, (pivotX_Median * 2) - pivotX_prev_high)
    array.push(r2, pivotX_Median + (1 * (pivotX_prev_high - pivotX_prev_low)))
    array.push(s2, pivotX_Median - (1 * (pivotX_prev_high - pivotX_prev_low)))
    array.push(r3, (pivotX_Median * 2) + (pivotX_prev_high - 2 * pivotX_prev_low))
    array.push(s3, (pivotX_Median * 2) - (2 * pivotX_prev_high - pivotX_prev_low))
    array.push(r4, (pivotX_Median * 3) + (pivotX_prev_high - 3 * pivotX_prev_low))
    array.push(s4, (pivotX_Median * 3) - (3 * pivotX_prev_high - pivotX_prev_low))
    array.push(r5, (pivotX_Median * 4) + (pivotX_prev_high - 4 * pivotX_prev_low))
    array.push(s5, (pivotX_Median * 4) - (4 * pivotX_prev_high - pivotX_prev_low))

//calc_pivot() =>
 //   if kind == TRADITIONAL
//        traditional()



resolution = get_pivot_resolution()

SIMPLE_DIVISOR = -1
custom_years_divisor = switch pivot_time_frame
	BIYEARLY => 2
	TRIYEARLY => 3
	QUINQUENNIALLY => 5
	DECENNIALLY => 10
	=> SIMPLE_DIVISOR

calc_high(prev, curr) =>
    if na(prev) or na(curr)
        nz(prev, nz(curr, na))
    else
        math.max(prev, curr)
    
calc_low(prev, curr) =>
    if not na(prev) and not na(curr)
        math.min(prev, curr)
    else
        nz(prev, nz(curr, na))

calc_OHLC_for_pivot(custom_years_divisor) =>
    if custom_years_divisor == SIMPLE_DIVISOR 
        [open, high, low, open[1], high[1], low[1], close[1], time[1]]
    else
        var prev_sec_open = float(na)
        var prev_sec_high = float(na)
        var prev_sec_low = float(na)
        var prev_sec_close = float(na)
        var prev_sec_time = int(na)
        var curr_sec_open = float(na)
        var curr_sec_high = float(na)
        var curr_sec_low = float(na)
        if year % custom_years_divisor == 0
        	curr_sec_open := open
			curr_sec_high := high
			curr_sec_low := low
            prev_sec_high := high[1]
            prev_sec_low := low[1]
            prev_sec_close := close[1]
            prev_sec_time := time[1]
            for i = 2 to custom_years_divisor
                prev_sec_open :=  nz(open[i], prev_sec_open)
                prev_sec_high := calc_high(prev_sec_high, high[i])
                prev_sec_low := calc_low(prev_sec_low, low[i])
                prev_sec_time := nz(time[i], prev_sec_time)
        [curr_sec_open, curr_sec_high, curr_sec_low, prev_sec_open, prev_sec_high, prev_sec_low, prev_sec_close, prev_sec_time]

[sec_open, sec_high, sec_low, prev_sec_open, prev_sec_high, prev_sec_low, prev_sec_close, prev_sec_time] = request.security(syminfo.tickerid, resolution, calc_OHLC_for_pivot(custom_years_divisor), lookahead = barmerge.lookahead_on)
sec_open_gaps_on = request.security(syminfo.tickerid, resolution, open, gaps = barmerge.gaps_on, lookahead = barmerge.lookahead_on)

is_change_years = custom_years_divisor > 0 and ta.change(time(resolution)) and year % custom_years_divisor == 0

var is_change = false
var uses_current_bar = timeframe.isintraday and kind == WOODIE
var change_time = int(na)
is_time_change = (ta.change(time(resolution)) and custom_years_divisor == SIMPLE_DIVISOR) or is_change_years
if is_time_change
    change_time := time

var start_time = time

without_time_change = barstate.islast and array.size(arr_time) == 0
is_can_calc_pivot = (not uses_current_bar and is_time_change) or (uses_current_bar and not na(sec_open_gaps_on)) or without_time_change
enough_bars_for_calculate = prev_sec_time >= start_time or is_daily_based

if is_can_calc_pivot and enough_bars_for_calculate 
    if array.size(arr_time) == 0 and is_daily_based
        pivotX_prev_open := prev_sec_open[1]
        pivotX_prev_high := prev_sec_high[1]
        pivotX_prev_low := prev_sec_low[1]
        pivotX_prev_close := prev_sec_close[1]
        pivotX_open := sec_open[1]
        pivotX_high := sec_high[1]
        pivotX_low := sec_low[1]
        array.push(arr_time, start_time)
        calc_pivot()
    
    if is_daily_based
        pivotX_prev_open := prev_sec_open
        pivotX_prev_high := prev_sec_high
        pivotX_prev_low := prev_sec_low
        pivotX_prev_close := prev_sec_close
        pivotX_open := sec_open
        pivotX_high := sec_high
        pivotX_low := sec_low
    else
        pivotX_prev_high := pivotX_high
        pivotX_prev_low := pivotX_low
        pivotX_prev_open := pivotX_open
        pivotX_prev_close := close[1]
        pivotX_open := open
        pivotX_high := high
        pivotX_low := low

    if barstate.islast and not is_change and array.size(arr_time) > 0 and not without_time_change
        array.set(arr_time, array.size(arr_time) - 1, change_time)
    else if without_time_change
        array.push(arr_time, start_time)
    else
        array.push(arr_time, nz(change_time, time))

    calc_pivot()

    if array.size(arr_time) > look_back
        if array.size(arr_time) > 0
            array.shift(arr_time)
        if array.size(p) > 0 and p_show
            array.shift(p)
        if array.size(bc) > 0 and p_show
            array.shift(bc)
        if array.size(tc) > 0 and p_show
            array.shift(tc)
        if array.size(r1) > 0 and r1_show
            array.shift(r1)
        if array.size(s1) > 0 and s1_show
            array.shift(s1)
        if array.size(r2) > 0 and r2_show
            array.shift(r2)
        if array.size(s2) > 0 and s2_show
            array.shift(s2)
        if array.size(r3) > 0 and r3_show
            array.shift(r3)
        if array.size(s3) > 0 and s3_show
            array.shift(s3)
        if array.size(r4) > 0 and r4_show
            array.shift(r4)
        if array.size(s4) > 0 and s4_show
            array.shift(s4)
        if array.size(r5) > 0 and r5_show
            array.shift(r5)
        if array.size(s5) > 0 and s5_show
            array.shift(s5)
    is_change := true
else if not is_daily_based
    pivotX_high := math.max(pivotX_high, high)
    pivotX_low := math.min(pivotX_low, low)

if barstate.islast and not is_daily_based and array.size(arr_time) == 0 
    runtime.error("Not enough intraday data to calculate Pivot Points. Lower the Pivots Timeframe or turn on the 'Use Daily-based Values' option in the indicator settings.")

if barstate.islast and array.size(arr_time) > 0 and is_change
    is_change := false
    if custom_years_divisor > 0
        last_pivot_time = array.get(arr_time, array.size(arr_time) - 1)
        pivot_timeframe = str.tostring(12 * custom_years_divisor) + "M"
        estimate_pivot_time = last_pivot_time + timeframe.in_seconds(pivot_timeframe) * 1000
        array.push(arr_time, estimate_pivot_time)
    else
        array.push(arr_time, time_close(resolution))

    for i = 0 to array.size(lines) - 1
        if array.size(lines) > 0
            line.delete(array.shift(lines))
        
    for i = 0 to array.size(arr_time) - 2
        if array.size(p) > 0 and p_show
            draw_line(i, p, p_color,0,sol)
        if array.size(r1) > 0 and r1_show
            draw_line(i, r1, r1_color,40,sol)
        if array.size(s1) > 0 and s1_show
            draw_line(i, s1, s1_color,40,sol)
        if array.size(r2) > 0 and r2_show
            draw_line(i, r2, r2_color,60,sol)
        if array.size(s2) > 0 and s2_show
            draw_line(i, s2, s2_color,60,sol)
        if array.size(r3) > 0 and r3_show
            draw_line(i, r3, r3_color,80,sol)
        if array.size(s3) > 0 and s3_show
            draw_line(i, s3, s3_color,80,sol)
        if array.size(bc) > 0 and p_show
            draw_line(i, bc, p_color,20,das)
        if array.size(tc) > 0 and p_show
            draw_line(i, tc, p_color,20,das)

