PROTECTED SOURCE SCRIPT
Most-Crossed Channels (FAST • Top-K • Flexible Window)

//version=5
indicator("Most-Crossed Channels (FAST • Top-K • Flexible Window)", overlay=true, max_boxes_count=60, max_labels_count=60)
// ---------- Inputs ----------
windowMode = input.string(defval="Last N Bars", title="Scan Window", options=["Last N Bars","Today (session)","Last N Sessions","Last X Minutes","Since time"])
barsLookback = input.int(defval=800, title="If Last N Bars → how many?", minval=100, maxval=5000)
sess = input.session(defval="0830-1500", title="Session (exchange tz)")
sessionsBack = input.int(defval=1, title="If Last N Sessions → how many?", minval=1, maxval=10)
minutesLookback = input.int(defval=120, title="If Last X Minutes → how many?", minval=5, maxval=24*60)
sinceTs = input.time(defval=timestamp("2024-01-01T09:30:00"), title="Since time (chart tz)")
channelsK = input.int(defval=3, title="How many channels (Top-K)?", minval=1, maxval=10)
binTicks = input.int(defval=8, title="Bin width (ticks)", minval=1, maxval=200) // NQ tick=0.25; 8 ticks = 2.0 pts
minSepTicks = input.int(defval=12, title="Min separation between channels (ticks)", minval=1, maxval=500)
countSource = input.string(defval="Wick (H-L)", title="Count bars using", options=["Wick (H-L)","Body only"])
drawMode = input.string(defval="Use Candle", title="Draw channel as", options=["Use Candle","Fixed Thickness"])
anchorPart = input.string(defval="Body", title="If Use Candle → part", options=["Body","Wick (High–Low)"])
fixedTicks = input.int(defval=8, title="If Fixed Thickness → thickness (ticks)", minval=1, maxval=200)
extendBars = input.int(defval=400, title="Extend to right (bars)", minval=50, maxval=5000)
showLabels = input.bool(defval=true, title="Show labels with counts")
// ---------- Colors ----------
colFill = color.new(color.blue, 78)
colEdge = color.new(color.blue, 0)
colTxt = color.white
// ---------- Draw caches (never empty) ----------
var box[] g_boxes = array.new_box()
var label[] g_lbls = array.new_label()
// ---------- Helpers ----------
barsFromMinutes(mins, avgBarMs) =>
ms = mins * 60000.0
int(math.max(2, math.round(ms / nz(avgBarMs, 60000.0))))
// First (oldest) candle in [0..scanN-1] whose selected part contains `level`
anchorIndexForPrice(level, useBody, scanNLocal) =>
idx = -1
for m = 1 to scanNLocal - 1
k = scanNLocal - m // oldest → newest
o = open[k]
c = close[k]
h = high[k]
l = low[k]
topZ = useBody ? math.max(o, c) : h
botZ = useBody ? math.min(o, c) : l
if level >= botZ and level <= topZ
idx := k
break
idx
// ---------- Window depth ----------
inSess = not na(time(timeframe.period, sess))
sessStartIdx = ta.valuewhen(inSess and not inSess[1], bar_index, 0)
sessStartIdxN = ta.valuewhen(inSess and not inSess[1], bar_index, sessionsBack - 1)
sinceStartIdx = ta.valuewhen(time >= sinceTs and time[1] < sinceTs, bar_index, 0)
avgBarMs = ta.sma(time - time[1], 50)
depthRaw = switch windowMode
"Last N Bars" => barsLookback
"Today (session)" => bar_index - nz(sessStartIdx, bar_index)
"Last N Sessions" => bar_index - nz(sessStartIdxN, bar_index)
"Last X Minutes" => barsFromMinutes(minutesLookback, avgBarMs)
"Since time" => bar_index - nz(sinceStartIdx, bar_index)
avail = bar_index + 1
scanN = math.min(avail, math.max(2, depthRaw))
scanN := math.min(scanN, 2000) // performance cap
// ---------- Early guard ----------
if scanN < 2
na
else
// ---------- Build price histogram (O(N + B)) ----------
priceMin = 10e10
priceMax = -10e10
for j = 0 to scanN - 1
loB = math.min(open[j], close[j])
hiB = math.max(open[j], close[j])
lo = (countSource == "Body only") ? loB : low[j]
hi = (countSource == "Body only") ? hiB : high[j]
priceMin := math.min(priceMin, nz(lo, priceMin))
priceMax := math.max(priceMax, nz(hi, priceMax))
rng = priceMax - priceMin
tick = syminfo.mintick
binSize = tick * binTicks
if na(rng) or rng <= 0 or binSize <= 0
na
else
// Pre-allocate fixed-size arrays (never size 0)
MAX_BINS = 600
var float[] diff = array.new_float(MAX_BINS + 2, 0.0) // +2 so iH+1 is safe
var float[] counts = array.new_float(MAX_BINS + 1, 0.0)
var int[] blocked = array.new_int(MAX_BINS + 1, 0)
var int[] topIdx = array.new_int()
binsN = math.max(1, math.min(MAX_BINS, int(math.ceil(rng / binSize)) + 1))
// reset slices
for i = 0 to binsN + 1
array.set(diff, i, 0.0)
for i = 0 to binsN
array.set(counts, i, 0.0)
array.set(blocked, i, 0)
array.clear(topIdx)
// Range adds
for j = 0 to scanN - 1
loB = math.min(open[j], close[j])
hiB = math.max(open[j], close[j])
lo = (countSource == "Body only") ? loB : low[j]
hi = (countSource == "Body only") ? hiB : high[j]
iL = int(math.floor((lo - priceMin) / binSize))
iH = int(math.floor((hi - priceMin) / binSize))
iL := math.max(0, math.min(binsN - 1, iL))
iH := math.max(0, math.min(binsN - 1, iH))
array.set(diff, iL, array.get(diff, iL) + 1.0)
array.set(diff, iH + 1, array.get(diff, iH + 1) - 1.0)
// Prefix sum → counts
run = 0.0
for b = 0 to binsN - 1
run += array.get(diff, b)
array.set(counts, b, run)
// Top-K with spacing
sepBins = math.max(1, int(math.ceil(minSepTicks / binTicks)))
picks = math.min(channelsK, binsN)
if picks > 0
for _ = 0 to picks - 1
bestVal = -1e9
bestBin = -1
for b = 0 to binsN - 1
if array.get(blocked, b) == 0
v = array.get(counts, b)
if v > bestVal
bestVal := v
bestBin := b
if bestBin >= 0
array.push(topIdx, bestBin)
lB = math.max(0, bestBin - sepBins)
rB = math.min(binsN - 1, bestBin + sepBins)
for bb = lB to rB
array.set(blocked, bb, 1)
// Clear old drawings safely
while array.size(g_boxes) > 0
box.delete(array.pop(g_boxes))
while array.size(g_lbls) > 0
label.delete(array.pop(g_lbls))
// Draw Top-K channels
sz = array.size(topIdx)
if sz > 0
for t = 0 to sz - 1
b = array.get(topIdx, t)
level = priceMin + (b + 0.5) * binSize
useBody = (drawMode == "Use Candle")
anc = anchorIndexForPrice(level, useBody, scanN)
anc := anc == -1 ? scanN - 1 : anc
oA = open[anc]
cA = close[anc]
hA = high[anc]
lA = low[anc]
float topV = na
float botV = na
if drawMode == "Use Candle"
topV := (anchorPart == "Body") ? math.max(oA, cA) : hA
botV := (anchorPart == "Body") ? math.min(oA, cA) : lA
else
half = (fixedTicks * tick) * 0.5
topV := level + half
botV := level - half
left = bar_index - anc
right = bar_index + extendBars
bx = box.new(left, topV, right, botV, xloc=xloc.bar_index, bgcolor=colFill, border_color=colEdge, border_width=2)
array.push(g_boxes, bx)
if showLabels
txt = str.tostring(int(array.get(counts, b))) + " crosses"
lb = label.new(left, topV, txt, xloc=xloc.bar_index, style=label.style_label_down, textcolor=colTxt, color=colEdge)
array.push(g_lbls, lb)
indicator("Most-Crossed Channels (FAST • Top-K • Flexible Window)", overlay=true, max_boxes_count=60, max_labels_count=60)
// ---------- Inputs ----------
windowMode = input.string(defval="Last N Bars", title="Scan Window", options=["Last N Bars","Today (session)","Last N Sessions","Last X Minutes","Since time"])
barsLookback = input.int(defval=800, title="If Last N Bars → how many?", minval=100, maxval=5000)
sess = input.session(defval="0830-1500", title="Session (exchange tz)")
sessionsBack = input.int(defval=1, title="If Last N Sessions → how many?", minval=1, maxval=10)
minutesLookback = input.int(defval=120, title="If Last X Minutes → how many?", minval=5, maxval=24*60)
sinceTs = input.time(defval=timestamp("2024-01-01T09:30:00"), title="Since time (chart tz)")
channelsK = input.int(defval=3, title="How many channels (Top-K)?", minval=1, maxval=10)
binTicks = input.int(defval=8, title="Bin width (ticks)", minval=1, maxval=200) // NQ tick=0.25; 8 ticks = 2.0 pts
minSepTicks = input.int(defval=12, title="Min separation between channels (ticks)", minval=1, maxval=500)
countSource = input.string(defval="Wick (H-L)", title="Count bars using", options=["Wick (H-L)","Body only"])
drawMode = input.string(defval="Use Candle", title="Draw channel as", options=["Use Candle","Fixed Thickness"])
anchorPart = input.string(defval="Body", title="If Use Candle → part", options=["Body","Wick (High–Low)"])
fixedTicks = input.int(defval=8, title="If Fixed Thickness → thickness (ticks)", minval=1, maxval=200)
extendBars = input.int(defval=400, title="Extend to right (bars)", minval=50, maxval=5000)
showLabels = input.bool(defval=true, title="Show labels with counts")
// ---------- Colors ----------
colFill = color.new(color.blue, 78)
colEdge = color.new(color.blue, 0)
colTxt = color.white
// ---------- Draw caches (never empty) ----------
var box[] g_boxes = array.new_box()
var label[] g_lbls = array.new_label()
// ---------- Helpers ----------
barsFromMinutes(mins, avgBarMs) =>
ms = mins * 60000.0
int(math.max(2, math.round(ms / nz(avgBarMs, 60000.0))))
// First (oldest) candle in [0..scanN-1] whose selected part contains `level`
anchorIndexForPrice(level, useBody, scanNLocal) =>
idx = -1
for m = 1 to scanNLocal - 1
k = scanNLocal - m // oldest → newest
o = open[k]
c = close[k]
h = high[k]
l = low[k]
topZ = useBody ? math.max(o, c) : h
botZ = useBody ? math.min(o, c) : l
if level >= botZ and level <= topZ
idx := k
break
idx
// ---------- Window depth ----------
inSess = not na(time(timeframe.period, sess))
sessStartIdx = ta.valuewhen(inSess and not inSess[1], bar_index, 0)
sessStartIdxN = ta.valuewhen(inSess and not inSess[1], bar_index, sessionsBack - 1)
sinceStartIdx = ta.valuewhen(time >= sinceTs and time[1] < sinceTs, bar_index, 0)
avgBarMs = ta.sma(time - time[1], 50)
depthRaw = switch windowMode
"Last N Bars" => barsLookback
"Today (session)" => bar_index - nz(sessStartIdx, bar_index)
"Last N Sessions" => bar_index - nz(sessStartIdxN, bar_index)
"Last X Minutes" => barsFromMinutes(minutesLookback, avgBarMs)
"Since time" => bar_index - nz(sinceStartIdx, bar_index)
avail = bar_index + 1
scanN = math.min(avail, math.max(2, depthRaw))
scanN := math.min(scanN, 2000) // performance cap
// ---------- Early guard ----------
if scanN < 2
na
else
// ---------- Build price histogram (O(N + B)) ----------
priceMin = 10e10
priceMax = -10e10
for j = 0 to scanN - 1
loB = math.min(open[j], close[j])
hiB = math.max(open[j], close[j])
lo = (countSource == "Body only") ? loB : low[j]
hi = (countSource == "Body only") ? hiB : high[j]
priceMin := math.min(priceMin, nz(lo, priceMin))
priceMax := math.max(priceMax, nz(hi, priceMax))
rng = priceMax - priceMin
tick = syminfo.mintick
binSize = tick * binTicks
if na(rng) or rng <= 0 or binSize <= 0
na
else
// Pre-allocate fixed-size arrays (never size 0)
MAX_BINS = 600
var float[] diff = array.new_float(MAX_BINS + 2, 0.0) // +2 so iH+1 is safe
var float[] counts = array.new_float(MAX_BINS + 1, 0.0)
var int[] blocked = array.new_int(MAX_BINS + 1, 0)
var int[] topIdx = array.new_int()
binsN = math.max(1, math.min(MAX_BINS, int(math.ceil(rng / binSize)) + 1))
// reset slices
for i = 0 to binsN + 1
array.set(diff, i, 0.0)
for i = 0 to binsN
array.set(counts, i, 0.0)
array.set(blocked, i, 0)
array.clear(topIdx)
// Range adds
for j = 0 to scanN - 1
loB = math.min(open[j], close[j])
hiB = math.max(open[j], close[j])
lo = (countSource == "Body only") ? loB : low[j]
hi = (countSource == "Body only") ? hiB : high[j]
iL = int(math.floor((lo - priceMin) / binSize))
iH = int(math.floor((hi - priceMin) / binSize))
iL := math.max(0, math.min(binsN - 1, iL))
iH := math.max(0, math.min(binsN - 1, iH))
array.set(diff, iL, array.get(diff, iL) + 1.0)
array.set(diff, iH + 1, array.get(diff, iH + 1) - 1.0)
// Prefix sum → counts
run = 0.0
for b = 0 to binsN - 1
run += array.get(diff, b)
array.set(counts, b, run)
// Top-K with spacing
sepBins = math.max(1, int(math.ceil(minSepTicks / binTicks)))
picks = math.min(channelsK, binsN)
if picks > 0
for _ = 0 to picks - 1
bestVal = -1e9
bestBin = -1
for b = 0 to binsN - 1
if array.get(blocked, b) == 0
v = array.get(counts, b)
if v > bestVal
bestVal := v
bestBin := b
if bestBin >= 0
array.push(topIdx, bestBin)
lB = math.max(0, bestBin - sepBins)
rB = math.min(binsN - 1, bestBin + sepBins)
for bb = lB to rB
array.set(blocked, bb, 1)
// Clear old drawings safely
while array.size(g_boxes) > 0
box.delete(array.pop(g_boxes))
while array.size(g_lbls) > 0
label.delete(array.pop(g_lbls))
// Draw Top-K channels
sz = array.size(topIdx)
if sz > 0
for t = 0 to sz - 1
b = array.get(topIdx, t)
level = priceMin + (b + 0.5) * binSize
useBody = (drawMode == "Use Candle")
anc = anchorIndexForPrice(level, useBody, scanN)
anc := anc == -1 ? scanN - 1 : anc
oA = open[anc]
cA = close[anc]
hA = high[anc]
lA = low[anc]
float topV = na
float botV = na
if drawMode == "Use Candle"
topV := (anchorPart == "Body") ? math.max(oA, cA) : hA
botV := (anchorPart == "Body") ? math.min(oA, cA) : lA
else
half = (fixedTicks * tick) * 0.5
topV := level + half
botV := level - half
left = bar_index - anc
right = bar_index + extendBars
bx = box.new(left, topV, right, botV, xloc=xloc.bar_index, bgcolor=colFill, border_color=colEdge, border_width=2)
array.push(g_boxes, bx)
if showLabels
txt = str.tostring(int(array.get(counts, b))) + " crosses"
lb = label.new(left, topV, txt, xloc=xloc.bar_index, style=label.style_label_down, textcolor=colTxt, color=colEdge)
array.push(g_lbls, lb)
סקריפט מוגן
סקריפט זה פורסם כמקור סגור. עם זאת, אתה יכול להשתמש בו באופן חופשי וללא כל הגבלה - למד עוד כאן.
כתב ויתור
המידע והפרסומים אינם אמורים להיות, ואינם מהווים, עצות פיננסיות, השקעות, מסחר או סוגים אחרים של עצות או המלצות שסופקו או מאושרים על ידי TradingView. קרא עוד בתנאים וההגבלות.
סקריפט מוגן
סקריפט זה פורסם כמקור סגור. עם זאת, אתה יכול להשתמש בו באופן חופשי וללא כל הגבלה - למד עוד כאן.
כתב ויתור
המידע והפרסומים אינם אמורים להיות, ואינם מהווים, עצות פיננסיות, השקעות, מסחר או סוגים אחרים של עצות או המלצות שסופקו או מאושרים על ידי TradingView. קרא עוד בתנאים וההגבלות.