How to Create a Volume Oscillator on TradingView
Measure shifts in participation by comparing short and long volume averages.
The Volume Oscillator highlights when short-term volume activity diverges from its longer baseline. This tutorial covers the exact formula, how traders read crosses and divergences, and a Pine Script v5 implementation using ta.sma on volume.
What Is the Volume Oscillator?
The Volume Oscillator (VO) is the difference between two simple (or sometimes exponential) moving averages of volume:
VO = MA_fast(volume) − MA_slow(volume)
It oscillates around zero: positive readings mean recent volume is hotter than the longer average; negative readings suggest cooling participation.
It is not a directional price indicator by itself—it measures activity, which you interpret alongside price structure.
Why Traders Use This Indicator
- Spots expansion and contraction phases in trading activity.
- Helps validate breakouts when volume oscillator rises with price.
- Surfaces potential divergences when price makes extremes without volume support.
- Simple to code with two ta.sma calls on volume.
- Complements price-only oscillators like RSI or MACD.
| Parameter | Default | Description |
|---|---|---|
| Fast length | 5 | Short lookback for responsive volume average; common choices 5–14. |
| Slow length | 10 | Longer smoothing baseline; often 10–30 depending on timeframe. |
| MA type | SMA | SMA is textbook for VO; EMA variants react faster. |
| Source | volume | Use raw volume; for spread assets verify volume availability. |
How the Volume Oscillator Is Computed
Step 1: Compute fastVol = SMA(volume, fastLen).
Step 2: Compute slowVol = SMA(volume, slowLen).
Step 3: VO = fastVol − slowVol.
Plot VO as a line or histogram centered on zero. Optional normalization (percent of slow) exists in some platforms—state your variant when comparing results.
Signal Interpretation
Zero-line cross: Fast volume MA crossing above slow hints at building participation; crossing below hints at fade.
Trend confirmation: Rising prices with a rising VO suggest conviction; rising prices with falling VO warns of weak thrust.
Divergence: Price higher high with VO lower high can foreshadow exhaustion—confirm with structure breaks, never in isolation.
Combining with Other Indicators
Pair with OBV or Accumulation/Distribution to blend activity with directional flow estimates.
Use alongside VWAP to see whether volume surges align with accepted value areas.
The Hard Way: Writing Pine Script Manually
Challenges of Manual Coding
Choosing lengths that are not redundant (fast must stay below slow).
Handling symbols with sporadic zero-volume bars.
Deciding between histogram vs line for crowded indicator panes.
Avoiding overfitting fast/slow pairs without out-of-sample checks.
Separating genuine divergence rules from hindsight bias in prose.
// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Pineify — Volume Oscillator (dual SMA)
//@version=5
indicator("Volume Oscillator (SMA)", shorttitle="Vol Osc")
fastLen = input.int(5, "Fast SMA length", minval=1)
slowLen = input.int(10, "Slow SMA length", minval=2)
useHist = input.bool(true, "Plot as histogram")
normalize = input.bool(false, "Normalize as % of slow SMA")
fastVol = ta.sma(volume, fastLen)
slowVol = ta.sma(volume, slowLen)
vo = fastVol - slowVol
voPct = slowVol != 0 ? (vo / slowVol) * 100.0 : 0.0
plotVal = normalize ? voPct : vo
plot(useHist ? na : plotVal, color=color.new(color.blue, 0), title="VO line", linewidth=2)
plot(useHist ? plotVal : na, style=plot.style_histogram, color=plotVal >= 0 ? color.new(color.teal, 30) : color.new(color.orange, 30), title="VO hist")
hline(0, "Zero", color=color.new(color.gray, 40))
bullCross = ta.crossover(vo, 0)
bearCross = ta.crossunder(vo, 0)
plotshape(bullCross, title="VO > 0 cross", style=shape.triangleup, location=location.bottom, size=size.tiny, color=color.new(color.green, 0))
plotshape(bearCross, title="VO < 0 cross", style=shape.triangledown, location=location.top, size=size.tiny, color=color.new(color.red, 0))
// Smoothed VO for slower visuals
voSmooth = ta.ema(vo, 3)
plot(voSmooth, color=color.new(color.purple, 40), title="VO EMA3", linewidth=1)
// Simple divergence hint: price new 20-bar high but VO not (educational)
priceHigh = ta.highest(high, 20)
voHigh = ta.highest(vo, 20)
newPriceHigh = high >= priceHigh[1]
weakVo = vo < voHigh[1]
divHint = newPriceHigh and weakVo and vo < 0
plotshape(divHint, title="Bearish vol div hint", style=shape.xcross, location=location.top, size=size.tiny, color=color.new(color.red, 50))
var table vt = table.new(position.bottom_right, 2, 5, border_width=1)
if barstate.islast
table.cell(vt, 0, 0, "Fast SMA", text_color=color.white, bgcolor=color.new(color.gray, 60))
table.cell(vt, 1, 0, str.tostring(fastVol, format.volume), text_color=color.white, bgcolor=color.new(color.gray, 60))
table.cell(vt, 0, 1, "Slow SMA", text_color=color.white, bgcolor=color.new(color.gray, 70))
table.cell(vt, 1, 1, str.tostring(slowVol, format.volume), text_color=color.white, bgcolor=color.new(color.gray, 70))
table.cell(vt, 0, 2, "VO", text_color=color.white, bgcolor=color.new(color.gray, 60))
table.cell(vt, 1, 2, str.tostring(vo, format.volume), text_color=color.white, bgcolor=color.new(color.gray, 60))
table.cell(vt, 0, 3, "VO %", text_color=color.white, bgcolor=color.new(color.gray, 70))
table.cell(vt, 1, 3, str.tostring(voPct, "#.##") + "%", text_color=color.white, bgcolor=color.new(color.gray, 70))
table.cell(vt, 0, 4, "Bias", text_color=color.white, bgcolor=color.new(color.gray, 60))
bias = vo > 0 ? "Expansion" : vo < 0 ? "Contraction" : "Neutral"
table.cell(vt, 1, 4, bias, text_color=color.white, bgcolor=color.new(color.gray, 60))
alertcondition(bullCross, title="VO crosses above zero", message="Volume oscillator crossed above zero")
alertcondition(bearCross, title="VO crosses below zero", message="Volume oscillator crossed below zero")Maintenance Note: On instruments with partial or synthetic volume, VO quality degrades—disclose data limitations in published scripts. If you add normalization, update tooltips so users know they are viewing percent, not raw delta.
The Easy Way: Build with Pineify Visual Editor
What if you could create the same indicator without writing a single line of code? Pineify is a visual editor designed for TradingView users who want professional-grade indicators through an intuitive interface.
| Feature | Manual Pine Script | Pineify Visual Editor |
|---|---|---|
| Parameter safety | Fast ≥ slow mistakes slip through without validation. | Input constraints and defaults stay synchronized. |
| Plot clutter | Multiple MAs and histograms fight for scale. | Layout presets keep zero-line oscillators readable. |
| Divergence logic | Easy to hardcode fragile peeking rules. | Encourages reusable, testable divergence helpers. |
| Alerts | Cross alerts need manual tuning vs noise. | Optional smoothing or confirm bars configurable in UI. |
| Documentation | Users confuse raw VO with percent VO. | Generated titles/descriptions clarify the variant. |
Switch SMA/EMA variants without rewriting the whole script.
Faster A/B tests on fast/slow pairs for research posts.
Cleaner exports when teaching volume confirmation workflows.
Integrated labeling for SEO-friendly TradingView publications.
Less boilerplate for tables, alerts, and zero lines.
Step-by-Step Tutorial
Pick fast and slow lengths
Ensure fast < slow; document your defaults for each timeframe.
Compute SMAs on volume
Use ta.sma(volume, len) twice.
Subtract to get VO
vo = fastVol - slowVol; optionally normalize.
Plot histogram or line
Color bars above/below zero for quick scanning.
Add zero line and markers
plotshape on ta.crossover/crossunder against zero.
Optional divergence layer
Implement strict swing comparisons—not single-bar hacks.
Publish alerts
Expose crosses and optional divergence confirmations.
Trading Strategies & Pro Tips
Breakout confirmation
Demand VO > 0 when price closes beyond a range high to accept breakout risk.
Pro Tip: Pair with a minimum absolute volume filter on illiquid names.
Pullback participation
In uptrends, look for VO turning positive as price leaves a shallow dip.
Divergence fade
When price grinds higher but VO trends down, tighten stops on longs.
Event study baseline
Mark earnings or macro prints and compare VO behavior vs median days.
Common Mistakes to Avoid
- Setting fast length greater than or equal to slow.
- Treating VO direction as automatic price direction.
- Ignoring asset-specific volume quality (FX vs equities).
- Overloading the pane with cumulative volume by mistake.
- Calling single-bar divergences without swing definitions.
Frequently Asked Questions
Ship volume tools faster with Pineify
- Generate VO variants and normalization toggles from one spec.
- Bundle alerts and tables without repetitive typing.
- Keep research notes and code definitions aligned.
Disclaimer: Trading involves significant risk. The indicators, code, and strategies provided are for educational purposes only and do not constitute financial advice. Past performance is not indicative of future results.