← К каталогу
Исходный код · ID 4197

FVG Displacement & Fill Scoring

indicator_fvg_displacement_&_fill_scoring_for_tradingview.pine
Вы автор или правообладатель?

Сообщите нам, если считаете, что материал размещён без разрешения.

Краткий обзор

О программе

Индикатор «FVG Displacement & Fill Scoring» предназначен для обнаружения и оценки ценовых разрывов (FVG — Fair Value Gaps) на графиках TradingView. Он рассчитывает составной балл смещения каждого разрыва на основе трёх взвешенных метрик: размера разрыва, диапазона свечи и соотношения тела свечи к диапазону, всё выражается в единицах ATR. Индикатор отслеживает жизненный цикл зон — от появления до заполнения или инвалидации — и визуализирует их в виде цветных прямоугольников с метками на графике.

Продукт предоставляет дашборд-панель с информацией о ближайших зонах спроса и предложения, режиме рынка (SMA на H4) и статистике разделения (квартильные бины балла смещения vs реакция цены). Дополнительно доступны условные оповещения TradingView при появлении новых зон и изменении состояния заполнения. Индикатор работает на подтверждённых барах без перерисовки и не генерирует торговые сигналы, входы или размеры позиций.

Настройки охватывают детекцию разрывов, весовые коэффициенты и нормализацию составного балла, измерение структурных проб и объёма, фильтрацию по сессии и таймфрейму режима, а также обширный набор параметров визуализации и производительности. Все данные о зонах хранятся в памяти с возможностью ограничения количества отслеживаемых зон.

Настройки программы

