TradingView indicator calculations

Volume-adjusted moving average: defining the adjustment

A volume adjusted moving average can mean different calculations. Arms-style VAMA fills a target quantity of volume by walking backward through bars, using only the required fraction of the oldest bar. Its bar count varies. This page implements a causal prior-baseline volume-clock VAMA, with an explicit rolling volume reference, rather than the whole-chart reference used in historical descriptions.

See the decision guide

The name does not identify one formula

The Stocks & Commodities publisher index attributes "Volume-Adjusted Moving Averages" to Richard W. Arms Jr. in March 1990. Public implementations differ in their volume reference and stopping rule. Read those choices before comparing plotted lines; matching a length input is insufficient.

  • The original article is identified through the publisher's index; the complete paid article was not used as a code source. The Pine implementation below is original and its policy is fully specified here.
  • A constant baseline chosen using only an earlier calibration sample can also be causal. A constant selected from the full backtest dataset cannot be treated as information available at every historical bar.
Published descriptionVolume reference and stopping ruleConsequence
Arms-style account in Fidelity's indicator guideMean of all volume in the chart; accumulate the requested volume increments, with a fractional oldest bar.Adding future observations to that reference can change earlier values.
NeuroShell's documented VAMAWhole-chart mean; stop at the volume target OR the selected number of bars, then divide by the volume ratios actually collected.A bar cap may produce a partially filled target. Its manual labels this version unsuitable for strategy use.
allanster's TradingView VAMADocuments input, current and rolling-subset reference modes, plus a strict volume requirement.The author explicitly describes the subset mode as a deviation from Arms' original specification.
LyroRS's Volume MAs Cloud TrendDescribes MA(price × volume) / MA(volume).With an ordinary SMA and equal fixed lengths, this is fixed-window VWMA; there is no volume-target stopping rule.

Define the causal variant before calculating weights

At bar t, let A_t = (v[t−1] + … + v[t−B]) / B. The baseline uses exactly B preceding volumes, including recorded zeros and excluding the current bar. Define increment I_t = f × A_t and target Q_t = N × I_t. The example uses B = 3, f = 0.67 and N = 3. N counts volume increments, not bars.

  • For each calculation, hold Q_t constant and start with remaining volume R = Q_t. For j = t, t−1, …, take u_j = min(v_j, R), assign weight w_j = u_j / Q_t, then reduce R by u_j. Stop when R = 0.
  • VAMA_t = Σ(w_j × p_j). If the oldest included positive-volume bar contributes u_k out of v_k, its included fraction is u_k/v_k. That fraction is different from its normalized price weight u_k/Q_t.
  • Recompute A_t for the next bar. Do not give each historical bar its own past increment size inside the current window: all included volumes are measured against the same current target.
  • This is the prior-baseline volume-clock variant. It changes the whole-chart method to avoid future observations in past calculations. It does not claim numerical identity with Arms' whole-chart curve or another author's implementation.

Reproduce the complete ten-bar fixture

These invented closes and share volumes are a calculation fixture, not market data or a backtest. Use B = 3, N = 3, f = 0.67, scan cap at least 4, and fixed VWMA length 3. Baselines are displayed rounded; target volumes and calculations use unrounded values. "Unavailable" means no complete valid result.

BarCloseVolumePrior mean ATarget QVAMAVWMA(3)
0100100UnavailableUnavailableUnavailableUnavailable
1102100UnavailableUnavailableUnavailableUnavailable
2104100UnavailableUnavailableUnavailable102
3110150100201108.477612106
41200116.666667234.5107.837953107.6
58040083.333333167.58088.181818
6900183.333333368.58080
71000133.3333332688080
81000133.33333326880Unavailable
91001000UnavailableUnavailable100

Audit every included price weight

