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.

This tutorial uses a custom dual-SMA difference. Its optional percentage mode is (fast − slow) / slow × 100. It is not the TFS directional-volume oscillator, and its histogram is the oscillator itself, not PVO minus a signal line. See TradingView’s PVO definition before comparing variants.

Volume activity does not establish price direction or a profitable trading rule.

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 typeSMAThis example uses SMA. TradingView PVO supports SMA or EMA; match the type when comparing.
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: A price high without a matching volume-average high is a descriptive comparison, not an established reversal forecast. Define swing rules before testing divergence.

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.

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")

if fastLen >= slowLen
    runtime.error("Fast length must be smaller than slow length.")
if na(volume)
    runtime.error("This example requires volume on every chart bar.")

fastVol = ta.sma(volume, fastLen)
slowVol = ta.sma(volume, slowLen)
vo = fastVol - slowVol
voPct = not na(slowVol) and slowVol > 0 ? (vo / slowVol) * 100.0 : na
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))

crossUp = ta.crossover(plotVal, 0)
crossDown = ta.crossunder(plotVal, 0)
bullCross = barstate.isconfirmed and crossUp
bearCross = barstate.isconfirmed and crossDown
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(plotVal, 3)
plot(voSmooth, color=color.new(color.purple, 40), title="VO EMA3", linewidth=1)

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, na(voPct) ? "Unavailable" : 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 = na(plotVal) ? "Unavailable" : 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: The script rejects missing volume and fast lengths greater than or equal to slow lengths. A zero slow average produces an unavailable percentage, not zero. The EMA3 line uses the selected plot units. Cross markers and alerts require a closed bar; plotted values update intrabar. Create alerts in TradingView and recreate them after input changes. No divergence detector or trading strategy is included.

Build with Pineify Visual Editor

Pineify lets you configure supported indicators and export Pine Script. Available settings and plan requirements vary by indicator. Compare the generated rules with the example before using its signals.

FeatureManual Pine ScriptPineify Visual Editor
Built-in visual blockThe example below computes a custom dual-SMA difference.The Visual Editor provides Percentage Volume Oscillator with short, long and signal lengths; its formula differs from this raw-volume example.
Exact custom variantInspect and edit the supplied source in Pine Editor.Describe the dual-SMA formula, normalization and confirmed alerts to the AI Coding Agent, then inspect its generated source.

Configure the existing PVO block with short, long and signal lengths.

Use the PVO, Signal and Difference outputs for visual conditions.

For the exact tutorial variant, use custom code and verify its formula rather than substituting the TFS Volume Oscillator.

Step-by-Step Tutorial

1

Choose the intended variant

For the existing visual block, select Percentage Volume Oscillator. The separately named Volume Oscillator is TFS directional volume and does not implement the formula below.

2

Set PVO parameters

Configure Short Length, Long Length and Signal Length. Inspect the exported formula before comparing against this SMA example.

3

Review outputs

PVO, Signal and Difference are separate outputs. The Difference histogram is not the raw fast-minus-slow volume histogram shown here.

4

Use custom code when needed

To reproduce this exact SMA variant, paste the example into TradingView or request that formula through the Pine Script AI Coding Agent. Review and chart-test the result.

Trading Strategies & Pro Tips

Breakout confirmation

Test whether adding VO > 0 to a predefined breakout rule changes results after costs; a volume filter alone does not establish an edge.

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

Define swing points and a comparison window before studying price/volume disagreement. This example does not detect swing divergence.

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

This example chooses SMA. TradingView PVO supports SMA and EMA. Match the formula and lengths before comparing readings.

Yes, whenever the fast volume average is below the slow average.

It will run, but volume semantics change—disclose the chart type in your methodology.

Both can subtract fast and slow averages of volume. Match the MA type, normalization and signal-line calculation; this example plots the oscillator itself as columns rather than oscillator minus signal.

Dividing by the slow volume average removes raw volume units. It does not make feeds or liquidity conditions equivalent; zero denominators remain unavailable.

Exchange-reported volume varies; consider using consistent venues and adjusted totals if available.

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.