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)
Korumalı komut dosyası
Bu komut dosyası kapalı kaynak olarak yayınlanmaktadır. Ancak, özgürce ve herhangi bir sınırlama olmaksızın kullanabilirsiniz – daha fazla bilgi burada.
Feragatname
Bilgiler ve yayınlar, TradingView tarafından sağlanan veya onaylanan finansal, yatırım, işlem veya diğer türden tavsiye veya tavsiyeler anlamına gelmez ve teşkil etmez. Kullanım Şartları'nda daha fazlasını okuyun.
Korumalı komut dosyası
Bu komut dosyası kapalı kaynak olarak yayınlanmaktadır. Ancak, özgürce ve herhangi bir sınırlama olmaksızın kullanabilirsiniz – daha fazla bilgi burada.
Feragatname
Bilgiler ve yayınlar, TradingView tarafından sağlanan veya onaylanan finansal, yatırım, işlem veya diğer türden tavsiye veya tavsiyeler anlamına gelmez ve teşkil etmez. Kullanım Şartları'nda daha fazlasını okuyun.