The table lists bars newest first. Each bracket has one entry per scanned bar, including zero-volume bars. "Span" is calendar-bar count, while the oldest fraction measures how much of the last positive-volume bar is used. Every valid row has normalized weights that sum to one.

  • Bar 3: (110×150 + 104×51)/201 = 108.4776119403. VWMA(3) instead uses bars 3, 2 and 1: (110×150 + 104×100 + 102×100)/350 = 106.
  • Bar 4 contributes no price weight, yet VAMA changes: the prior mean rises and Q becomes 234.5. The extra 33.5 shares come from bar 2 at price 104. (110×150 + 104×84.5)/234.5 = 107.8379530917. Zero current volume does not imply a held value under a rolling baseline.
  • The 400-share shock on bar 5 exceeds Q = 167.5, so only 41.875% of that bar is needed and VAMA equals 80. VWMA(3) still includes bar 3 and equals 88.1818181818. This difference follows from the volume target, not a predicted reversal.
  • Bar 8 reaches back four bars across three recorded zeros. Its fixed three-bar VWMA has no volume. Bar 9 has a positive current volume but a zero prior baseline, so this variant deliberately returns no value.
Output bar / included barsVolume used per barNormalized weightsSpan / oldest fraction
3: [3, 2][150, 51][50/67, 17/67]2 / 51/100
4: [4, 3, 2][0, 150, 84.5][0, 300/469, 169/469]3 / 169/200
5: [5][167.5][1]1 / 67/160
6: [6, 5][0, 368.5][0, 1]2 / 737/800
7: [7, 6, 5][0, 0, 268][0, 0, 1]3 / 67/100
8: [8, 7, 6, 5][0, 0, 0, 268][0, 0, 0, 1]4 / 67/100

Make missing data and the scan limit observable

A zero is known volume; missing volume is unknown. This script never replaces missing volume with zero and never rescales an incomplete volume window to look valid. The strict rejection policy is part of this variant.

ConditionPolicy
Fewer than B preceding barsNo baseline and no VAMA. There is no seed or partial baseline.
Any missing or negative volume in the baselineNo VAMA until a complete valid B-bar baseline exists. Price is not used in the baseline.
All B preceding volumes are zeroTarget is undefined for this indicator; return unavailable rather than divide by zero.
Zero volume encountered during backward scanUse zero weight and continue; the bar still consumes one place in the scan cap.
Missing/negative volume or missing close encountered before the target fillsReject the result, including a missing close on a zero-volume bar. Do not skip unknown bars.
Target exceeds available history or maximum scanned volumeReturn unavailable. With the fixture and cap 1, bars 3 and 4 fail, but bar 5 succeeds.
Current volume exceeds the targetUse a fraction of the current bar; its normalized price weight is one.
Invalid observations older than the filled windowThey do not affect this price sum, unless their volume lies in the separate baseline window.

Pine Script v6 with bounded history and fixture mode

Paste this complete indicator into the Pine Editor. Defaults match the fixture. Enable Synthetic ten-bar fixture and inspect the first ten loaded chart bars in the Data Window; later fixture prices and volumes are missing by design. Turn fixture mode off for chart close and volume. The blue line is this VAMA variant; orange is a strict fixed-window VWMA.

  • Baseline input is bounded to 500 prior bars. The scan visits at most 1,000 bars, with offsets 0 through 999, and never reads before bar_index 0. max_bars_back = 1000 explicitly accommodates both ranges. There are no future references or higher-timeframe requests.
  • A scan cap is a resource limit, not a fallback length. Raising it may expose older invalid data and increases work. Pine also has execution-time limits, so bounded indexing alone does not guarantee fast execution on every dataset.
  • The fractional-bar approximation assigns one close price to every used unit of that bar's volume. It cannot reconstruct the actual intrabar trade prices. Changing timeframe or volume feed changes the input and therefore the curve.
Original educational implementation. Status 0 means no valid positive baseline; 1 means insufficient scanned volume; 2 means invalid data encountered; 3 means the target was filled.pine
//@version=6
indicator("Prior-baseline volume-clock VAMA", overlay=true, max_bars_back=1000)
baselineBars = input.int(3, "Prior volume baseline bars", minval=1, maxval=500)
increments = input.float(3.0, "Volume increments", minval=0.1, maxval=1000)
factor = input.float(0.67, "Increment factor", minval=0.01, maxval=10)
maxScan = input.int(500, "Maximum scanned bars", minval=1, maxval=1000)
compareBars = input.int(3, "Fixed VWMA bars", minval=1, maxval=500)
isTest = input.bool(false, "Synthetic ten-bar fixture")
float testPrice = switch bar_index
    0 => 100.0
    1 => 102.0
    2 => 104.0
    3 => 110.0
    4 => 120.0
    5 => 80.0
    6 => 90.0
    7 => 100.0
    8 => 100.0
    9 => 100.0
    => na
