Choosing an OBV indicator on TradingView
The best OBV indicator for your workflow depends on whether you need the standard volume calculation, a smoothing line, or explicit divergence alerts. OBV adds volume when the close rises, subtracts it when the close falls, and leaves the total unchanged on equal closes. It does not identify institutional trades or guarantee a reversal.
Use the calculation below to check your chart, then inspect a complete Pine Script v6 example with price-pivot divergence and alerts that wait for the confirmation bar to close.
Educational example. Not investment advice or evidence of profitable trading.
Choose the features you need
| Setup | Useful for | Check before using |
|---|---|---|
| TradingView built-in OBV | A baseline for the standard formula and available smoothing settings. | Select On Balance Volume from the Indicators search and verify that the symbol has volume. |
| OBV with an EMA | Comparing the cumulative series with a smoother reference. | Length and lag. Smoothing does not remove false signals or repair missing data. |
| Custom divergence indicator | Defining exactly which swings are compared and when an alert becomes available. | Price versus OBV pivots, right-side confirmation delay, marker offsets and alert frequency. |
For a community script, inspect its source where available and compare its documented rules with the chart. A historical marker drawn on a swing bar may have appeared several bars later. This page provides a reproducible implementation, not a ranking of community scripts by returns.
OBV calculation, one bar at a time
Each bar contributes its entire volume with a sign determined by the current close versus the previous close. The size of the price move does not change that contribution. These are the three cases in TradingView's OBV definition.
Higher close: OBV = previous OBV + volume
Lower close: OBV = previous OBV - volume
Equal close: OBV = previous OBV
This synthetic five-bar example sets bar 0 to zero. It is an arithmetic demonstration, not market data or a backtest. The 50,000 units on the starting bar establish context and are not added to this chosen baseline.
| Bar | Close | Volume | Calculation | OBV |
|---|---|---|---|---|
| 0 | 100.00 | 50,000 | Set example baseline | 0 |
| 1 | 102.50 | 120,000 | 0 + 120,000 | 120,000 |
| 2 | 101.80 | 45,000 | 120,000 - 45,000 | 75,000 |
| 3 | 101.80 | 30,000 | Equal close: no change | 75,000 |
| 4 | 103.20 | 210,000 | 75,000 + 210,000 | 285,000 |
Bar 3 has the same close as bar 2, so its 30,000 units do not enter OBV. An equal close is not necessarily a doji: a doji compares open and close within one bar. The final total is 285,000 relative to our starting point; it is not a universal bullish threshold.
Divergence rules and confirmation timing
This example finds pivots in price and samples OBV on those same bars. It compares consecutive confirmed lows with lows, and highs with highs, subject to the configured distance range. An implementation based on OBV pivots can select different bars and therefore produce different signals.
Bullish comparison
The new price pivot low is lower, while OBV at that pivot is higher than at the previous price low.
Bearish comparison
The new price pivot high is higher, while OBV at that pivot is lower than at the previous price high.
For example, suppose confirmed price lows occur at bars 100 and 120: price falls from 100 to 98, while OBV rises from 75,000 to 90,000. With five right-side bars and a permitted pivot distance of 5 to 60 bars, the second comparison becomes available at the close of bar 125. The bullish marker belongs on bar 125, not bar 120. This is a hypothetical timing example, not a recorded signal.
The code checks barstate.isconfirmed and uses no negative plotting offset. Waiting for confirmation adds delay. The live OBV and EMA can still move during an open bar, and historical data revisions remain possible. TradingView's repainting documentation explains both live-bar changes and the misleading effect of plotting pivots in the past.
A divergence does not establish that a trend has ended. If you test an additional price-break or trend filter, define its lookback and confirmation rule before reviewing the results. Record missed moves as well as false signals.
Volume data limits come before settings
TradingView displays the volume it receives from the provider. Some symbols have no volume; some provide tick volume, which counts price updates. Availability can depend on the timeframe. Check the exact exchange or broker prefix, instrument, session and volume units before interpreting OBV. See TradingView's volume-data limitations.
- Stocks and futures: confirm which venue and session the feed covers. Do not assume every stock chart contains all market transactions.
- Forex and CFDs: tick volume is not traded currency volume. A futures chart is a different instrument, so substituting it changes the analysis.
- Crypto: keep the exchange, pair and contract type consistent. A spot pair, perpetual contract and index are not interchangeable datasets.
The script stops if it encounters missing volume and reports an error if the loaded dataset has no nonzero volume. This catches absent data; it cannot establish the quality or coverage of a feed. Do not replace missing values with invented volume to obtain a signal.
OBV Pine Script v6 with confirmed divergence alerts
The example plots ta.obv and an optional EMA. Twenty bars is an editable default, not an optimized length. Shorter averages react faster; longer averages respond later. The divergence calculation uses raw OBV, so changing EMA length affects the line and crossover alerts but not the divergence comparisons.
Paste into a new indicator in the TradingView Pine Editor.
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Pineify
//@version=6
indicator(title="OBV with Confirmed Price-Pivot Divergence [Pineify]", shorttitle="Pineify OBV", overlay=false, precision=0, max_bars_back=500)
// Inputs
group_obv = "OBV Smoothing"
smoothLength = input.int(20, title="EMA Smoothing Length", minval=1, group=group_obv, tooltip="Period for the exponential moving average applied to the OBV line.")
showEma = input.bool(true, title="Display EMA Smoothing Line", group=group_obv)
group_div = "Divergence Detection"
showDiv = input.bool(true, title="Enable Divergence Signals", group=group_div)
pivotLeft = input.int(5, title="Pivot Lookback Left", minval=1, maxval=50, group=group_div, tooltip="Number of bars to the left required to confirm a swing high/low.")
pivotRight = input.int(5, title="Pivot Lookback Right", minval=1, maxval=50, group=group_div, tooltip="Number of bars to the right required to confirm a swing high/low.")
minLookback = input.int(5, title="Min Divergence Distance", minval=1, group=group_div, tooltip="Minimum bar distance between two consecutive pivots.")
maxLookback = input.int(60, title="Max Divergence Distance", minval=5, maxval=200, group=group_div, tooltip="Maximum bar distance between two consecutive pivots.")
group_style = "Style & Colors"
colorObvUp = input.color(#089981, title="OBV Above EMA", group=group_style)
colorObvDown = input.color(#f23645, title="OBV Below EMA", group=group_style)
colorEma = input.color(#ff9800, title="EMA Signal Line", group=group_style)
colorBull = input.color(#089981, title="Bullish Divergence", group=group_style)
colorBear = input.color(#f23645, title="Bearish Divergence", group=group_style)
// Validate inputs and require a volume feed.
if minLookback > maxLookback
runtime.error("Minimum divergence distance must not exceed maximum distance.")
if na(volume)
runtime.error("Volume is missing on this feed. Choose a symbol with volume data.")
var float totalVolume = 0.0
totalVolume += nz(volume)
if barstate.islast and totalVolume == 0
runtime.error("No nonzero volume is available in this dataset.")
// OBV Calculation
obvValue = ta.obv
obvEma = ta.ema(obvValue, smoothLength)
// Dynamic OBV line color
obvColor = showEma ? (obvValue >= obvEma ? colorObvUp : colorObvDown) : #2962ff
plot(obvValue, title="OBV", color=obvColor, linewidth=2)
plot(showEma ? obvEma : na, title="OBV EMA", color=colorEma, linewidth=1)
// Pivot Detection
plFound = not na(ta.pivotlow(low, pivotLeft, pivotRight))
phFound = not na(ta.pivothigh(high, pivotLeft, pivotRight))
// Divergence state memory
var float prevLowPrice = na
var float prevLowObv = na
var int prevLowBar = na
var float prevHighPrice = na
var float prevHighObv = na
var int prevHighBar = na
bullDivergence = false
bearDivergence = false
// Regular Bullish Divergence: Price makes a Lower Low, OBV makes a Higher Low
if plFound and showDiv and barstate.isconfirmed
currLowPrice = low[pivotRight]
currLowObv = obvValue[pivotRight]
currLowBar = bar_index - pivotRight
if not na(prevLowPrice) and (currLowBar - prevLowBar >= minLookback) and (currLowBar - prevLowBar <= maxLookback)
if currLowPrice < prevLowPrice and currLowObv > prevLowObv
bullDivergence := true
prevLowPrice := currLowPrice
prevLowObv := currLowObv
prevLowBar := currLowBar
// Regular Bearish Divergence: Price makes a Higher High, OBV makes a Lower High
if phFound and showDiv and barstate.isconfirmed
currHighPrice = high[pivotRight]
currHighObv = obvValue[pivotRight]
currHighBar = bar_index - pivotRight
if not na(prevHighPrice) and (currHighBar - prevHighBar >= minLookback) and (currHighBar - prevHighBar <= maxLookback)
if currHighPrice > prevHighPrice and currHighObv < prevHighObv
bearDivergence := true
prevHighPrice := currHighPrice
prevHighObv := currHighObv
prevHighBar := currHighBar
// Visual Plotting
plotshape(bullDivergence, title="Bullish Divergence", style=shape.triangleup, location=location.bottom, color=colorBull, size=size.small, text="BULL DIV", textcolor=color.white)
plotshape(bearDivergence, title="Bearish Divergence", style=shape.triangledown, location=location.top, color=colorBear, size=size.small, text="BEAR DIV", textcolor=color.white)
// Alert Conditions
alertcondition(bullDivergence, title="OBV Bullish Divergence", message="OBV Bullish Divergence detected on {{ticker}}: Confirmed price-pivot comparison.")
alertcondition(bearDivergence, title="OBV Bearish Divergence", message="OBV Bearish Divergence detected on {{ticker}}: Confirmed price-pivot comparison.")
crossUp = ta.crossover(obvValue, obvEma)
crossDown = ta.crossunder(obvValue, obvEma)
alertcondition(crossUp and barstate.isconfirmed, title="OBV Crosses Above EMA", message="OBV line crossed above its configured EMA on {{ticker}}.")
alertcondition(crossDown and barstate.isconfirmed, title="OBV Crosses Below EMA", message="OBV line crossed below its configured EMA on {{ticker}}.")- Open the TradingView Pine Editor, create an indicator, paste the source, save it and add it to a chart with volume data.
- Use the default five left and five right bars first. Compare each marked confirmation bar with the price pivot five bars earlier and the preceding pivot of the same type.
- Use Create Alert, select this indicator and one of its four named conditions, then choose Once Per Bar Close. The code exposes conditions; it does not create an active alert itself.
- Recreate alerts after changing script inputs or code. Running alerts retain the saved script and input snapshot described in TradingView's alerts documentation.
To audit behavior, use Bar Replay and note the pivot bar, confirmation bar and marker separately; historical markers do not prove that live notifications were delivered. This indicator places no orders. Testing returns requires separate strategy rules for entries, exits, sizing, fees and slippage. No profitability or live-alert delivery test is claimed here.
Build your OBV rules in Pineify
Use the Pine Script AI Coding Agent to draft a variation with explicit pivot, distance and alert rules. For example: "Compare consecutive confirmed price lows with OBV on those bars; require five right-side bars; show markers on confirmation bars; add an optional price EMA filter." Inspect the generated code and test its behavior on your selected feed.
Open Pine Script AI Coding AgentFor a visual workflow, explore the Visual Pine Editor and its available indicator and condition modules. Custom divergence logic may require code beyond a standard OBV module.