OPEN-SOURCE SCRIPT
Grouped Peaks Screener (5/5) — Intraday (04:00 ET reset) — V2

//version=6
indicator("Grouped Peaks Screener (5/5) — Intraday (04:00 ET reset) — screener ready", overlay=true, max_lines_count=500, max_labels_count=0, max_boxes_count=0)
// ---------- user spec constants
LEFT = 5
RIGHT = 5
MS_PER_DAY = 24 * 60 * 60 * 1000
// ---------- typed arrays (Pine v6 best practice)
var int[] grp_ticks = array.new_int()
var int[] grp_counts = array.new_int()
var int[] grp_types = array.new_int() // 1 = high(res), -1 = low(sup)
var int[] grp_start = array.new_int() // bar_index of earliest peak in group
var line[] grp_lines = array.new_line()
var bool[] grp_active = array.new_bool()
// ---------- table (top-right)
max_table_rows = 20
var table infoTbl = table.new(position.top_right, 2, max_table_rows + 1, border_width = 1)
// ---------- helper: safely clear groups and delete drawings
f_clear_groups() =>
int nLines = array.size(grp_lines)
if nLines > 0
for i = 0 to nLines - 1
line ln = array.get(grp_lines, i)
if not na(ln)
line.delete(ln)
array.clear(grp_ticks)
array.clear(grp_counts)
array.clear(grp_types)
array.clear(grp_start)
array.clear(grp_lines)
array.clear(grp_active)
// ---------- trading day detection (premarket start 04:00 ET)
int y = year(time, "America/New_York")
int mo = month(time, "America/New_York")
int d = dayofmonth(time, "America/New_York")
int preStart = timestamp("America/New_York", y, mo, d, 4, 0)
int trading_day_start = time >= preStart ? preStart : preStart - MS_PER_DAY
var int prev_trading_day_start = na
if na(prev_trading_day_start)
prev_trading_day_start := trading_day_start
// reset at new premarket
if trading_day_start != prev_trading_day_start
f_clear_groups()
prev_trading_day_start := trading_day_start
bool allow_detection = time >= trading_day_start
// ---------- pivot detection (5 left / 5 right)
float ph = ta.pivothigh(high, LEFT, RIGHT)
float pl = ta.pivotlow(low, LEFT, RIGHT)
// ---------- process peak (add new or merge into existing same-price group)
f_process_peak(float price, int startBar, int kind) =>
int tick = int(math.round(price / syminfo.mintick))
int found = -1
int sz = array.size(grp_ticks)
if sz > 0
for i = 0 to sz - 1
if array.get(grp_active, i) and array.get(grp_types, i) == kind and array.get(grp_ticks, i) == tick
found := i
break
if found != -1
// merge into existing group
array.set(grp_counts, found, array.get(grp_counts, found) + 1)
if startBar < array.get(grp_start, found)
array.set(grp_start, found, startBar)
line ln = array.get(grp_lines, found)
if not na(ln)
line.set_xy1(ln, startBar, float(array.get(grp_ticks, found)) * syminfo.mintick)
line.set_xy2(ln, startBar + 1, float(array.get(grp_ticks, found)) * syminfo.mintick)
line ln2 = array.get(grp_lines, found)
if not na(ln2)
line.set_color(ln2, color.yellow)
else
color col = kind == 1 ? color.red : color.green
line lnNew = line.new(startBar, price, startBar + 1, price, xloc = xloc.bar_index, extend = extend.right, color = col, width = 2)
array.push(grp_ticks, tick)
array.push(grp_counts, 1)
array.push(grp_types, kind)
array.push(grp_start, startBar)
array.push(grp_lines, lnNew)
array.push(grp_active, true)
// apply pivots (pivot bar is bar_index - RIGHT, pivot confirmed at current bar)
if not na(ph)
int pivotBarIdxH = bar_index - RIGHT
int pivotTimeH = time[RIGHT]
if allow_detection and pivotTimeH >= trading_day_start
f_process_peak(ph, pivotBarIdxH, 1)
if not na(pl)
int pivotBarIdxL = bar_index - RIGHT
int pivotTimeL = time[RIGHT]
if allow_detection and pivotTimeL >= trading_day_start
f_process_peak(pl, pivotBarIdxL, -1)
// ---------- deletion rule: if price crosses level by >=1 tick -> delete group immediately
int nGroups = array.size(grp_ticks)
if nGroups > 0
for i = 0 to nGroups - 1
if array.get(grp_active, i)
int tick = array.get(grp_ticks, i)
int kind = array.get(grp_types, i)
int startB = array.get(grp_start, i)
if bar_index >= startB
if kind == 1
int highTick = int(math.round(high / syminfo.mintick))
if highTick > tick
line ln = array.get(grp_lines, i)
if not na(ln)
line.delete(ln)
array.set(grp_active, i, false)
else
int lowTick = int(math.round(low / syminfo.mintick))
if lowTick < tick
line ln = array.get(grp_lines, i)
if not na(ln)
line.delete(ln)
array.set(grp_active, i, false)
// ---------- Update top-right table "Equal H/L" and "Equal H/L price"
// header
table.cell(infoTbl, 0, 0, "Equal H/L", text_color = color.white, bgcolor = color.new(color.gray, 40))
table.cell(infoTbl, 1, 0, "Equal H/L price", text_color = color.white, bgcolor = color.new(color.gray, 40))
// clear rest of rows to avoid stale values
for r = 1 to max_table_rows
table.cell(infoTbl, 0, r, "", text_color = color.white, bgcolor = color.new(color.black, 100))
table.cell(infoTbl, 1, r, "", text_color = color.white, bgcolor = color.new(color.black, 100))
// populate grouped entries (count >= 2) that are active today
int row = 1
if array.size(grp_ticks) > 0
for i = 0 to array.size(grp_ticks) - 1
if array.get(grp_active, i) and array.get(grp_counts, i) >= 2
int kind = array.get(grp_types, i)
string typS = kind == 1 ? "H" : "L"
string leftText = str.tostring(array.get(grp_counts, i)) + "x " + typS
float p = float(array.get(grp_ticks, i)) * syminfo.mintick
string ptxt = str.tostring(p, format.mintick)
table.cell(infoTbl, 0, row, leftText, text_color = color.white, bgcolor = color.new(color.black, 60))
table.cell(infoTbl, 1, row, ptxt, text_color = color.white, bgcolor = color.new(color.black, 60))
row += 1
if row > max_table_rows
break
// ---------- Screener-ready plot (use this plot as the Screener column/filter)
// Screener reads plots. Plot = 1 when there is at least one active grouped Equal H/L (count >= 2)
bool hasEqualHL = false
if array.size(grp_ticks) > 0
for i = 0 to array.size(grp_ticks) - 1
if array.get(grp_active, i) and array.get(grp_counts, i) >= 2
hasEqualHL := true
break
float equalHLPlot = hasEqualHL ? 1.0 : 0.0
// Hidden on chart but visible to Screener. If you prefer a visible mark remove `display=display.none`.
plot(equalHLPlot, title="EqualHL", style=plot.style_columns, color=color.new(color.yellow, 0), linewidth=2, display=display.none)
// invisible plot to keep indicator active
plot(na)
indicator("Grouped Peaks Screener (5/5) — Intraday (04:00 ET reset) — screener ready", overlay=true, max_lines_count=500, max_labels_count=0, max_boxes_count=0)
// ---------- user spec constants
LEFT = 5
RIGHT = 5
MS_PER_DAY = 24 * 60 * 60 * 1000
// ---------- typed arrays (Pine v6 best practice)
var int[] grp_ticks = array.new_int()
var int[] grp_counts = array.new_int()
var int[] grp_types = array.new_int() // 1 = high(res), -1 = low(sup)
var int[] grp_start = array.new_int() // bar_index of earliest peak in group
var line[] grp_lines = array.new_line()
var bool[] grp_active = array.new_bool()
// ---------- table (top-right)
max_table_rows = 20
var table infoTbl = table.new(position.top_right, 2, max_table_rows + 1, border_width = 1)
// ---------- helper: safely clear groups and delete drawings
f_clear_groups() =>
int nLines = array.size(grp_lines)
if nLines > 0
for i = 0 to nLines - 1
line ln = array.get(grp_lines, i)
if not na(ln)
line.delete(ln)
array.clear(grp_ticks)
array.clear(grp_counts)
array.clear(grp_types)
array.clear(grp_start)
array.clear(grp_lines)
array.clear(grp_active)
// ---------- trading day detection (premarket start 04:00 ET)
int y = year(time, "America/New_York")
int mo = month(time, "America/New_York")
int d = dayofmonth(time, "America/New_York")
int preStart = timestamp("America/New_York", y, mo, d, 4, 0)
int trading_day_start = time >= preStart ? preStart : preStart - MS_PER_DAY
var int prev_trading_day_start = na
if na(prev_trading_day_start)
prev_trading_day_start := trading_day_start
// reset at new premarket
if trading_day_start != prev_trading_day_start
f_clear_groups()
prev_trading_day_start := trading_day_start
bool allow_detection = time >= trading_day_start
// ---------- pivot detection (5 left / 5 right)
float ph = ta.pivothigh(high, LEFT, RIGHT)
float pl = ta.pivotlow(low, LEFT, RIGHT)
// ---------- process peak (add new or merge into existing same-price group)
f_process_peak(float price, int startBar, int kind) =>
int tick = int(math.round(price / syminfo.mintick))
int found = -1
int sz = array.size(grp_ticks)
if sz > 0
for i = 0 to sz - 1
if array.get(grp_active, i) and array.get(grp_types, i) == kind and array.get(grp_ticks, i) == tick
found := i
break
if found != -1
// merge into existing group
array.set(grp_counts, found, array.get(grp_counts, found) + 1)
if startBar < array.get(grp_start, found)
array.set(grp_start, found, startBar)
line ln = array.get(grp_lines, found)
if not na(ln)
line.set_xy1(ln, startBar, float(array.get(grp_ticks, found)) * syminfo.mintick)
line.set_xy2(ln, startBar + 1, float(array.get(grp_ticks, found)) * syminfo.mintick)
line ln2 = array.get(grp_lines, found)
if not na(ln2)
line.set_color(ln2, color.yellow)
else
color col = kind == 1 ? color.red : color.green
line lnNew = line.new(startBar, price, startBar + 1, price, xloc = xloc.bar_index, extend = extend.right, color = col, width = 2)
array.push(grp_ticks, tick)
array.push(grp_counts, 1)
array.push(grp_types, kind)
array.push(grp_start, startBar)
array.push(grp_lines, lnNew)
array.push(grp_active, true)
// apply pivots (pivot bar is bar_index - RIGHT, pivot confirmed at current bar)
if not na(ph)
int pivotBarIdxH = bar_index - RIGHT
int pivotTimeH = time[RIGHT]
if allow_detection and pivotTimeH >= trading_day_start
f_process_peak(ph, pivotBarIdxH, 1)
if not na(pl)
int pivotBarIdxL = bar_index - RIGHT
int pivotTimeL = time[RIGHT]
if allow_detection and pivotTimeL >= trading_day_start
f_process_peak(pl, pivotBarIdxL, -1)
// ---------- deletion rule: if price crosses level by >=1 tick -> delete group immediately
int nGroups = array.size(grp_ticks)
if nGroups > 0
for i = 0 to nGroups - 1
if array.get(grp_active, i)
int tick = array.get(grp_ticks, i)
int kind = array.get(grp_types, i)
int startB = array.get(grp_start, i)
if bar_index >= startB
if kind == 1
int highTick = int(math.round(high / syminfo.mintick))
if highTick > tick
line ln = array.get(grp_lines, i)
if not na(ln)
line.delete(ln)
array.set(grp_active, i, false)
else
int lowTick = int(math.round(low / syminfo.mintick))
if lowTick < tick
line ln = array.get(grp_lines, i)
if not na(ln)
line.delete(ln)
array.set(grp_active, i, false)
// ---------- Update top-right table "Equal H/L" and "Equal H/L price"
// header
table.cell(infoTbl, 0, 0, "Equal H/L", text_color = color.white, bgcolor = color.new(color.gray, 40))
table.cell(infoTbl, 1, 0, "Equal H/L price", text_color = color.white, bgcolor = color.new(color.gray, 40))
// clear rest of rows to avoid stale values
for r = 1 to max_table_rows
table.cell(infoTbl, 0, r, "", text_color = color.white, bgcolor = color.new(color.black, 100))
table.cell(infoTbl, 1, r, "", text_color = color.white, bgcolor = color.new(color.black, 100))
// populate grouped entries (count >= 2) that are active today
int row = 1
if array.size(grp_ticks) > 0
for i = 0 to array.size(grp_ticks) - 1
if array.get(grp_active, i) and array.get(grp_counts, i) >= 2
int kind = array.get(grp_types, i)
string typS = kind == 1 ? "H" : "L"
string leftText = str.tostring(array.get(grp_counts, i)) + "x " + typS
float p = float(array.get(grp_ticks, i)) * syminfo.mintick
string ptxt = str.tostring(p, format.mintick)
table.cell(infoTbl, 0, row, leftText, text_color = color.white, bgcolor = color.new(color.black, 60))
table.cell(infoTbl, 1, row, ptxt, text_color = color.white, bgcolor = color.new(color.black, 60))
row += 1
if row > max_table_rows
break
// ---------- Screener-ready plot (use this plot as the Screener column/filter)
// Screener reads plots. Plot = 1 when there is at least one active grouped Equal H/L (count >= 2)
bool hasEqualHL = false
if array.size(grp_ticks) > 0
for i = 0 to array.size(grp_ticks) - 1
if array.get(grp_active, i) and array.get(grp_counts, i) >= 2
hasEqualHL := true
break
float equalHLPlot = hasEqualHL ? 1.0 : 0.0
// Hidden on chart but visible to Screener. If you prefer a visible mark remove `display=display.none`.
plot(equalHLPlot, title="EqualHL", style=plot.style_columns, color=color.new(color.yellow, 0), linewidth=2, display=display.none)
// invisible plot to keep indicator active
plot(na)
Skrip open-source
Dengan semangat TradingView yang sesungguhnya, penulis skrip ini telah menjadikannya sumber terbuka, sehingga para trader dapat meninjau dan memverifikasi fungsinya. Hormat untuk penulisnya! Meskipun anda dapat menggunakannya secara gratis, ingatlah bahwa penerbitan ulang kode tersebut tunduk pada Tata Tertib kami.
Pernyataan Penyangkalan
Informasi dan publikasi tidak dimaksudkan untuk menjadi, dan bukan merupakan saran keuangan, investasi, perdagangan, atau rekomendasi lainnya yang diberikan atau didukung oleh TradingView. Baca selengkapnya di Persyaratan Penggunaan.
Skrip open-source
Dengan semangat TradingView yang sesungguhnya, penulis skrip ini telah menjadikannya sumber terbuka, sehingga para trader dapat meninjau dan memverifikasi fungsinya. Hormat untuk penulisnya! Meskipun anda dapat menggunakannya secara gratis, ingatlah bahwa penerbitan ulang kode tersebut tunduk pada Tata Tertib kami.
Pernyataan Penyangkalan
Informasi dan publikasi tidak dimaksudkan untuk menjadi, dan bukan merupakan saran keuangan, investasi, perdagangan, atau rekomendasi lainnya yang diberikan atau didukung oleh TradingView. Baca selengkapnya di Persyaratan Penggunaan.