// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © midnight pickle
//
// ============================================================================
// Pivots MP
// Pivot Enhanced (Fibonacci / Camarilla, R1-R5 / S1-S5 + alerts)
//
// Split out of the "Pivots + Key Levels" master script. Logic is unchanged;
// the Key Levels half now lives in its own indicator, so this script has the
// full 500-line / 500-label object budget to itself. A very large "Lookback
// Period" combined with all levels enabled can still hit the cap and drop
// oldest objects.
// ============================================================================
//@version=6
indicator('Pivots MP', shorttitle='Pivots MP', overlay=true, max_lines_count=500, max_labels_count=500, max_bars_back=5000)
// ============================================================================
// ██ PIVOTS ██
// ============================================================================
// Timeframe Options
AUTO = 'Auto'
HOURLY = 'Hourly'
DAILY = 'Daily'
WEEKLY = 'Weekly'
MONTHLY = 'Monthly'
QUARTERLY = 'Quarterly'
YEARLY = 'Yearly'
BIYEARLY = 'Biyearly'
TRIYEARLY = 'Triyearly'
QUINQUENNIAL = 'Quinquennial'
// Pivot Calculation Methods
FIBONACCI = 'Fibonacci'
CAMARILLA = 'Camarilla'
// Pivot input groups
grp_piv = '⭐ Pivots · Settings'
grp_piv_vis = '⭐ Pivots · Visuals'
grp_piv_lvl = '⭐ Pivots · Show/Hide Levels'
grp_piv_col = '⭐ Pivots · Colors'
grp_piv_alert = '⭐ Pivots · Alerts'
// Master enable for the whole pivot system
enable_pivots = input.bool(true, '🔥 Master Toggle: Pivots', group=grp_piv)
// Inputs
kind = input.string(title = 'Calculation Method', defval = 'Camarilla', options = [FIBONACCI, CAMARILLA], group = grp_piv)
pivot_time_frame = input.string(title = 'Timeframe', defval = DAILY, options = [AUTO, HOURLY, DAILY, WEEKLY, MONTHLY, QUARTERLY, YEARLY, BIYEARLY, TRIYEARLY, QUINQUENNIAL], group = grp_piv)
look_back = input.int(title = 'Lookback Period', defval = 1, minval = 1, maxval = 5000, tooltip = 'How many past periods of pivots to keep drawn. Note: total drawn objects (segments x enabled levels) share the 500 line / 500 label cap for this indicator.', group = grp_piv)
is_daily_based = input.bool(title = 'Use Daily-based Values', defval = true, tooltip = 'Use daily OHLC values even on intraday charts', group = grp_piv)
show_labels = input.bool(title = 'Show Labels', defval = true, group = grp_piv_vis)
position_labels = input.string('Left', 'Label Position', options = ['Left', 'Right'], group = grp_piv_vis)
// Show/Hide Levels
show_p = input.bool(title = 'Show Pivot (P)', defval = true, group = grp_piv_lvl)
show_r1 = input.bool(title = 'Show R1/S1', defval = true, group = grp_piv_lvl)
show_r2 = input.bool(title = 'Show R2/S2', defval = true, group = grp_piv_lvl)
show_r3 = input.bool(title = 'Show R3/S3', defval = true, group = grp_piv_lvl, tooltip = 'Carries the H4/L4 value from the calculation. In published Camarilla material H4/L4 is the breakout level, so this line is not the "far extreme, rarely reached" that R3 means in standard pivot notation.')
show_r4 = input.bool(title = 'Show R4/S4', defval = true, group = grp_piv_lvl, tooltip = 'Carries the H5/L5 value from the calculation.')
show_r5 = input.bool(title = 'Show R5/S5', defval = true, group = grp_piv_lvl, tooltip = 'Carries the H6/L6 value from the calculation. TradingView\'s built-in Camarilla plots this same value at its R5, but the levels in between will not line up between the two indicators.')
// Color Settings
p_color = input.color(color.orange, 'Pivot Color', group = grp_piv_col)
s1r1_color = input.color(color.blue, 'S1/R1 Color', group = grp_piv_col)
s2r2_color = input.color(color.red, 'S2/R2 Color', group = grp_piv_col)
s3r3_color = input.color(color.purple, 'S3/R3 Color', group = grp_piv_col)
s4r4_color = input.color(color.green, 'S4/R4 Color', group = grp_piv_col)
s5r5_color = input.color(color.navy, 'S5/R5 Color', group = grp_piv_col)
// Alert Settings
use_close = input.bool(true, 'Use Close Price for Alerts', tooltip = 'When enabled, alerts trigger when the close CROSSES the level. When disabled, alerts trigger on price touch (bar range straddles the level).', group = grp_piv_alert)
// Arrays for storing pivot data
var arr_time = array.new_int()
var p = array.new_float()
var r1 = array.new_float()
var s1 = array.new_float()
var r2 = array.new_float()
var s2 = array.new_float()
var r3 = array.new_float()
var s3 = array.new_float()
var r4 = array.new_float()
var s4 = array.new_float()
var r5 = array.new_float()
var s5 = array.new_float()
// Arrays for storing line and label IDs for cleanup
var line_ids_p = array.new_line()
var line_ids_r1 = array.new_line()
var line_ids_s1 = array.new_line()
var line_ids_r2 = array.new_line()
var line_ids_s2 = array.new_line()
var line_ids_r3 = array.new_line()
var line_ids_s3 = array.new_line()
var line_ids_r4 = array.new_line()
var line_ids_s4 = array.new_line()
var line_ids_r5 = array.new_line()
var line_ids_s5 = array.new_line()
var label_ids_p = array.new_label()
var label_ids_r1 = array.new_label()
var label_ids_s1 = array.new_label()
var label_ids_r2 = array.new_label()
var label_ids_s2 = array.new_label()
var label_ids_r3 = array.new_label()
var label_ids_s3 = array.new_label()
var label_ids_r4 = array.new_label()
var label_ids_s4 = array.new_label()
var label_ids_r5 = array.new_label()
var label_ids_s5 = array.new_label()
// Variables for OHLC data
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 the resolution for pivot calculation
get_pivot_resolution() =>
resolution = 'M'
if pivot_time_frame == AUTO
if timeframe.isintraday
resolution := timeframe.multiplier <= 15 ? 'D' : 'W'
resolution
else if timeframe.isweekly or timeframe.ismonthly
resolution := '12M'
resolution
else if pivot_time_frame == HOURLY
resolution := '60' // "Hourly" maps to 60 minutes
resolution
else if pivot_time_frame == DAILY
resolution := 'D'
resolution
else if pivot_time_frame == WEEKLY
resolution := 'W'
resolution
else if pivot_time_frame == MONTHLY
resolution := 'M'
resolution
else if pivot_time_frame == QUARTERLY
resolution := '3M'
resolution
else if pivot_time_frame == YEARLY or pivot_time_frame == BIYEARLY or pivot_time_frame == TRIYEARLY or pivot_time_frame == QUINQUENNIAL
resolution := '12M'
resolution
resolution
// Drawing functions with ID storage
draw_line(i, pivot, col, line_array) =>
if array.size(arr_time) > 1
new_line = line.new(array.get(arr_time, i), array.get(pivot, i), array.get(arr_time, i + 1), array.get(pivot, i), color = col, xloc = xloc.bar_time)
array.push(line_array, new_line)
draw_label(i, y, txt, txt_color, label_array) =>
if show_labels
offset = ' '
labels_align_str_left = position_labels == 'Left' ? txt + offset : offset + txt
x = position_labels == 'Left' ? array.get(arr_time, i) : array.get(arr_time, i + 1)
new_label = label.new(x = x, y = y, text = labels_align_str_left, textcolor = txt_color, style = label.style_label_center, color = #00000000, xloc = xloc.bar_time)
array.push(label_array, new_label)
// Helpers to fully clear stored drawing objects before each full redraw,
// preventing duplicate/stale line & label accumulation across realtime rollovers.
delete_lines(arr) =>
if array.size(arr) > 0
for ln in arr
line.delete(ln)
array.clear(arr)
delete_labels(arr) =>
if array.size(arr) > 0
for lb in arr
label.delete(lb)
array.clear(arr)
clear_all_drawings() =>
delete_lines(line_ids_p), delete_labels(label_ids_p)
delete_lines(line_ids_r1), delete_labels(label_ids_r1)
delete_lines(line_ids_s1), delete_labels(label_ids_s1)
delete_lines(line_ids_r2), delete_labels(label_ids_r2)
delete_lines(line_ids_s2), delete_labels(label_ids_s2)
delete_lines(line_ids_r3), delete_labels(label_ids_r3)
delete_lines(line_ids_s3), delete_labels(label_ids_s3)
delete_lines(line_ids_r4), delete_labels(label_ids_r4)
delete_lines(line_ids_s4), delete_labels(label_ids_s4)
delete_lines(line_ids_r5), delete_labels(label_ids_r5)
delete_lines(line_ids_s5), delete_labels(label_ids_s5)
// Pivot calculation methods
fibonacci() =>
pivotX_Median = (pivotX_prev_high + pivotX_prev_low + pivotX_prev_close) / 3
pivot_range = pivotX_prev_high - pivotX_prev_low
array.push(p, pivotX_Median)
array.push(r1, pivotX_Median + 0.382 * pivot_range)
array.push(s1, pivotX_Median - 0.382 * pivot_range)
array.push(r2, pivotX_Median + 0.618 * pivot_range)
array.push(s2, pivotX_Median - 0.618 * pivot_range)
array.push(r3, pivotX_Median + 1 * pivot_range)
array.push(s3, pivotX_Median - 1 * pivot_range)
array.push(r4, pivotX_Median + 1.272 * pivot_range)
array.push(s4, pivotX_Median - 1.272 * pivot_range)
array.push(r5, pivotX_Median + 1.618 * pivot_range)
array.push(s5, pivotX_Median - 1.618 * pivot_range)
// NOTE: Camarilla math left exactly as provided (per request). This is a
// deliberate custom variant, NOT the textbook Camarilla equation. Do not
// "correct" it against published formulas.
//
// LEVEL NAMING — read this before cross-referencing any Camarilla material.
// The internal H/L values are stored into the R/S arrays with a deliberate
// one-step shift, and H1/L1 are not plotted at all:
//
// plotted as ... R1 R2 R3 R4 R5
// comes from ... H2 H3 H4 H5 H6
// plotted as ... S1 S2 S3 S4 S5
// comes from ... L2 L3 L4 L5 L6
//
// Consequences worth knowing:
// * This script's R3/S3 carries the H4/L4 value. In published Camarilla
// material H4/L4 is the breakout level, so R3 here is NOT the "far
// extreme, rarely reached" that R3 means in standard pivot notation.
// * TradingView's built-in Pivot Points Standard → Camarilla plots only
// five levels and puts (prevHigh / prevLow) × prevClose at its R5. That
// value lives at this script's R5 too, but the levels in between will not
// line up, so the two indicators are not directly comparable.
camarilla() =>
pivotX_Median = (pivotX_prev_high + pivotX_prev_low + pivotX_prev_close) / 3
pivot_range = pivotX_prev_high - pivotX_prev_low
H4 = pivotX_prev_close + pivot_range * 1.1 / 2
H3 = pivotX_prev_close + pivot_range * 1.1 / 4
H2 = pivotX_prev_close + pivot_range * 1.1 / 6
H1 = pivotX_prev_close + pivot_range * 1.1 / 12
L1 = pivotX_prev_close - pivot_range * 1.1 / 12
L2 = pivotX_prev_close - pivot_range * 1.1 / 6
L3 = pivotX_prev_close - pivot_range * 1.1 / 4
L4 = pivotX_prev_close - pivot_range * 1.1 / 2
L5 = L4 - 1.168 * (L3 - L4)
H5 = H4 + 1.168 * (H4 - H3)
H6 = pivotX_prev_high / pivotX_prev_low * pivotX_prev_close
L6 = pivotX_prev_close - (H6 - pivotX_prev_close)
array.push(p, pivotX_Median)
array.push(r1, H2)
array.push(s1, L2)
array.push(r2, H3)
array.push(s2, L3)
array.push(r3, H4)
array.push(s3, L4)
array.push(r4, H5)
array.push(s4, L5)
array.push(r5, H6)
array.push(s5, L6)
// Main logic
resolution = get_pivot_resolution()
[sec_open, sec_high, sec_low, sec_close] = request.security(syminfo.tickerid, resolution, [open, high, low, close], lookahead = barmerge.lookahead_on)
sec_open_gaps_on = request.security(syminfo.tickerid, resolution, open, gaps = barmerge.gaps_on, lookahead = barmerge.lookahead_on)
// Fixed custom years resolution handling
var int year_counter = 0
var bool is_change_years = false
var bool custom_years_resolution = pivot_time_frame == BIYEARLY or pivot_time_frame == TRIYEARLY or pivot_time_frame == QUINQUENNIAL
if custom_years_resolution and bool(ta.change(time(resolution)))
// Increment counter on each timeframe change
year_counter := year_counter + 1
// Check if we've reached the target multiple
if pivot_time_frame == BIYEARLY and year_counter % 2 == 0
is_change_years := true
is_change_years
else if pivot_time_frame == TRIYEARLY and year_counter % 3 == 0
is_change_years := true
is_change_years
else if pivot_time_frame == QUINQUENNIAL and year_counter % 5 == 0
is_change_years := true
is_change_years
else
is_change_years := false
is_change_years
else
is_change_years := false
is_change_years
var is_change = false
var uses_current_bar = timeframe.isintraday
var change_time = int(na)
is_time_change = bool(ta.change(time(resolution))) and not custom_years_resolution or is_change_years
if is_time_change
change_time := time
change_time
if not uses_current_bar and is_time_change or uses_current_bar and not na(sec_open_gaps_on)
if is_daily_based
pivotX_prev_open := sec_open[1]
pivotX_prev_high := sec_high[1]
pivotX_prev_low := sec_low[1]
pivotX_prev_close := sec_close[1]
pivotX_open := sec_open
pivotX_high := sec_high
pivotX_low := sec_low
pivotX_low
else
pivotX_prev_high := pivotX_high
pivotX_prev_low := pivotX_low
pivotX_prev_open := pivotX_open
pivotX_open := open
pivotX_high := high
pivotX_low := low
pivotX_prev_close := close[1]
pivotX_prev_close
if barstate.islast and not is_change and array.size(arr_time) > 0
array.set(arr_time, array.size(arr_time) - 1, change_time)
else
array.push(arr_time, change_time)
if kind == FIBONACCI
fibonacci()
else if kind == CAMARILLA
camarilla()
// Cap stored data arrays to the lookback window.
if array.size(arr_time) > look_back
if array.size(arr_time) > 0
array.shift(arr_time)
// Shift data arrays
if array.size(p) > 0
array.shift(p)
if array.size(r1) > 0
array.shift(r1)
if array.size(s1) > 0
array.shift(s1)
if array.size(r2) > 0
array.shift(r2)
if array.size(s2) > 0
array.shift(s2)
if array.size(r3) > 0
array.shift(r3)
if array.size(s3) > 0
array.shift(s3)
if array.size(r4) > 0
array.shift(r4)
if array.size(s4) > 0
array.shift(s4)
if array.size(r5) > 0
array.shift(r5)
if array.size(s5) > 0
array.shift(s5)
is_change := true
is_change
else
if is_daily_based
pivotX_high := math.max(pivotX_high, sec_high)
pivotX_low := math.min(pivotX_low, sec_low)
pivotX_low
else
pivotX_high := math.max(pivotX_high, high)
pivotX_low := math.min(pivotX_low, low)
pivotX_low
// Get current pivot values for alerts
get_current_pivot_value(arr) =>
array.size(arr) > 0 ? array.get(arr, array.size(arr) - 1) : na
p_val = get_current_pivot_value(p)
r1_val = get_current_pivot_value(r1)
s1_val = get_current_pivot_value(s1)
r2_val = get_current_pivot_value(r2)
s2_val = get_current_pivot_value(s2)
r3_val = get_current_pivot_value(r3)
s3_val = get_current_pivot_value(s3)
r4_val = get_current_pivot_value(r4)
s4_val = get_current_pivot_value(s4)
r5_val = get_current_pivot_value(r5)
s5_val = get_current_pivot_value(s5)
// Precompute crosses so "Use Close" alerts actually fire.
p_cross = ta.cross(close, p_val)
r1_cross = ta.cross(close, r1_val)
s1_cross = ta.cross(close, s1_val)
r2_cross = ta.cross(close, r2_val)
s2_cross = ta.cross(close, s2_val)
r3_cross = ta.cross(close, r3_val)
s3_cross = ta.cross(close, s3_val)
r4_cross = ta.cross(close, r4_val)
s4_cross = ta.cross(close, s4_val)
r5_cross = ta.cross(close, r5_val)
s5_cross = ta.cross(close, s5_val)
// Alert Conditions (gated by the pivot master toggle)
// use_close = true -> close crosses the level
// use_close = false -> bar range straddles the level (touch)
r1_touch = enable_pivots and not na(r1_val) and (use_close ? r1_cross : (low <= r1_val and high >= r1_val))
s1_touch = enable_pivots and not na(s1_val) and (use_close ? s1_cross : (low <= s1_val and high >= s1_val))
r2_touch = enable_pivots and not na(r2_val) and (use_close ? r2_cross : (low <= r2_val and high >= r2_val))
s2_touch = enable_pivots and not na(s2_val) and (use_close ? s2_cross : (low <= s2_val and high >= s2_val))
r3_touch = enable_pivots and not na(r3_val) and (use_close ? r3_cross : (low <= r3_val and high >= r3_val))
s3_touch = enable_pivots and not na(s3_val) and (use_close ? s3_cross : (low <= s3_val and high >= s3_val))
r4_touch = enable_pivots and not na(r4_val) and (use_close ? r4_cross : (low <= r4_val and high >= r4_val))
s4_touch = enable_pivots and not na(s4_val) and (use_close ? s4_cross : (low <= s4_val and high >= s4_val))
r5_touch = enable_pivots and not na(r5_val) and (use_close ? r5_cross : (low <= r5_val and high >= r5_val))
s5_touch = enable_pivots and not na(s5_val) and (use_close ? s5_cross : (low <= s5_val and high >= s5_val))
p_touch = enable_pivots and not na(p_val) and (use_close ? p_cross : (low <= p_val and high >= p_val))
// Create alertcondition entries
alertcondition(r1_touch, title = 'R1 Touched', message = 'R1 Pivot Touched')
alertcondition(s1_touch, title = 'S1 Touched', message = 'S1 Pivot Touched')
alertcondition(r2_touch, title = 'R2 Touched', message = 'R2 Pivot Touched')
alertcondition(s2_touch, title = 'S2 Touched', message = 'S2 Pivot Touched')
alertcondition(r3_touch, title = 'R3 (Key Resistance) Touched', message = 'R3 STRONG Resistance Touched')
alertcondition(s3_touch, title = 'S3 (Key Support) Touched', message = 'S3 STRONG Support Touched')
alertcondition(r4_touch, title = 'R4 (Extreme) Touched', message = 'R4 Extreme Resistance Touched')
alertcondition(s4_touch, title = 'S4 (Extreme) Touched', message = 'S4 Extreme Support Touched')
alertcondition(r5_touch, title = 'R5 (ULTRA) Touched', message = 'R5 ULTRA Resistance Touched')
alertcondition(s5_touch, title = 'S5 (ULTRA) Touched', message = 'S5 ULTRA Support Touched')
alertcondition(p_touch, title = 'Pivot Touched', message = 'Pivot Point Touched')
// Drawing the pivot levels
if enable_pivots and barstate.islast and array.size(arr_time) > 0 and is_change
is_change := false
// Clear every previously drawn object before redrawing the full set.
clear_all_drawings()
if array.size(arr_time) > 2 and custom_years_resolution
last_pivot_time = array.get(arr_time, array.size(arr_time) - 1)
prev_pivot_time = array.get(arr_time, array.size(arr_time) - 2)
estimate_pivot_time = last_pivot_time - prev_pivot_time
array.push(arr_time, last_pivot_time + estimate_pivot_time)
else
array.push(arr_time, time_close(resolution))
for i = 0 to array.size(arr_time) - 2 by 1
if array.size(p) > 0 and show_p
draw_line(i, p, p_color, line_ids_p)
draw_label(i, array.get(p, i), 'P', p_color, label_ids_p)
if array.size(r1) > 0 and show_r1
draw_line(i, r1, s1r1_color, line_ids_r1)
draw_label(i, array.get(r1, i), 'R1', s1r1_color, label_ids_r1)
if array.size(s1) > 0 and show_r1
draw_line(i, s1, s1r1_color, line_ids_s1)
draw_label(i, array.get(s1, i), 'S1', s1r1_color, label_ids_s1)
if array.size(r2) > 0 and show_r2
draw_line(i, r2, s2r2_color, line_ids_r2)
draw_label(i, array.get(r2, i), 'R2', s2r2_color, label_ids_r2)
if array.size(s2) > 0 and show_r2
draw_line(i, s2, s2r2_color, line_ids_s2)
draw_label(i, array.get(s2, i), 'S2', s2r2_color, label_ids_s2)
if array.size(r3) > 0 and show_r3
draw_line(i, r3, s3r3_color, line_ids_r3)
draw_label(i, array.get(r3, i), 'R3', s3r3_color, label_ids_r3)
if array.size(s3) > 0 and show_r3
draw_line(i, s3, s3r3_color, line_ids_s3)
draw_label(i, array.get(s3, i), 'S3', s3r3_color, label_ids_s3)
if array.size(r4) > 0 and show_r4
draw_line(i, r4, s4r4_color, line_ids_r4)
draw_label(i, array.get(r4, i), 'R4', s4r4_color, label_ids_r4)
if array.size(s4) > 0 and show_r4
draw_line(i, s4, s4r4_color, line_ids_s4)
draw_label(i, array.get(s4, i), 'S4', s4r4_color, label_ids_s4)
if array.size(r5) > 0 and show_r5
draw_line(i, r5, s5r5_color, line_ids_r5)
draw_label(i, array.get(r5, i), 'R5', s5r5_color, label_ids_r5)
if array.size(s5) > 0 and show_r5
draw_line(i, s5, s5r5_color, line_ids_s5)
draw_label(i, array.get(s5, i), 'S5', s5r5_color, label_ids_s5)
Show all 484 lines