Индикатор On Balance Volume: формула, настройки и чтение

On Balance Volume, опубликованный Джозефом Гранвиллом в 1963 году, прибавляет весь объём бара к текущей сумме, когда закрытие растёт, и вычитает его, когда закрытие падает. Неизменное закрытие оставляет сумму ровно на месте.

Senzoukria · Индикаторы · Updated September 2026


On Balance Volume Объём, в отдельной панели под графиком.

Что измеряет On Balance Volume

Направление определяется строгим сравнением соседних закрытий: выше — прибавляем, ниже — вычитаем, равно — ничего не делаем, поэтому бары-доджи никогда не считаются бычьими. Объём каждого бара берётся со знаком целиком — индикатор не различает закрытие на тик выше и на тридцать тиков выше. Senzoukria задаёт начальное значение серии 0 на первом загруженном баре: линия воспроизводима, но её абсолютный уровень лишён смысла. Накопление непрерывно и не сбрасывается на открытии в 17:00 CT — этот якорь оставлен для сессионных мер вроде накопленной дельты.

Формула — так, как она реализована

Это не привычное определение из других источников, а то, что вычисляет поставляемый код, задокументированное рядом с реализацией:

Période entière ≥ `min` depuis les params (garde-fou UI). */ function intParam( params: Record<string, unknown>, key: string, fallback: number, min = 1, ): number { return Math.max(min, Math.floor(numParam(params, key, fallback))); } function toPoints( bars: readonly FootprintBar[], values: ReadonlyArray<number | null>, ): SeriesPoint[] { return bars.map((b, i) => ({ time: b.bucketTsNs, value: values[i] ?? null })); } /** Points d'histogramme : `f` rend `null` quand la barre n'a pas d'information — la barre n'émet alors PAS de point (contrat `HistogramPoint` : pas de trou, pas de valeur inventée). */ function histPoints( bars: readonly FootprintBar[], f: (bar: FootprintBar, i: number) => number | null, ): HistogramPoint[] { const out: HistogramPoint[] = []; for (let i = 0; i < bars.length; i++) { const v = f(bars[i], i); if (v !== null) out.push({ time: bars[i].bucketTsNs, value: v }); } return out; } function histOut( points: HistogramPoint[], positiveColor: string, negativeColor: string, ): SeriesOutput { return { shape: "histogram", points, positiveColor, negativeColor }; } /** SMA N sur série à trous : null dès qu'UNE valeur de la fenêtre est null — moyenner autour d'un trou mentirait sur la période effective (même règle que `smaNullable` de momentum.ts). */ function smaNullable( values: ReadonlyArray<number | null>, period: number, ): Array<number | null> { const n = Math.max(1, Math.floor(period)); const out: Array<number | null> = new Array<number | null>(values.length).fill(null); for (let i = n - 1; i < values.length; i++) { let sum = 0; let ok = true; for (let j = i - n + 1; j <= i; j++) { const v = values[j]; if (v === null) { ok = false; break; } sum += v; } if (ok) out[i] = sum / n; } return out; } /** EMA N par segment de valeurs définies consécutives. Un trou reste null et réinitialise uniquement la graine suivante : les valeurs antérieures sont conservées. Chaque reprise attend N valeurs pour sa SMA initiale, puis suit la même récurrence que `emaSeries`, sans zéro synthétique. */ function segmentedEma( values: ReadonlyArray<number | null>, period: number, ): Array<number | null> { const n = Math.max(1, Math.floor(period)); const alpha = 2 / (n + 1); const out: Array<number | null> = new Array<number | null>(values.length).fill(null); let count = 0; let sum = 0; let ema = 0; for (let i = 0; i < values.length; i++) { const v = values[i]; if (v === null) { count = 0; sum = 0; continue; } if (count < n) { sum += v; count += 1; if (count < n) continue; ema = sum / n; } else { ema += alpha * (v - ema); } out[i] = ema; } return out; } /** Money Flow Volume de Chaikin pour UNE barre : MFM = ((close − low) − (high − close)) / (high − low) ∈ [−1, +1] MFV = MFM × volume MFM est la position du close dans le range, recentrée : +1 = close au plus haut (toute la barre a été achetée), −1 = close au plus bas. Range NUL (high = low) → `null` : le multiplicateur est un 0/0, la barre ne porte aucune information directionnelle. Les appelants décident quoi en faire — cumul → contribution 0 (cf. en-tête), non cumulé → pas de point. / function moneyFlowVolume(bar: FootprintBar): number | null { const range = bar.high - bar.low; if (!(range > 0)) return null; const mfm = (bar.close - bar.low - (bar.high - bar.close)) / range; return mfm * bar.totalVolume; } /** Ligne A/D (Chaikin) : cumul du Money Flow Volume depuis la première barre chargée. Barre à range nul → contribution 0 (exception documentée en en-tête : un accumulateur ne peut pas porter de trou). CONTINU — pas de reset session (l'A/D est une ligne d'accumulation de long terme). */ function accumDistValues(bars: readonly FootprintBar[]): number[] { const out: number[] = new Array<number>(bars.length); let running = 0; for (let i = 0; i < bars.length; i++) { running += moneyFlowVolume(bars[i]) ?? 0; out[i] = running; } return out; } /** VWMA N sur une fenêtre glissante : Σ(close·volume) / Σ(volume). Σvolume = 0 sur la fenêtre (barres placeholder sans trade) → null : 0/0 n'est pas « 0 ». Warm-up (< N barres) → null. Même formule que `vwma` (averages2.ts) — redéclarée ici plutôt que d'élargir la surface publique d'un autre fichier de famille (sa primitive n'est pas exportée). */ function vwmaSeries( bars: readonly FootprintBar[], period: number, ): Array<number | null> { const n = Math.max(1, Math.floor(period)); const out: Array<number | null> = new Array<number | null>(bars.length).fill(null); let sumPV = 0; let sumV = 0; for (let i = 0; i < bars.length; i++) { sumPV += bars[i].close * bars[i].totalVolume; sumV += bars[i].totalVolume; if (i >= n) { sumPV -= bars[i - n].close * bars[i - n].totalVolume; sumV -= bars[i - n].totalVolume; } if (i >= n - 1) out[i] = sumV > 0 ? sumPV / sumV : null; } return out; } /** Indices de volume de Fosback/Dysart (NVI / PVI) — même moteur, deux déclencheurs : index[0] = base index[i] = index[i−1] × (1 + (close[i] − close[i−1]) / close[i−1]) si `trigger(volume[i], volume[i−1])`, sinon index[i−1] NVI : trigger = volume EN BAISSE (Fosback — l'« argent malin » se positionne les jours calmes). PVI : trigger = volume EN HAUSSE (la foule). Volume ÉGAL → aucun des deux ne bouge (inégalités STRICTES, convention d'origine : une journée sans changement de participation n'est ni l'un ni l'autre). close[i−1] = 0 → contribution 0, l'indice est GELÉ (exception documentée en en-tête : le ratio n'existe pas, et un null perdrait l'ancrage de toute la suite de l'indice — un indice base 1000 sans ancrage ne vaut rien). / function volumeIndexValues( bars: readonly FootprintBar[], base: number, trigger: (vol: number, prevVol: number) => boolean, ): number[] { const out: number[] = new Array<number>(bars.length); let idx = base; for (let i = 0; i < bars.length; i++) { if (i > 0) { const prev = bars[i - 1]; const prevClose = prev.close; if (trigger(bars[i].totalVolume, prev.totalVolume) && prevClose !== 0) { idx *= 1 + (bars[i].close - prevClose) / prevClose; } } out[i] = idx; } return out; } // ── Defs ──────────────────────────────────────────────────────────────────── /** OBV — On Balance Volume (Granville, 1963) : OBV[0] = 0 (graine, cf. convention) OBV[i] = OBV[i−1] + volume[i] si close[i] > close[i−1] = OBV[i−1] − volume[i] si close[i] < close[i−1] = OBV[i−1] si close[i] = close[i−1] POURQUOI : la thèse de Granville est que le volume précède le prix. L'OBV signe le volume ENTIER de la barre par la direction du close — sa PENTE et ses divergences avec le prix sont l'information, jamais son niveau absolu. Convention GRAINE : OBV[0] = 0. L'indicateur n'a pas d'origine absolue (Granville lui-même part d'un nombre arbitraire) ; ancrer à 0 sur la première barre CHARGÉE rend la série reproductible et lisible. Corollaire assumé : charger plus d'historique décale toute la courbe — c'est vrai de tout OBV, ATAS et TradingView compris. Convention ÉGALITÉ : close inchangé → OBV inchangé (règle de Granville, inégalités STRICTES — pas de « ≥ » qui compterait les dojis comme haussiers). CONTINU : pas de reset de session — l'ancrage 17:00 CT ne concerne que les cumuls orderflow type CVD, l'OBV est une accumulation de long terme.

Как его читать

  • Гранвилл утверждал, что объём опережает цену; как бы вы к этому ни относились, полезное содержание — это наклон линии и её расхождения с ценой, но никогда не уровень.
  • Серия растущих минимумов OBV при плоской цене означает, что на этом отрезке закрытия вниз приходили на меньшем объёме, чем закрытия вверх.
  • Когда цена обновляет максимум, а OBV — нет, закрытия вверх за этим максимумом несли меньше объёма, чем предыдущие: это факт об этих барах, а не сигнал.
  • Поскольку загрузка большей истории сдвигает всю кривую, не стройте ничего на пороговом значении OBV — стройте на форме.
  • На внутридневных фьючерсах сравнивайте OBV с накопленной дельтой: OBV берёт объём со знаком по направлению закрытия, дельта — по стороне агрессора, и расходятся они ровно там, где было поглощение.

Параметры и значения по умолчанию

Сглаживание по умолчанию 1 — сырая накопительная линия остаётся нетронутой; значения до 200 применяют дополнительное сглаживание, которое облегчает чтение наклона на длинном горизонте, но прячет отдельные бары, его создавшие. Цвет по умолчанию синий (#2962ff).

On Balance Volume — параметры, доступные в приложении, со значениями по умолчанию.
ПараметрТипПо умолчаниюДиапазон
Smoothingnumber11 – 200

Чего он не показывает

OBV работает по принципу «всё или ничего»: продвижение на один тик и большое движение вносят одинаковый вклад, из-за чего линия дёргается на инструментах, часто закрывающихся около прежней цены. Он использует только закрытие, поэтому всё, что происходило внутри бара, остаётся невидимым, включая поглощение и сторону агрессора. Нулевое начальное значение делает абсолютные величины бессмысленными и несравнимыми между графиками и глубинами истории — это свойство любой реализации OBV, включая ATAS и TradingView. Равные закрытия замораживают линию, поэтому сильно округлённые данные или данные с низким разрешением цены уплощают её по чисто механическим причинам.

Все индикаторы

Эта страница на других языках

Frequently asked questions

Как рассчитывается On Balance Volume?
От начального значения к сумме прибавляется весь объём бара, если его закрытие выше предыдущего, и вычитается, если ниже. При равных закрытиях сумма не меняется. В Senzoukria начальное значение равно 0 на первом баре, загруженном в график.
Сбрасывается ли On Balance Volume на открытии сессии?
Нет. OBV идёт непрерывно через сессии, поскольку Гранвилл задумывал его как накопление на длинном горизонте. Сессионный якорь 17:00 CT в приложении относится к сессионным накоплениям — накопленной дельте или объёму сессии, — но не к OBV.
Чем OBV отличается от накопленной дельты?
OBV берёт объём бара со знаком по направлению его закрытия, поэтому зависит только от данных OHLC. Накопленная дельта берёт объём со знаком по стороне агрессора каждой сделки и требует классификации по bid/ask из потока данных. Они расходятся там, где агрессивный объём не сдвинул закрытие, — это подпись поглощения.

Keep reading