Indicator TutorialPine Script v5

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.
ParameterDefaultDescription
Fast length5Short lookback for responsive volume average; common choices 5–14.
Slow length10Longer smoothing baseline; often 10–30 depending on timeframe.
MA typeSMASMA is textbook for VO; EMA variants react faster.
SourcevolumeUse 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.

Pine Script v5
// 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.

FeatureManual Pine ScriptPineify Visual Editor
Parameter safetyFast ≥ slow mistakes slip through without validation.Input constraints and defaults stay synchronized.
Plot clutterMultiple MAs and histograms fight for scale.Layout presets keep zero-line oscillators readable.
Divergence logicEasy to hardcode fragile peeking rules.Encourages reusable, testable divergence helpers.
AlertsCross alerts need manual tuning vs noise.Optional smoothing or confirm bars configurable in UI.
DocumentationUsers 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

1

Pick fast and slow lengths

Ensure fast < slow; document your defaults for each timeframe.

2

Compute SMAs on volume

Use ta.sma(volume, len) twice.

3

Subtract to get VO

vo = fastVol - slowVol; optionally normalize.

4

Plot histogram or line

Color bars above/below zero for quick scanning.

5

Add zero line and markers

plotshape on ta.crossover/crossunder against zero.

6

Optional divergence layer

Implement strict swing comparisons—not single-bar hacks.

7

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.