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)
Script protégé
Ce script est publié en source fermée. Toutefois, vous pouvez l'utiliser librement et sans aucune restriction - en savoir plus ici.
Clause de non-responsabilité
Les informations et les publications ne sont pas destinées à être, et ne constituent pas, des conseils ou des recommandations en matière de finance, d'investissement, de trading ou d'autres types de conseils fournis ou approuvés par TradingView. Pour en savoir plus, consultez les Conditions d'utilisation.
Script protégé
Ce script est publié en source fermée. Toutefois, vous pouvez l'utiliser librement et sans aucune restriction - en savoir plus ici.
Clause de non-responsabilité
Les informations et les publications ne sont pas destinées à être, et ne constituent pas, des conseils ou des recommandations en matière de finance, d'investissement, de trading ou d'autres types de conseils fournis ou approuvés par TradingView. Pour en savoir plus, consultez les Conditions d'utilisation.