Adaptive Velocity Oscillator [UAlgo]Adaptive Velocity Oscillator is a momentum and reversal framework built around the rate of change of an Adaptive Moving Average. Instead of using a fixed smoothing engine, the script first creates a Kaufman style adaptive average whose responsiveness changes according to market efficiency, then measures how fast that adaptive baseline is moving from one bar to the next. That velocity becomes the core oscillator.
The main idea is straightforward. When the adaptive average starts accelerating upward, the oscillator rises above zero. When the adaptive average starts decelerating or turning lower, the oscillator falls below zero. This gives the user a direct view of directional pressure, but in a way that remains sensitive to changing market conditions because the underlying average itself is adaptive rather than static.
To make the oscillator more practical, the script surrounds the velocity with dynamic filter bands derived from the standard deviation of the AMA series. These bands act like a contextual noise threshold. Small fluctuations inside the band are treated as less important, while stronger moves through the band can be interpreted as meaningful directional expansion or reversal activity.
The script also supports two signal styles. Standard mode reacts to velocity transitions through the regular filter band. Extreme Reversal mode requires a deeper stretch into an expanded threshold before signaling a reversal style response. Optional price confirmation can then require bullish candle structure for buy signals and bearish candle structure for sell signals. A cooldown filter is added on top so the same type of signal cannot repeat too rapidly.
The result is an oscillator that can be used for trend context, reversal spotting, and momentum transition analysis. It is especially useful for traders who want something more adaptive than a traditional moving average crossover or a simple rate of change calculation.
🔹 Features
🔸 Adaptive AMA Core
The script uses an adaptive moving average whose smoothing constant changes according to the Efficiency Ratio. When price is moving efficiently in one direction, the average becomes more responsive. When price is noisy and directionless, the average becomes slower and more stable.
🔸 Velocity Based Oscillator
The oscillator is not built from price directly. It is built from the bar to bar change of the adaptive average. This means the indicator measures how quickly the smoothed baseline itself is moving, which creates a cleaner momentum signal than raw price change alone.
🔸 Dynamic Filter Bands
A statistical filter band is calculated from the standard deviation of the AMA series and then scaled by the user selected gamma value. This creates a noise threshold that expands and contracts with market conditions.
🔸 Standard and Extreme Reversal Modes
In Standard mode, signals are generated when velocity crosses back through the regular filter threshold. In Extreme Reversal mode, the script requires a deeper stretch using an expanded band before a signal can trigger. This gives the user a choice between more responsive and more selective behavior.
🔸 Optional Price Confirmation
Signals can require candle confirmation. Bullish signals may be restricted to bars where close is above open, and bearish signals may be restricted to bars where close is below open. This can help reduce signals that appear without supportive candle structure.
🔸 Cooldown Protection
A built in cooldown logic prevents repeated same side signals from firing too close together. This helps reduce clustering during noisy or oscillatory phases.
🔸 Trend State From Zero Crosses
The script also tracks velocity crosses through zero. These zero transitions can be interpreted as broader positive or negative momentum regime shifts.
🔸 Visual Context Through Color and Bands
The histogram and line coloring change according to whether velocity is strongly positive, strongly negative, or neutral relative to the filter zone. This makes the oscillator easy to read at a glance.
🔸 Signal Labels and Alerts
The indicator places buy and sell labels around qualifying signal bars and includes alert conditions for bullish signals, bearish signals, positive trend shifts, and negative trend shifts.
🔹 Calculations
1) AMA State Container
type AMACalculator
float value = na
float value_prev = na
float velocity = 0.0
float filterBand = 0.0
This object stores the internal state of the adaptive average engine.
value holds the latest AMA value.
value_prev stores the previous AMA value.
velocity stores the bar to bar change of that AMA.
filterBand stores the active dynamic threshold used later for signal filtering.
So before any signal logic begins, the script already has a dedicated structure for the adaptive average, its momentum, and its statistical band.
2) Efficiency Ratio Calculation
float change = math.abs(src - src )
float volatility = math.sum(math.abs(src - src ), lengthER)
float ER = volatility == 0 ? 0 : change / volatility
This is the first major step inside the AMA calculation.
The script compares two quantities.
change measures the net directional move from the current price back to the price lengthER bars ago.
volatility measures the total path traveled during that same interval by summing all absolute bar to bar changes.
The Efficiency Ratio is then:
ER = change / volatility
If price moved smoothly in one direction, net change will be large relative to total movement, and ER will be high. If price moved in a noisy back and forth way, total movement will be large but net change will be smaller, so ER will be low.
This ratio tells the AMA how efficiently price has been moving, which directly controls how responsive the adaptive average should become.
3) Building the Adaptive Smoothing Constant
float fastest = 2.0 / (fastLen + 1)
float slowest = 2.0 / (slowLen + 1)
float sc = math.pow(ER * (fastest - slowest) + slowest, 2)
This block converts the Efficiency Ratio into a smoothing constant.
First, the script computes the EMA style constants for the chosen fast and slow lengths. Then it blends between them using ER. When ER is high, the result moves closer to the fast setting. When ER is low, the result stays closer to the slow setting.
Finally, the blended value is squared. This is a classic AMA technique that makes the adaptive response more sensitive to efficiency changes.
So the smoothing constant automatically shifts between fast and slow behavior depending on market structure.
4) Updating the Adaptive Moving Average
this.value_prev := this.value
float prevAma = na(this.value_prev) ? src : this.value_prev
this.value := prevAma + sc * (src - prevAma)
This is the actual AMA update formula.
The script first stores the previous AMA value. If no previous value exists yet, it uses the current source as the starting point.
Then it updates the average using:
new AMA = previous AMA + smoothing constant × (source minus previous AMA)
So the adaptive average moves toward price, but the speed of that movement depends entirely on the previously calculated smoothing constant.
When the market is efficient, the average reacts more quickly. When the market is noisy, it reacts more slowly.
5) Computing Velocity
this.velocity := this.value - prevAma
This single line is the core of the oscillator.
Velocity here is simply the difference between the current AMA value and the previous AMA value.
If the adaptive average is rising, velocity is positive.
If the adaptive average is falling, velocity is negative.
If the adaptive average is barely moving, velocity stays close to zero.
So the oscillator is not measuring price change directly. It is measuring the momentum of the adaptive baseline itself.
6) Creating the Dynamic Filter Band
float amaGlobalSeries = amaObj.value
float sigma = ta.stdev(amaGlobalSeries, n)
amaObj.filterBand := gamma * sigma
After the AMA is calculated, the script builds a dynamic filter threshold from its standard deviation.
sigma measures how much the AMA has been varying over the selected period.
That value is then scaled by gamma to create the final filter band.
So the band expands when the adaptive average becomes more variable and contracts when the average becomes quieter.
This creates a context aware threshold that helps separate meaningful momentum movement from smaller background fluctuations.
7) Regular Band and Extreme Band
float currentVelocity = amaObj.velocity
float currentFilter = amaObj.filterBand
float extremeBand = currentFilter * extMulti
This block prepares the two signal thresholds used later.
currentFilter is the normal band.
extremeBand is a larger band created by multiplying the normal band by the user selected extreme multiplier.
So the script supports two layers of selectivity:
a regular threshold for Standard mode,
and a deeper threshold for Extreme Reversal mode.
8) Raw Signal Logic
bool rawBullSignal = sigMode == "Standard" ? ta.crossover(currentVelocity, -currentFilter) : ta.crossover(currentVelocity, -extremeBand)
bool rawBearSignal = sigMode == "Standard" ? ta.crossunder(currentVelocity, currentFilter) : ta.crossunder(currentVelocity, extremeBand)
This is the primary signal engine.
In Standard mode:
a bullish signal appears when velocity crosses upward through the negative regular filter level.
a bearish signal appears when velocity crosses downward through the positive regular filter level.
In Extreme Reversal mode:
a bullish signal requires velocity to recover upward through the deeper negative extreme band.
a bearish signal requires velocity to fall downward through the deeper positive extreme band.
The important idea is that signals are not based on zero line crosses alone. They are based on velocity reentering from stretched territory. That makes the logic more reversal oriented than a simple trend flip model.
9) Optional Price Confirmation
if reqPriceConf
rawBullSignal := rawBullSignal and close > open
rawBearSignal := rawBearSignal and close < open
This block adds an extra candle structure filter.
If price confirmation is enabled:
bullish signals are only allowed when the bar closes above its open.
bearish signals are only allowed when the bar closes below its open.
This can help reduce signals that occur mathematically in the oscillator but do not have supportive price behavior on the actual candle.
So the oscillator can be used either in pure indicator form or with a stricter candle aligned confirmation layer.
10) Cooldown Filter Logic
method filterSignal(array arr, bool cond, int waitBars) =>
bool isValid = false
if cond
int lastSignalBar = arr.size() > 0 ? arr.get(0) : -waitBars - 1
if (bar_index - lastSignalBar) >= waitBars
isValid := true
arr.unshift(bar_index)
if arr.size() > 2
arr.pop()
isValid
This method prevents signals from firing too frequently.
When a new raw signal appears, the script looks at the most recent stored signal bar for that direction. If enough bars have passed since the previous signal, the new one is accepted. Otherwise it is ignored.
The accepted signal bar index is then stored at the front of the array. Only a small recent history is kept.
So this method acts as a timing gate that stops repetitive same side signals during choppy conditions.
11) Final Signal Construction
var array lastBull = array.new()
var array lastBear = array.new()
bool finalBullSignal = lastBull.filterSignal(rawBullSignal, cooldown)
bool finalBearSignal = lastBear.filterSignal(rawBearSignal, cooldown)
This is where raw signals become final trade style signals.
Bullish signals are passed through the bullish cooldown array.
Bearish signals are passed through the bearish cooldown array.
That means buy and sell signals are filtered independently. A recent bullish signal only blocks another bullish signal, and a recent bearish signal only blocks another bearish signal.
So the script maintains clean directional spacing for both sides.
12) Histogram Coloring Logic
color histColor = currentVelocity > currentFilter ? color.new(colorUp, 20) :
currentVelocity < -currentFilter ? color.new(colorDn, 20) :
currentVelocity > 0 ? color.new(colorUp, 70) :
color.new(colorDn, 70)
This block assigns visual meaning to oscillator strength.
If velocity is above the upper regular filter, the histogram uses a stronger bullish color.
If velocity is below the lower regular filter, it uses a stronger bearish color.
If velocity is still positive but inside the filter region, it uses a softer bullish color.
If velocity is negative but inside the filter region, it uses a softer bearish color.
So the color logic tells the user both direction and intensity at the same time.
13) Drawing the Filter Bands
plot(currentFilter, "Upper Filter Band", color=color.new(colorDn, 40), linewidth=1, style=plot.style_line)
plot(-currentFilter, "Lower Filter Band", color=color.new(colorUp, 40), linewidth=1, style=plot.style_line)
plot(extremeBand, "Upper Extreme Band", color=color.new(colorDn, 60), linewidth=1, style=plot.style_line, display=sigMode == "Extreme Reversal" ? display.all : display.none)
plot(-extremeBand, "Lower Extreme Band", color=color.new(colorUp, 60), linewidth=1, style=plot.style_line, display=sigMode == "Extreme Reversal" ? display.all : display.none)
These plots create the visual threshold system.
The regular upper and lower filter bands are always shown.
The wider extreme bands are shown only when Extreme Reversal mode is selected.
This makes it easy to see whether current velocity is operating inside the neutral zone, beyond the regular band, or at deeper stretch levels.
14) Filling the Neutral Filter Region
p_upper = plot(currentFilter, display=display.none)
p_lower = plot(-currentFilter, display=display.none)
fill(p_upper, p_lower, color=color.new(colorNeu, 95), title="Filter Band Fill")
This block shades the area between the upper and lower regular filter bands.
The filled zone visually represents the neutral or lower conviction region. When velocity remains inside this zone, momentum is more muted relative to recent adaptive average behavior.
So the fill acts as a quick background cue for whether velocity is still inside normal fluctuation territory.
15) Plotting Velocity as Histogram and Line
plot(currentVelocity, "AMA Velocity", color=histColor, style=plot.style_columns)
plot(currentVelocity, "Velocity Line", color=histColor, linewidth=2)
The same velocity value is drawn in two forms.
The column plot gives a strong histogram style momentum read.
The line plot overlays the same data as a smoother continuous path.
Using both together makes the oscillator easier to read because the columns highlight amplitude while the line emphasizes turning points and transitions.
16) Signal Label Placement
if finalBullSignal
label.new(x=bar_index, y=math.min(0, currentVelocity) - math.abs(sigMode == "Extreme Reversal" ? extremeBand : currentFilter) - (math.abs(currentFilter) * 1.5),
text="▲ BUY",
color=color.new(color.white, 100),
textcolor=colorUp,
style=label.style_none,
size=size.small)
if finalBearSignal
label.new(x=bar_index, y=math.max(0, currentVelocity) + math.abs(sigMode == "Extreme Reversal" ? extremeBand : currentFilter) + (math.abs(currentFilter) * 1.5),
text="SELL ▼",
color=color.new(color.white, 100),
textcolor=colorDn,
style=label.style_none,
size=size.small)
These blocks place the signal labels outside the oscillator body rather than directly on top of the bars.
For bullish signals, the label is positioned below the relevant lower threshold area.
For bearish signals, the label is positioned above the relevant upper threshold area.
This helps keep the chart readable and visually separates the signal from the oscillator itself.
17) Alert Conditions
alertcondition(finalBullSignal, "Buy Signal", "AMA Velocity Buy Signal")
alertcondition(finalBearSignal, "Sell Signal", "AMA Velocity Sell Signal")
bool posTrend = ta.crossover(currentVelocity, 0)
bool negTrend = ta.crossunder(currentVelocity, 0)
alertcondition(posTrend, "Positive Trend", "AMA Velocity crossed above zero")
alertcondition(negTrend, "Negative Trend", "AMA Velocity crossed below zero")
The script provides four alert types.
The first two alert on final buy and sell signals after all filters and cooldown checks are applied.
The second two alert when velocity crosses the zero line, which can be interpreted as broader momentum regime shifts.
So the indicator supports both reversal style event monitoring and general trend transition monitoring.
Chỉ báo Pine Script®






