float testVolume = switch bar_index
    0 => 100.0
    1 => 100.0
    2 => 100.0
    3 => 150.0
    4 => 0.0
    5 => 400.0
    6 => 0.0
    7 => 0.0
    8 => 0.0
    9 => 100.0
    => na
float p = isTest ? testPrice : close
float v = isTest ? testVolume : volume
bool baselineValid = bar_index >= baselineBars
float baselineTotal = 0.0
if baselineValid
    for j = 1 to baselineBars
        if na(v[j]) or v[j] < 0
            baselineValid := false
            break
        baselineTotal += v[j]
float baseline = baselineValid ? baselineTotal / baselineBars : na
float target = baselineValid and baseline > 0 ? increments * factor * baseline : na
float remaining = target
float weighted = 0.0
float oldestFraction = na
int span = 0
int status = 0
if not na(target)
    status := 1
    for i = 0 to math.min(maxScan - 1, bar_index)
        span := i + 1
        if na(v[i]) or v[i] < 0 or na(p[i])
            status := 2
            break
        if v[i] > 0
            float used = math.min(v[i], remaining)
            weighted += p[i] * used
            remaining -= used
            oldestFraction := used / v[i]
        if remaining <= 0
            status := 3
            break
float vama = status == 3 ? weighted / target : na
bool validBar = not na(v) and v >= 0 and not na(p)
float validCount = math.sum(validBar ? 1.0 : 0.0, compareBars)
float compareVolume = math.sum(validBar ? v : 0.0, compareBars)
float compareWeighted = math.sum(validBar ? p * v : 0.0, compareBars)
float vwma = bar_index >= compareBars - 1 and validCount == compareBars and compareVolume > 0 ? compareWeighted / compareVolume : na
plot(vama, "Prior-baseline VAMA", color=color.blue, linewidth=2, style=plot.style_linebr)
plot(vwma, "Fixed-window VWMA", color=color.orange, style=plot.style_linebr)
plot(p, "Input price", display=display.data_window)
plot(v, "Input volume", display=display.data_window)
plot(baseline, "Prior mean volume", display=display.data_window)
plot(target, "Target volume", display=display.data_window)
plot(status == 3 ? span : na, "Window span including zeros", display=display.data_window)
plot(status == 3 ? oldestFraction : na, "Oldest bar fraction", display=display.data_window)
plot(status, "Status: 0 baseline, 1 short, 2 invalid, 3 ready", display=display.data_window)
plot(isTest ? bar_index : na, "Fixture bar index", display=display.data_window)

Causality does not mean the open bar is fixed

The prior-bar baseline is fixed throughout the current bar. Current close and accumulated volume can still change before that bar closes, so the current VAMA can change too. Compare completed bars. Appending future bars does not mathematically alter previous values in this specification when the earlier data and settings are unchanged.

  • Loading additional earlier history can fill a previously unavailable window. Feed corrections, split adjustments, session changes and different aggregation can change historical inputs. Treat these separately from future leakage.
  • A price crossing is an indicator observation, not an executable trading rule. Define bar-close timing, order execution, fees and position sizing separately before evaluating a strategy. No return or predictive claim follows from the fixture.

Give Pineify the formula and the expected outputs

Pine Script AI Coding Agent can create and revise inspectable code documents. Supply this named variant, the prior-bar baseline, partial oldest-bar rule and strict invalid-data policy. Ask explicitly for a syntax check, then compare the generated script against the fixture in TradingView.

  • Require bar 3 = 108.4776119403, bar 4 = 107.8379530917, bar 5 = 80, and no VAMA at bar 9. Ask the agent to expose target volume and window span, so a fixed-VWMA substitution is visible.
  • A successful syntax check does not verify the arithmetic or establish trading performance. Preserve the same inputs, volume units and completed-bar timing when comparing implementations.
Where Pineify fits

Pine Script AI Coding Agent

Draft or revise Pine Script from the stated volume-clock formula, inspect the source, and request syntax diagnostics before comparing fixture values.

Build this VAMA variant

Frequently asked questions

Educational indicator calculations, not investment advice. All sample prices and volumes are hypothetical. Volume weighting does not guarantee profitable signals.

Sources and verification

Published by Pineify. Pineify is an independent product and is not affiliated with TradingView. Sources support the technical explanations; examples do not establish trading performance.