НастройкаОписание
atrLen Длина периода для расчёта ATR (средний истинный диапазон), используемого как базовая единица измерения размеров разрывов и свечных диапазонов.
minGapATR Минимальный размер разрыва в единицах ATR для отслеживания. Значение 0 означает отслеживание всех корректных трёхсвечных разрывов. Повышение фильтрует микроразрывы.
detectBull Включает или отключает обнаружение бычьих (demand) разрывов, где низкая свеча выше высокой свечи двумя барами ранее.
detectBear Включает или отключает обнаружение медвежьих (supply) разрывов, где высокая свеча ниже низкой свечи двумя барами ранее.
wGap Весовой коэффициент для размера разрыва в составеном балле смещения.
wRange Весовой коэффициент для диапазона свечи в составеном балле смещения.
wBody Весовой коэффициент для соотношения тела свечи к диапазону в составеном балле смещения.
gapNorm Нормализующий коэффициент: значение размера разрыва в единицах ATR, которое соответствует полной подсчётной оценке 1.0 для метрики размера разрыва.
rngNorm Нормализующий коэффициент: значение диапазона свечи в единицах ATR, которое соответствует полной подсчётной оценке 1.0 для метрики диапазона.
structProbe Включает измерение пробоя структуры: проверяет, не пробил ли свеча смещения последний подтверждённый пивот (бычий: максимум, медвежий: минимум). Результат экспортируется и бинится, но не входит в составной балл.
pivLen Количество баров для поиска назад и вперёд при определении ключевых пивотов, используемых в пробе структурного пробоя.
volProbe Включает измерение соотношения объёма свечи смещения к среднему объёму. Результат экспортируется и бинится, но не входит в составной балл.
volAvgLen Длина периода для расчёта среднего объёма, используемого в зондировании объёма.
halfPct Порог половинного заполнения зоны в доле от размера разрыва. При достижении этого уровня заполнение зоны переходит в состояние «половинное».
reactBars Количество баров после первой отметки тега, в течение которых измеряется благоприятное удаление цены от зоны. Это переменная результата для хранилища разделения.
regimeTF Таймфрейм для определения режима рынка (H4 по умолчанию). Регим используется только как флаг для панели и хранилища, не как условие фильтрации.
regimeLen Длина SMA для расчёта линии режима на указанном таймфрейме.
useSess Включает фильтрацию обнаружения разрывов только внутри заданной торговой сессии.
sess Временной интервал торговой сессии для фильтрации обнаружения разрывов (по умолчанию 0000-2400 — круглосуточно).
sessTZ Часовой пояс торговой сессии, в котором задаётся фильтр.
showBoxes Включает или отключает отображение прямоугольников зон разрывов на графике.
gradeOp При включении прозрачность прямоугольников зон изменяется в зависимости от состояния заполнения (свежая — более плотная, заполненная — более прозрачная).
showLbl Включает или отключает инлайн-метки (текстовые подписи) на активных зонах.
retailLbl При включении метки зон отображаются в формате «DEMAND ZONE / SUPPLY ZONE · балл/100 · состояние». При отключении — в количественном формате «балл СОСТОЯНИЕ %заполнения».
showMid Включает отображение пунктирной линии на уровне 50% заполнения (половинный порог) каждой активной зоны.
maxAge Максимальный возраст зоны в баражах для отображения на графике. Зоны старше этого порога перестают рисоваться. Значение 0 означает без ограничений. Данные в хранилище при этом не затрагиваются.
showCall Включает отображение стрелок-вызовов (callout) на свече смещения при обнаружении нового разрыва.
showPanel Включает или отключает дашборд-панель в правом верхнем углу графика с информацией о зонах, режиме и ATR.
maxDraw Максимальное количество прямоугольников зон, отображаемых на графике одновременно. Более старые зоны удаляются визуально.
maxLbl Максимальное количество инлайн-меток, отображаемых одновременно. Присваиваются только наиболее высоко оценённым активным зонам. Мёртвые зоны (полностью заполненные или инвалидированные) не получают меток.
minLblScore Минимальный составной балл для присвоения инлайн-метки зоне. Зоны с оценкой ниже этого порога не получают меток. Значение 0 отключает фильтр.
lblAnti Режим обработки перекрывающихся меток: «Off» — без коррекции, «Nudge apart (vertical)» — раздвигает метки вертикально, «Cascade right (bars)» — сдвигает метки вправо по барам.
lblStagger В режиме каскадного сдвига — количество баров, на которое каждое последующее перекрывающееся смещённое смещается вправо.
lblLeader Включает отображение пунктирных соединительных линий от смещённой метки к правому краю соответствующей зоны.
lblOvlATR Минимальное вертикальное расстояние между якорями меток, выраженное в единицах ATR. В режиме сдвига задаёт допустимое перекрытие.
showDead При включении прямоугольники полностью заполненных или инвалидированных зон сохраняются на графике в приглушённом виде для обзора. При отключении они удаляются сразу после заполнения.
bullHex Цвет для обозначения бычьих (demand) зон на графике.
bearHex Цвет для обозначения медвежьих (supply) зон на графике.
maxZones Жёсткий лимит количества отслеживаемых зон в памяти. Старые зоны за пределами лимита удаляются. Влияет на производительность при глубокой истории.
showStats Включает или отключает таблицу квартильной статистики разделения в правом нижнем углу графика.
minN Минимальное количество завершённых наблюдений в выборке для доверенной оценки квартильных статистик в таблице разделения.
sliceOpt Фильтр выборки для хранилища разделения: «All» — все данные, «Regime-aligned» — только совпадающие с режимом, «Counter-regime» — противоположный режим, «Struct-broke» — пробитые пивоты, «No-struct» — без пробития.
Получить полный код

Поиск по коду файла

indicator("FVG Displacement & Fill Scoring", shorttitle="FVG DispFill", overlay=true, max_boxes_count=500, max_labels_count=500, max_lines_count=500)

grpDet   = "Detection"
atrLen       = input.int(14,   "ATR length", minval=1, group=grpDet)
minGapATR    = input.float(0.0,"Min gap size (× ATR) to track", minval=0.0, step=0.05, group=grpDet, tooltip="0 = track every valid 3-candle gap; the score does the ranking. Raise only to prune micro-gaps.")
detectBull   = input.bool(true,"Detect bullish (demand) gaps", group=grpDet)
detectBear   = input.bool(true,"Detect bearish (supply) gaps", group=grpDet)

