Three-Day Rolling Pivot
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © weak_hand

//@version=5
indicator("Three-Day Rolling Pivot", "Rolling Pivot", overlay = true, max_lines_count = 500)

// ----------------------------------------------}
// PREDEFINED ROLLING TYPE 
// ----------------------------------------------{

string type_grp = "--- Rolling Type"
string interval = input.string("D", "Calculation Interval", ["D", "W", "M"], "When changing to 'W' this will result in a weekly rolling pivot. Same goes for 'M' which will result in a monthly rolling pivot.\n\nDefault is 'D' for daily.\nMark Fisher used the default seetings back in the days.", group = type_grp)
int calc_amount = input.int(3, "Length", [2, 3, 4, 5, 6], "This is where you can select the amount of intervals which should take into account.\nFor example, default is 3 and this means a three days/week/month rolling pivot.\n\nThere is no 1 nor 7, cause this would result in daily and weekly Pivot Range.", group = type_grp)
bool speculations = input.bool(false, "Speculations", "When enabled this will calculated unpredictable results and should be avoided. It seems this will offset the lines by one day/week/month to the right.\nI dont know why this option exsist. But I use this sometimes to get less trading signals.", group = type_grp)

// ----------------------------------------------}
// FETCHING HISTORICAL DATA 
// ----------------------------------------------{

[yesterday_open, yesterday_high, yesterday_low, yesterday_close, yesterday_bar_index] = request.security(syminfo.tickerid, interval, [open[1], high[1], low[1], close[1], bar_index[1]], lookahead = speculations ? barmerge.lookahead_off : barmerge.lookahead_on)

// ----------------------------------------------}
// FILL 1st UDT
// ----------------------------------------------{

type candle
    float open
    float high
    float low
    float close
    int bar_index

new_candle = candle.new(yesterday_open, yesterday_high, yesterday_low, yesterday_close, yesterday_bar_index)

var candles = array.new<candle>()

if timeframe.change(interval)
    candles.unshift(new_candle)

// ----------------------------------------------}
// FILL 2nd UDT 
// ----------------------------------------------{

string col_grp    = "--- Color and Shape"
color col_upper   = input.color(color.lime, "Upper Line", group = col_grp)
color col_lower   = input.color(color.maroon, "Lower Line", group = col_grp)
color col_bg      = input.color(color.new(color.black, 80), "Background", group = col_grp)
string line_style = input.string(line.style_dashed, "Style of Lines", [line.style_dashed, line.style_dotted, line.style_solid], display = display.none, group = col_grp)

type rolling_pivot
    float y_upper
    float y_lower
    int x_start
    int x_stop
    string xloc
    line line_upper 
    line line_lower
    linefill line_bg

var rolling_pivots = array.new<rolling_pivot>()

int time_session_open  = time(interval)
int time_session_close = time_close(interval)

if timeframe.change(interval) and candles.size() >= calc_amount and ((interval == "D" and timeframe.isintraday) or interval != "D")
    high_low = array.new<float>()
    for counter = 0 to calc_amount - 1
        high_low.unshift(candles.get(counter).high)
        high_low.unshift(candles.get(counter).low)
    float new_high   = high_low.max()
    float new_low    = high_low.min()
    float pivot      = (new_high + new_low + candles.get(0).close) / 3
    float hl2_3day   = (new_high + new_low) / 2
    float diff       = math.max(pivot, hl2_3day) - math.min(pivot, hl2_3day)
    float upper      = math.round_to_mintick(pivot + diff)
    float lower      = math.round_to_mintick(pivot - diff)
    line line_upper  = line.new(time_session_open, upper, time_session_close, upper, xloc.bar_time, extend.none, col_upper, line_style, 2)
    line line_lower  = line.new(time_session_open, lower, time_session_close, lower, xloc.bar_time, extend.none, col_lower, line_style, 2)
    linefill line_bg = linefill.new(line_upper, line_lower, col_bg)
    rolling_pivots.unshift(rolling_pivot.new(upper, lower, time_session_open, time_session_close, xloc.bar_time, line_upper, line_lower, line_bg))

// ----------------------------------------------}
// OUTPUT 
// ----------------------------------------------{

float latest_upper = rolling_pivots.size() > 0 ? rolling_pivots.get(0).y_upper : na
float latest_lower = rolling_pivots.size() > 0 ? rolling_pivots.get(0).y_lower : na

plot_upper = plot(latest_upper, " High", col_upper, display = display.all - display.pane, editable = false)
plot_lower = plot(latest_lower, " Low", col_lower, display = display.all - display.pane, editable = false)

// ----------------------------------------------}
// TRIM ARRAYS 
// ----------------------------------------------{

int max_rolling_pivots_back = input.int(15, "Max Rolling Pivots Back", 1, 250, group = type_grp)

if rolling_pivots.size() > max_rolling_pivots_back
    old_rolling_pivot = rolling_pivots.pop()
    old_rolling_pivot.line_upper.delete()
    old_rolling_pivot.line_lower.delete()
    old_rolling_pivot.line_bg.delete()

if candles.size() > max_rolling_pivots_back + 2
    candles.pop()

// ----------------------------------------------}
// ALERTS 
// ----------------------------------------------{

string alarm_grp  = "--- Alarm"
string alarm_text = input.text_area("Price touched Three-Day Rolling Pivot", "Display Text", group = alarm_grp)
string alarm_freq = input.string(alert.freq_once_per_bar_close, "Frequency", [alert.freq_all, alert.freq_once_per_bar, alert.freq_once_per_bar_close], display = display.none, group = alarm_grp)

if ta.crossunder(close, latest_upper) or ta.crossover(close, latest_lower)
    alert(alarm_text, alert.freq_once_per_bar)

// ----------------------------------------------}
// DEBUG 
// ----------------------------------------------{

//import weak_hand/Debug/2 as debug
//if candles.size() > 0
    //ebug.label_on_last_bar(str.tostring(candles.get(0).bar_index) + "\n" + str.tostring(bar_index), high)
