OBV Oscillator: Rolling Volume Residual and Pine Script | Pineify
This OBV oscillator subtracts an EMA from a rolling sum of signed volume. Its zero line compares that sum with its own smoothed history. A positive output does not mean the sum itself is positive, and neither value identifies buyers, sellers, or institutional activity.
Inspect the Pine ScriptThe actual calculation
For valid price observations, define q as current volume when the selected source rises, negative volume when it falls, and zero when it is unchanged. Let S be the sum of the latest N contributions. The EMA E uses the same N, with alpha = 2/(N + 1). The plotted oscillator is O = S − E.
This is not cumulative OBV minus its EMA. On complete observations, S equals cumulative OBV minus its value N bars earlier. Subtracting E then adds another transformation. Different scripts called OBV Oscillator may use different definitions, so compare the source before comparing values.
The copy below fixes source to close and N to 20. There are no input controls. It draws one opaque teal line and a dashed zero reference. It contains no divergence detector, markers, alert conditions, trade orders, or timeframe requests.
TradingView documents the original OBV calculation and Granville’s 1963 publication. That history does not establish that this particular rolling-sum oscillator was invented in 1963.
A positive oscillator with a negative signed-volume sum
Use N = 3 for a short hypothetical calculation. Begin with a settled sequence of contributions −100, so S and E both equal −300 and O is zero. Each new contribution displaces the oldest one; alpha is 0.5. These are invented volume units, not market observations.
| Step | New q | Removed q | S | E | O |
|---|---|---|---|---|---|
| 1 | 20 | -100 | -180 | -240 | 60 |
| 2 | -80 | -100 | -160 | -200 | 40 |
| 3 | -120 | -100 | -180 | -190 | 10 |
| 4 | -20 | 20 | -220 | -205 | -15 |
At step 1, O is +60 although the rolling sum is −180. At step 2, the new contribution is negative, yet S rises because an even more negative observation leaves the window. O remains positive at +40. Neither result proves net buying or an increase in current price.
For complete windows, S[t] − S[t−1] = q[t] − q[t−N]. This expiry effect explains why watching only the newest volume bar is insufficient. At step 4, O crosses below zero because S falls below E; S was already negative on every row.
Zero-centered does not mean bounded or guaranteed to revert
The output retains volume units. Scaling every volume observation by a positive factor scales S, E, and O by that factor on matching history and initialization. There is no fixed upper or lower bound comparable to RSI or MFI. A reading of 500 on two feeds is not automatically comparable.
If every contribution is +100 for a settled complete window, S becomes 100 × N and O approaches zero while cumulative OBV continues increasing by 100 each bar. Conversely, if S keeps rising at a constant rate, an EMA can retain a persistent lag and O can stay positive. Subtracting an EMA removes a constant component; it does not prove a stochastic mean-reverting process or require frequent zero crossings.
At N = 1, EMA(S, 1) equals S wherever valid, so O is zero. A shorter length therefore does not universally increase crossover frequency. Changing N changes both the rolling sum and EMA, not just a smoothing knob. Twenty daily bars are twenty sessions or observations, not a fixed calendar month.
Missing volume and live-bar behavior
The exact source uses zero times volume on an equal-price bar. With missing volume, that expression remains missing rather than becoming a fabricated zero. Pine’s math.sum ignores missing source observations and counts N non-missing values, so a window can span more than N chart bars. The helper has no explicit validity flag, no feed-quality warning, and no custom reset rule.
A missing price comparison takes the fallback branch; with valid volume, that produces zero. Thus the script does not strictly reject every invalid price pair. Decide whether to preserve these built-in semantics or require consecutive valid observations before adapting it.
Reported zero volume is a numeric observation. A zero-filled feed can yield zero values without proving an absence of trading pressure. Tick volume counts price updates rather than traded quantity. Confirm symbol, venue, session, and volume units against TradingView’s volume-data limitations.
Current price direction and accumulated volume change during an open bar. If price crosses the previous close, its entire current volume contribution can change sign. The plotted line is live; the script has no bar-close gate. Read the math.sum reference and repainting documentation when defining an adaptation.
Make divergence and timeframe rules explicit
A value above zero is a state. Crossing above zero is an event that also depends on the previous value. A change in slope is a third condition. The example only plots O; none of these events creates an alert automatically.
For divergence, specify whether price pivots select the comparison bars or whether oscillator pivots do. Set left and right confirmation bars, acceptable pivot spacing, equality rules, and the earliest bar on which the signal can be known. A pivot confirmed several bars later cannot be treated as an entry known on the earlier swing bar.
Two calls with lengths 20 and 14 still run on the same chart timeframe. They do not produce daily and hourly data by themselves. A multi-timeframe adaptation needs explicit data requests and a documented policy for the unclosed higher-timeframe bar.
Additional volume or trend filters are hypotheses to test, not established improvements in win rate. Define entries, exits, sizing, fees and slippage; separate the parameter-selection sample from later evaluation. No reliable signal frequency, reversal percentage, or preferred market is established by this formula.
Compare the information each volume indicator uses
OBV accumulates signed volume without the rolling subtraction. This oscillator measures a rolling sum relative to its EMA. MFI uses typical-price direction and price-times-volume flows to form a bounded ratio when its inputs and denominators are valid.
These are different transformations of price and reported volume, not independent observations of who traded. Agreement between them does not establish causation or a performance ranking. Check calculations on the same feed before adding trading interpretations.
Pine Script v6: implementation and limits
The complete source below preserves the local helper and its fixed length. Paste it into a new TradingView indicator and inspect the values. The label-count allowance does not create labels; no labels or alerts are implemented.
// 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="On Balance Volume Oscillator", overlay=false, max_labels_count=500)
p_ta_obv_oscillator(series float src, simple int length) =>
change = ta.change(src)
obv = math.sum(change > 0 ? volume : change < 0 ? -volume : 0*volume, length)
obv_osc = obv - ta.ema(obv, length)
obv_osc
p_ind_1 = p_ta_obv_oscillator(close, 20) // OBV Oscillator
plot(p_ind_1, title="OBV Oscillator", color=color.rgb(0, 188, 212, 0), linewidth=2)
hline(0, "Zero Line", color=color.gray, linestyle=hline.style_dashed)
Specify a checkable adaptation in Pineify
Ask for an explicit rolling signed-volume sum minus its EMA, then specify length, source, missing-data policy, and whether signals require a closed bar. Keep S, E, and O visible while checking the four-row synthetic example. If adding divergence or another timeframe, define confirmation timing before testing a trading rule.
Open Pine Script AI Coding AgentEducational indicator analysis. No measured trading performance is established here. Syntax validation does not prove formula equivalence, execution assumptions or profitability.