Indicador On Balance Volume: fórmula, ajustes y cómo leerlo
On Balance Volume, publicado por Joseph Granville en 1963, suma todo el volumen de una barra a un total acumulado cuando el cierre sube y lo resta cuando el cierre baja. Un cierre sin cambio deja el total exactamente donde estaba.
Senzoukria · Indicadores · Updated September 2026
On Balance Volume — Volumen, en su propio panel debajo del gráfico.
Qué mide On Balance Volume
La dirección se decide por comparación estricta de cierres consecutivos: mayor suma, menor resta, igual no hace nada, así que las barras doji nunca cuentan como alcistas. El volumen de cada barra se firma íntegro: el indicador no distingue entre un cierre un tick más arriba y uno treinta ticks más arriba. Senzoukria inicializa la serie en 0 en la primera barra cargada, lo que hace reproducible la línea dibujada y deja su nivel absoluto sin significado. La acumulación es continua y no se reinicia en la apertura de las 17:00 CT, un anclaje reservado a las medidas de sesión como el delta acumulado.
La fórmula, tal como está implementada
No es la definición habitual en otros sitios: es lo que calcula el código que se distribuye, documentado junto a su implementación:
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.
Cómo leerlo
- La tesis de Granville era que el volumen precede al precio; se comparta o no, el contenido utilizable es la pendiente de la línea y sus desacuerdos con el precio, nunca su nivel.
- Una serie de mínimos crecientes en el OBV bajo un precio plano significa que los cierres bajistas llegaron con menos volumen que los alcistas a lo largo de ese tramo.
- Cuando el precio hace un máximo nuevo y el OBV no, los cierres alcistas detrás de ese máximo cargaron menos volumen que los anteriores: un hecho sobre esas barras, no una señal.
- Como cargar más historial desplaza la curva entera, no construyas nada sobre un umbral de OBV; construye sobre la forma.
- En futuros intradía, compara el OBV con el delta acumulado: el OBV firma el volumen por la dirección del cierre, el delta lo firma por el agresor, y discrepan justo donde hubo absorción.
Parámetros y valores por defecto
El suavizado está en 1 por defecto y deja intacta la línea acumulada bruta; valores hasta 200 aplican una pasada de suavizado posterior que facilita ver la pendiente de largo plazo mientras oculta las barras individuales que la produjeron. El azul (#2962ff) es el color por defecto.
| Parámetro | Tipo | Por defecto | Rango |
|---|---|---|---|
| Smoothing | number | 1 | 1 – 200 |
Lo que no muestra
El OBV es todo o nada: un avance de un tick y uno grande aportan lo mismo, lo que vuelve la línea nerviosa en instrumentos que cierran a menudo cerca de sin cambios. Usa solo el cierre, así que todo lo ocurrido dentro de la barra queda invisible, incluidas la absorción y el lado agresor. El inicio en cero deja los valores absolutos sin significado e incomparables entre gráficos o profundidades de historial: una propiedad compartida por toda implementación de OBV, incluidas las de ATAS y TradingView. Los cierres iguales congelan la línea, así que unos datos de precio muy redondeados o de baja resolución la aplanan por motivos mecánicos.
Indicadores relacionados
- Volume — Volumen
- Buy/Sell Volume — Volumen
- Average Trade Size — Volumen
- Volume ROC % — Volumen
- Relative Volume — Volumen
- Trade Count — Volumen
Esta página en otros idiomas
Frequently asked questions
- ¿Cómo se calcula el On Balance Volume?
- Partiendo de un valor inicial, se suma todo el volumen de cada barra cuando su cierre está por encima del cierre anterior y se resta cuando está por debajo. Cuando ambos cierres son iguales el total no cambia. En Senzoukria el valor inicial es 0 en la primera barra cargada en el gráfico.
- ¿El On Balance Volume se reinicia en la apertura de sesión?
- No. El OBV corre de forma continua entre sesiones, ya que Granville lo diseñó como una acumulación de largo plazo. El anclaje de sesión de las 17:00 CT de la aplicación se aplica a acumulaciones de sesión como el delta acumulado o el volumen de sesión, no al OBV.
- ¿Cuál es la diferencia entre el OBV y el delta acumulado?
- El OBV firma el volumen de una barra por la dirección de su cierre, así que depende solo de los datos OHLC. El delta acumulado firma el volumen por el lado agresor de cada operación y necesita clasificación bid/ask de la fuente. Divergen donde el volumen agresivo no movió el cierre, que es la firma de la absorción.