grpScore = "Displacement score (composite, 0–100)"
wGap     = input.float(1.0, "Weight — gap size (ATR)",     minval=0.0, step=0.1, group=grpScore)
wRange   = input.float(1.0, "Weight — candle range (ATR)", minval=0.0, step=0.1, group=grpScore)
wBody    = input.float(1.0, "Weight — body / range ratio", minval=0.0, step=0.1, group=grpScore)
gapNorm  = input.float(1.0, "Normalise — gap ATR = full at",   minval=0.1, step=0.1, group=grpScore, tooltip="Gap size (in ATR) that maps to a full sub-score of 1.0.")
rngNorm  = input.float(2.0, "Normalise — range ATR = full at", minval=0.1, step=0.1, group=grpScore, tooltip="Displacement-candle range (in ATR) that maps to a full sub-score of 1.0.")

grpProbe = "Measure-only probes (not in composite)"
structProbe  = input.bool(true, "Structure-break probe (broke prior pivot)", group=grpProbe, tooltip="Did the displacement candle break the most recent confirmed pivot (bull: pivot high, bear: pivot low)? Exported and binned, kept out of the score until the harness earns it.")
pivLen       = input.int(10, "Pivot lookback / lookforward", minval=2, group=grpProbe)
volProbe     = input.bool(true, "Volume probe (disp vol vs avg)", group=grpProbe)
volAvgLen    = input.int(20, "Volume average length", minval=2, group=grpProbe)

grpFill  = "Fill lifecycle"
halfPct  = input.float(0.5, "Half-fill threshold (fraction of gap)", minval=0.05, maxval=0.95, step=0.05, group=grpFill)
reactBars= input.int(20, "Reaction window (bars) after first tag", minval=1, group=grpFill, tooltip="Bars over which favorable excursion away from the zone is measured. This is the outcome variable for the separation harness.")

grpRegime= "Regime / context (flag only)"

regimeTF = input.timeframe("240", "Regime timeframe", group=grpRegime)
regimeLen= input.int(20, "Regime SMA length", minval=1, group=grpRegime)

grpSess  = "Session filter (UTC)"
useSess  = input.bool(false, "Only detect inside session", group=grpSess)
sess     = input.session("0000-2400", "Session", group=grpSess)
sessTZ   = input.string("UTC", "Session timezone", group=grpSess)

grpViz   = "Visuals"
showBoxes= input.bool(true,  "Draw gap boxes", group=grpViz)
gradeOp  = input.bool(true,  "Grade box opacity by fill state", group=grpViz)
showLbl  = input.bool(true,  "Inline zone label", group=grpViz)
retailLbl= input.bool(true,  "Retail wording (Demand/Supply zone + strength)", group=grpViz, tooltip="On = 'DEMAND ZONE · 72/100 · Fresh' for published charts. Off = quant read 'score STATE fill%' for your own use.")
showMid  = input.bool(true,  "50% midline (half-fill threshold)", group=grpViz, tooltip="Dashed line at the half-fill level of each live zone. Drawn at halfPct, so it follows the half-fill threshold.")
maxAge   = input.int(400, "Max zone age drawn (bars, 0 = unlimited)", minval=0, group=grpViz, tooltip="Display only. Zones older than this stop being drawn so ancient gaps don't paint wall-to-wall bands across the chart. Harness data is untouched.")
showCall = input.bool(true,  "Callout arrow on displacement candle", group=grpViz)
showPanel= input.bool(true,  "Dashboard panel", group=grpViz)
maxDraw  = input.int(30, "Max gap boxes drawn (oldest retired)", minval=1, maxval=400, group=grpViz)
maxLbl   = input.int(8,  "Max inline labels (top score, live only)", minval=0, maxval=60, group=grpViz, tooltip="Only the highest-scoring LIVE zones get an inline label. Dead zones (full/invalidated) are never labelled. Stops the label wall in ranges.")
minLblScore = input.float(0.0, "Min score for inline label", minval=0.0, maxval=100.0, step=5, group=grpViz, tooltip="Zones scoring below this never get an inline label, even if a top-K slot is free. 0 = off. Display only — boxes and harness untouched.")
lblAnti     = input.string("Nudge apart (vertical)", "Overlapping labels", options=["Off", "Nudge apart (vertical)", "Cascade right (bars)"], group=grpViz, tooltip="What to do when two labelled zones sit too close together. Nudge = keep labels at the zone edge and push them apart vertically by the minimum needed, split evenly up/down. Cascade = step each colliding label rightward instead. Display only.")
lblStagger  = input.int(11, "Cascade step (bars)", minval=1, maxval=100, group=grpViz, tooltip="Cascade mode only — how many bars each subsequent colliding label steps right.")
lblLeader   = input.bool(true, "Leader line (label → zone)", group=grpViz, tooltip="When anti-overlap moves a label away from its zone mid (nudge or cascade), draw a dotted connector from the label anchor back to the zone's right edge at its midpoint. Labels that were not displaced get no leader. Display only.")
lblOvlATR   = input.float(0.8, "Label spacing (× ATR)", minval=0.1, maxval=10.0, step=0.1, group=grpViz, tooltip="Minimum vertical distance between label anchors, in ATRs. In nudge mode this is the gap enforced between labels; in cascade mode it is the collision tolerance. Raise if labels still touch at your zoom level, lower for tighter packing.")
showDead = input.bool(false, "Keep full / invalidated zone boxes", group=grpViz, tooltip="Off = retire a zone's box the moment it fills or invalidates (data stays in the harness). On = keep them faint for review.")
bullHex  = input.color(#16CFA1, "Bull (demand) color", group=grpViz)
bearHex  = input.color(#FF4D6E, "Bear (supply) color", group=grpViz)

grpPerf  = "Performance"
maxZones = input.int(500, "Max zones held in memory (oldest dropped)", minval=50, maxval=2000, group=grpPerf, tooltip="Hard cap on the tracked-zone array — this is what bounds the per-bar loop. The oldest zones past the cap are dropped. Lower it if you hit 'Loop takes too long' on deep history.")

grpRes   = "Research — separation harness"
showStats= input.bool(true, "Show quartile stats table", group=grpRes)
minN     = input.int(20, "Min completed sample to trust bins", minval=4, group=grpRes)
sliceOpt = input.string("All", "Harness slice", options=["All", "Regime-aligned", "Counter-regime", "Struct-broke", "No-struct"], group=grpRes, tooltip="Filter the completed sample before binning. Regime slices use the H4 regime direction AT FIRST TAG (the decision moment), not at birth. Struct slices use the structure-break probe. Quartile thresholds are recomputed within the slice.")

colBull   = bullHex
colBear   = bearHex
colText   = color.new(#e6e6e6, 0)
colDim    = color.new(#9aa0a6, 0)
panelBg   = color.new(#12161c, 10)
panelHdr  = color.new(#1c232c, 0)

atr = ta.atr(atrLen)

regimeSMA = request.security(syminfo.tickerid, regimeTF, ta.sma(close, regimeLen), lookahead=barmerge.lookahead_off)

regimeDir = na(regimeSMA) ? 0 : (close > regimeSMA ? 1 : close < regimeSMA ? -1 : 0)

inSession = not useSess or not na(time(timeframe.period, sess, sessTZ))

var float lastPivotHigh = na
var float lastPivotLow  = na
ph = ta.pivothigh(high, pivLen, pivLen)
pl = ta.pivotlow(low,  pivLen, pivLen)
if not na(ph)
    lastPivotHigh := ph
if not na(pl)
    lastPivotLow := pl

volAvg = ta.sma(volume, volAvgLen)

S_FRESH = 0
S_TAG   = 1
S_HALF  = 2
S_FULL  = 3
S_INVAL = 4

type FVG
    int    dir            // +1 bull demand / -1 bear supply
    float  top            // upper edge
    float  bot            // lower edge
    int    birthBar       // bar_index at birth
    float  dispScore      // composite 0–100
    bool   structBroke    // measure-only probe
    float  volRatio       // measure-only probe
    int    state          // fill state code
    float  fillDepth      // 0..1 max penetration into the gap
    float  reactExc       // favorable excursion (ATR-at-tag units) in reactBars after first tag
    int    tagBar         // bar_index when first tagged (na → -1)
    float  atrTag         // ATR frozen at first tag — excursion denominator for the whole window
    int    regimeTag      // H4 regime direction at first tag (+1 / -1 / 0)
    bool   reactDone      // reaction window complete
    bool   counted        // pushed to harness sample
    box    bx
    label  lb
    line   ml             // 50% (half-fill) midline
    line   ldr            // dotted leader connecting a displaced label to its zone

var array<FVG> zones = array.new<FVG>()

var array<float> sampScore = array.new<float>()
var array<float> sampExc   = array.new<float>()
var array<int>   sampStruct= array.new<int>()   // 1/0
var array<int>   sampRegime= array.new<int>()   // +1 aligned / -1 counter / 0 flat-unknown (at first tag)

clamp01(x) => math.max(0.0, math.min(1.0, x))

f_pushSample(FVG z) =>
    array.push(sampScore,  z.dispScore)
    array.push(sampExc,    z.reactExc)
    array.push(sampStruct, z.structBroke ? 1 : 0)
    array.push(sampRegime, z.regimeTag == 0 ? 0 : z.dir == z.regimeTag ? 1 : -1)

dispScoreOf(float gapATR, float rngATR, float bodyRatio) =>
    gs = clamp01(gapATR / gapNorm)
    rs = clamp01(rngATR / rngNorm)
    bs = clamp01(bodyRatio)
    wsum = wGap + wRange + wBody
    wsum <= 0 ? 0.0 : 100.0 * (wGap*gs + wRange*rs + wBody*bs) / wsum

stateTransp(int st) =>
    if not gradeOp
        82
    else
        switch st
            S_FRESH => 78
            S_TAG   => 84
            S_HALF  => 89
            S_FULL  => 93
            => 93

var float alBDir    = 0.0
var float alBScore  = 0.0
var float alBState  = 0.0
var float alBStruct = 0.0
var float alBVol    = 0.0
var float alBTop    = 0.0
var float alBBot    = 0.0
var float alBFill   = 0.0

var float alCDir    = 0.0
var float alCScore  = 0.0
var float alCState  = 0.0
var float alCStruct = 0.0
var float alCVol    = 0.0
var float alCTop    = 0.0
var float alCBot    = 0.0
var float alCFill   = 0.0

bornThisBar    = false
changedThisBar = false
float chgBest  = na    // highest dispScore among state changes this bar

makeZone(int dir, float top, float bot) =>

    dRange = high[1] - low[1]
    dBody  = math.abs(close[1] - open[1])
    dBodyR = dRange > 0 ? dBody / dRange : 0.0
    gapSz  = top - bot
    gapATR = atr > 0 ? gapSz / atr : 0.0
    rngATR = atr > 0 ? dRange / atr : 0.0
    score  = dispScoreOf(gapATR, rngATR, dBodyR)

    sBroke = false
    if structProbe
        sBroke := dir == 1 ? (not na(lastPivotHigh) and high[1] > lastPivotHigh) : (not na(lastPivotLow)  and low[1]  < lastPivotLow)
    vRatio = volProbe and not na(volAvg) and volAvg > 0 ? volume[1] / volAvg : na
    FVG.new(dir, top, bot, bar_index, score, sBroke, vRatio, S_FRESH, 0.0, 0.0, -1, na, 0, false, false, na, na, na, na)

if barstate.isconfirmed and inSession and bar_index > 2

    if detectBull and low > high[2]
        top = low
        bot = high[2]
        if (top - bot) >= minGapATR * atr
            z = makeZone(1, top, bot)
            array.push(zones, z)
            bornThisBar := true
            alBDir    := z.dir
            alBScore  := z.dispScore
            alBState  := z.state
            alBStruct := z.structBroke ? 1 : 0
            alBVol    := na(z.volRatio) ? -1.0 : z.volRatio
            alBTop    := z.top
            alBBot    := z.bot
            alBFill   := z.fillDepth

    if detectBear and high < low[2]
        top = low[2]
        bot = high
        if (top - bot) >= minGapATR * atr
            z = makeZone(-1, top, bot)
            array.push(zones, z)
            bornThisBar := true
            alBDir    := z.dir
            alBScore  := z.dispScore
            alBState  := z.state
            alBStruct := z.structBroke ? 1 : 0
            alBVol    := na(z.volRatio) ? -1.0 : z.volRatio
            alBTop    := z.top
            alBBot    := z.bot
            alBFill   := z.fillDepth
var table wm = table.new(position.bottom_right, 1, 1)
if barstate.isfirst
    table.cell(wm, 0, 0, "Telegram @SimpleForexTools", text_color = color.new(color.gray, 70), text_size = size.large)

if barstate.isconfirmed
    n = array.size(zones)
    if n > 0
        for i = 0 to n - 1
            z = array.get(zones, i)
            span = z.top - z.bot
            if span > 0 and z.state != S_INVAL
                prevState = z.state

                pen = 0.0
                if z.dir == 1

                    if low < z.top
                        pen := clamp01((z.top - math.max(low, z.bot)) / span)
                else

                    if high > z.bot
                        pen := clamp01((math.min(high, z.top) - z.bot) / span)
                if pen > z.fillDepth
                    z.fillDepth := pen

                invalidated = z.dir == 1 ? close < z.bot : close > z.top

                newState = z.state
                if invalidated
                    newState := S_INVAL
                else if z.fillDepth >= 1.0
                    newState := S_FULL
                else if z.fillDepth >= halfPct
                    newState := S_HALF
                else if z.fillDepth > 0.0
                    newState := S_TAG
                if newState != prevState
                    z.state := newState

                    if z.tagBar == -1 and newState >= S_TAG
                        z.tagBar    := bar_index
                        z.atrTag    := atr
                        z.regimeTag := regimeDir

                    if na(chgBest) or z.dispScore > chgBest
                        chgBest := z.dispScore
                        changedThisBar := true
                        alCDir    := z.dir
                        alCScore  := z.dispScore
                        alCState  := z.state
                        alCStruct := z.structBroke ? 1 : 0
                        alCVol    := na(z.volRatio) ? -1.0 : z.volRatio
                        alCTop    := z.top
                        alCBot    := z.bot
                        alCFill   := z.fillDepth

                if z.tagBar != -1 and not z.reactDone and bar_index > z.tagBar
                    if z.dir == 1
                        exc = z.atrTag > 0 ? (high - z.top) / z.atrTag : 0.0
                        if exc > z.reactExc
                            z.reactExc := exc
                    else
                        exc = z.atrTag > 0 ? (z.bot - low) / z.atrTag : 0.0
                        if exc > z.reactExc
                            z.reactExc := exc
                    if bar_index - z.tagBar >= reactBars
                        z.reactDone := true
                        if not z.counted
                            z.counted := true
                            f_pushSample(z)

                if z.state == S_INVAL and z.tagBar != -1 and not z.counted
                    z.reactDone := true
                    z.counted   := true
                    f_pushSample(z)
                array.set(zones, i, z)

if barstate.isconfirmed and not showDead
    pj = array.size(zones) - 1
    while pj >= 0
        zp = array.get(zones, pj)
        if (zp.state == S_FULL or zp.state == S_INVAL) and zp.counted
            if not na(zp.bx)
                box.delete(zp.bx)
            if not na(zp.lb)
                label.delete(zp.lb)
            if not na(zp.ml)
                line.delete(zp.ml)
            if not na(zp.ldr)
                line.delete(zp.ldr)
            array.remove(zones, pj)
        pj -= 1

if barstate.isconfirmed
    while array.size(zones) > maxZones
        zd = array.get(zones, 0)
        if not na(zd.bx)
            box.delete(zd.bx)
        if not na(zd.lb)
            label.delete(zd.lb)
        if not na(zd.ml)
            line.delete(zd.ml)
        if not na(zd.ldr)
            line.delete(zd.ldr)
        array.remove(zones, 0)

f_stateName(int st) =>
    switch st
        S_FRESH => "FRESH"
        S_TAG   => "TAGGED"
        S_HALF  => "HALF"
        S_FULL  => "FULL"
        S_INVAL => "INVALID"
        => "?"

f_retailState(int st) =>
    switch st
        S_FRESH => "Fresh"
        S_TAG   => "Tested"
        S_HALF  => "Half-filled"
        S_FULL  => "Filled"
        => "Broken"

f_zoneText(int dir, float score, int st, float fill, bool sBroke) =>
    if retailLbl
        (dir == 1 ? "DEMAND ZONE" : "SUPPLY ZONE") + "\n" + str.tostring(math.round(score)) + "/100 · " + f_retailState(st)
    else
        str.tostring(math.round(score)) + "  " + f_stateName(st) + " " + str.tostring(math.round(fill * 100)) + "%" + (sBroke ? " ⚡" : "")

if barstate.islast and (showBoxes or showLbl or showMid)

    total = array.size(zones)
    start = math.max(0, total - maxDraw)

    if start > 0
        for i = 0 to start - 1
            zr = array.get(zones, i)
            if not na(zr.bx)
                box.delete(zr.bx)
                zr.bx := na
            if not na(zr.lb)
                label.delete(zr.lb)
                zr.lb := na
            if not na(zr.ml)
                line.delete(zr.ml)
                zr.ml := na
            if not na(zr.ldr)
                line.delete(zr.ldr)
                zr.ldr := na
            array.set(zones, i, zr)

    lblSet = array.new<int>()
    if showLbl and maxLbl > 0 and total > 0
        liveScores = array.new<float>()
        for i = start to total - 1
            zc = array.get(zones, i)
            zcExp = maxAge > 0 and (bar_index - zc.birthBar) > maxAge
            if zc.state != S_INVAL and zc.state != S_FULL and not zcExp and zc.dispScore >= minLblScore
                array.push(liveScores, zc.dispScore)
        nLive = array.size(liveScores)
        if nLive > 0
            float lblCut = -1.0                               // <= all real scores → everyone eligible
            if nLive > maxLbl
                array.sort(liveScores, order.descending)
                lblCut := array.get(liveScores, maxLbl - 1)   // K-th highest score
            int slots = maxLbl

            for i = start to total - 1
                zc = array.get(zones, i)
                zcExp = maxAge > 0 and (bar_index - zc.birthBar) > maxAge
                elig = zc.state != S_INVAL and zc.state != S_FULL and not zcExp and zc.dispScore >= minLblScore
                if elig and zc.dispScore > lblCut and slots > 0
                    array.push(lblSet, i)
                    slots -= 1

            ti = total - 1
            while ti >= start and slots > 0
                zc = array.get(zones, ti)
                zcExp = maxAge > 0 and (bar_index - zc.birthBar) > maxAge
                elig = zc.state != S_INVAL and zc.state != S_FULL and not zcExp and zc.dispScore

Показано первые 50% кода (символов: 26455)

Открыть по тарифу

Купите тариф с пакетом открытий или войдите, если тариф уже есть. Открытый файл останется в вашем кабинете навсегда.

Разовая покупка файла

На этой странице откроется полный исходный код файла вместо ознакомительного фрагмента.

Чтобы оформить заказ, введите email и перейдите к оплате.

Стоимость полного кода
2 645,50₽ ≈ $32.33