Overview
The Relative Volume Percentage Momentum Trading Strategy is a comprehensive trading system that combines volume momentum analysis, price action filtering, breakout detection, and dynamic stop-loss/take-profit logic. The core of this strategy is calculating a Williams %R-like indicator for relative volume (RVPR), paired with dual moving average filters (Fast and Slow) to identify moments of volume expansion or contraction. The strategy further refines entry conditions through configurable price action filters based on different types of candle formations. This strategy is particularly suitable for traders looking to identify reversals or continuation setups based on relative volume spikes and candle behavior, providing highly adaptable long and short trading signals.
Strategy Principles
The core principle of this strategy is transforming volume data into a percentage range, using a Williams %R-like calculation to analyze the relationship between current volume and its historical range. The strategy uses the following key components to generate trading signals:
Relative Volume %R Oscillator: Compares the current volume with historical volume highs and lows, calculating the relative position. This indicator is similar to Williams %R in the price domain but applied to volume data.
Dual Moving Average Filter: The strategy uses two volume moving averages (Fast and Slow), with options for various smoothing algorithms (SMA, EMA, JMA, T3, Super Smoother, etc.). When volume is greater than the Fast MA, and Fast MA is greater than Slow MA, it indicates an upward volume trend, potentially a long signal; vice versa for short.
Price Action Filter: Further filters trading signals based on different candle patterns:
- Simple Mode: Basic up/down candles
- Filtered Mode: Range-based strength confirmation
- Aggressive Mode: Momentum-based breakout
- Inside Mode: Reversal candle patterns
Breakout Filter: Optionally excludes trades near 5-bar highs/lows to avoid poor risk-reward trades.
Stop Loss and Take Profit System: Dynamic SL/TP mechanism based on ATR (Average True Range), with configurable multipliers to adjust the distance of SL and TP.
Timed Exit: Option to exit trades after a fixed number of candles.
Long entry conditions include: volume greater than Fast MA, Fast MA greater than Slow MA, Relative Volume %R greater than threshold, price passing the long directional filter, and optionally below recent breakout high. Short entry conditions are the opposite, with positions closed when the set exit conditions are triggered.
Strategy Advantages
Multi-dimensional Analysis: The strategy combines volume, price action, and dynamic SL/TP, providing a comprehensive market analysis framework.
Highly Customizable: The strategy offers multiple adjustable parameters, including trade direction control, different price action filtering modes, volume moving average type selection, etc., allowing traders to customize according to their style and market preferences.
Intelligent Entry Filtering: By combining volume momentum and price action patterns, the strategy can identify higher probability trading opportunities, avoiding low-quality trading signals.
Flexible Exit Mechanisms: The strategy provides time and price-based exit options, including fixed bar exit and ATR-based dynamic SL/TP, making risk management more flexible and effective.
Adaptable to Various Market Environments: Through different price action modes (Simple, Filtered, Aggressive, Inside), the strategy can adapt to different market conditions, including trending and ranging markets.
Advanced Technical Indicator Integration: The strategy incorporates multiple advanced moving average types, such as JMA (Jurik Moving Average), T3, and Super Smoother, which excel in reducing noise and capturing true trends.
Strategy Risks
Parameter Optimization Risk: As the strategy contains multiple adjustable parameters, there is a risk of over-optimization, potentially leading to excellent historical backtesting performance but poor live trading results. The solution is to use forward testing and robustness analysis to ensure parameters maintain stability across different market conditions.
False Breakout Risk: Volume spikes do not always accompany sustainable price movements, and the strategy may generate false signals in fake breakouts. This risk can be mitigated by adding additional confirmation indicators or delaying entries.
Market Environment Dependency: The strategy's performance may be inconsistent in different market environments (e.g., high volatility vs. low volatility). It is recommended to test the strategy's performance under various market conditions before implementation.
Stop Loss Trigger Risk: Using ATR-based stops may be triggered when volatility suddenly expands. Consider using volatility-adjusted stop multipliers or setting stops at key support/resistance levels for more effectiveness.
Inflexible Time Exit: Fixed bar exits may close profitable trades too early or losing trades too late. Consider combining trend or momentum indicators to dynamically adjust exit timing.
Computational Complexity: The strategy uses multiple complex moving average algorithms and condition combinations, potentially increasing computational burden and leading to execution delays. In real-time trading, it may be necessary to simplify some computation-intensive indicators.
Strategy Optimization Directions
Dynamic Threshold Adjustment: The current strategy uses a fixed Relative Volume %R threshold (27); consider implementing adaptive thresholds that automatically adjust based on recent volume volatility. This would make the strategy better adapt to different market conditions and seasonal changes.
Multi-timeframe Confirmation: Introduce higher timeframe confirmation signals, trading only in the direction of the larger trend, which can improve the strategy's win rate and risk-reward ratio. For example, only execute long signals on the hourly chart when the daily trend is upward.
Volume Quality Analysis: In addition to relative volume, incorporate volume divergence indicators or volume distribution analysis to assess volume quality rather than just quantity. This helps distinguish healthy trend-confirming volume from potential exhaustion signals.
Intelligent Stop Loss/Take Profit: The current ATR-based SL/TP can be improved to a more intelligent system, for example, based on key support/resistance positions, or using volatility-adjusted stops, tightening stops in low volatility periods and widening them in high volatility periods.
Integrate Market Structure: Incorporating price structure analysis (such as support/resistance, trendlines, price channels) into the strategy can improve the quality of entry and exit points.
Risk Management Enhancement: Implement dynamic position sizing, based on current market volatility and recent strategy performance, increasing positions in high win-rate environments and reducing them in uncertain periods.
Machine Learning Integration: Using machine learning algorithms to dynamically optimize strategy parameters or predict which price action filter is most effective in current market conditions can further improve strategy performance.
Summary
The Relative Volume Percentage Momentum Trading Strategy is a comprehensive and flexible trading system that provides traders with a powerful tool to identify potential market opportunities by combining volume analysis, various price action filters, and dynamic risk management techniques. The core strengths of this strategy lie in its adaptability and customizability, allowing traders to adjust according to personal preferences and market conditions.
This strategy is particularly suitable for traders seeking reversal or trend continuation signals based on volume confirmation. By using a Williams %R style relative volume indicator, the strategy can identify volume spike points, which often represent important shifts in market sentiment or trend acceleration. Meanwhile, multiple price action filtering options allow traders to choose more conservative or aggressive entry conditions based on their risk preferences and trading style.
While the strategy offers many advantages, traders should be aware of potential over-optimization risks and market environment dependencies. Through continuous testing and adjustment, combined with the suggested optimization directions, traders can further enhance the robustness and long-term profitability of this strategy. Ultimately, as with all trading strategies, the key to success lies in thoroughly understanding its principles, wisely managing risk, and continuously evaluating its performance under different market conditions.
Strategy source code
/*backtest
start: 2024-07-04 00:00:00
end: 2025-07-02 08:00:00
period: 1d
basePeriod: 1d
exchanges: [{"eid":"Futures_Binance","currency":"ETH_USDT"}]
*/
// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © GabrielAmadeusLau
//@version=6
strategy("Relative Volume Strategy", overlay=true, default_qty_type=strategy.percent_of_equity, default_qty_value=100)
// === Input: Trade Direction === //
tradeDirection = input.string("Long Only", title="Trade Direction", options=["Long Only", "Short Only", "Both"], group="Strategy Settings")
dirBarModeL = input.string("Simple", title="Long Directional Bar Mode", options=["Simple", "Filtered", "Aggressive", "Inside", "Filtered & Aggressive", "Filtered & Aggressive & Inside", "Without"], group="Strategy Settings")
dirBarModeS = input.string("Inside", title="Short Directional Bar Mode", options=["Simple", "Filtered", "Aggressive", "Inside", "Filtered & Aggressive", "Filtered & Aggressive & Inside", "Without"], group="Strategy Settings")
useBreakout = input.bool(true, "Use Breakout Filter", group="Strategy Settings")
useSLTP = input.bool(false, "Use Stop Loss & Take Profit", group="Strategy Settings")
atrSLMult = input.float(1, "ATR SL Multiplier", step = 0.05, group="Strategy Settings")
atrTPMult = input.float(1.75, "ATR TP Multiplier", step = 0.05, group="Strategy Settings")
// === Input: MA Function Selector === //
// — T3 Moving Average Function —
// src = input source (e.g. rsi1, close, etc.)
// length = smoothing length (period)
// a = T3 alpha (commonly between 0.7 and 0.9)
t3(src, length, a) =>
e1 = ta.ema(src, length)
e2 = ta.ema(e1, length)
e3 = ta.ema(e2, length)
e4 = ta.ema(e3, length)
e5 = ta.ema(e4, length)
e6 = ta.ema(e5, length)
c1 = -a * a * a
c2 = 3 * a * a + 3 * a * a * a
c3 = -6 * a * a - 3 * a - 3 * a * a * a
c4 = 1 + 3 * a + a * a * a + 3 * a * a
c1 * e6 + c2 * e5 + c3 * e4 + c4 * e3
// == Jurik MA == //
jma(float src, int length, float power, float phase) =>
phaseRatio = phase < -100 ? 0.5 : phase > 100 ? 2.5 : phase / 100 + 1.5
beta = 0.45 * (length - 1) / (0.45 * (length - 1) + 2)
alpha = math.pow(beta, power)
JMA = 0.0
e0 = 0.0
e0 := (1 - alpha) * src + alpha * nz(e0[1])
e1 = 0.0
e1 := (src - e0) * (1 - beta) + beta * nz(e1[1])
e2 = 0.0
e2 := (e0 + phaseRatio * e1 - nz(JMA[1])) * math.pow(1 - alpha, 2) + math.pow(alpha, 2) * nz(e2[1])
JMA := e2 + nz(JMA[1])
//===== 2 Pole Super Smoother Filter =====//
superSmoother(float Series, float Period) =>
var float ALPHA = math.pi * math.sqrt(2.0) / Period
var float BETA = math.exp(-ALPHA )
var float COEF2 = -math.pow(BETA, 2)
var float COEF1 = math.cos( ALPHA ) * 2.0 * BETA
var float COEF0 = 1.0 - COEF1 - COEF2
float sma2 = math.avg(Series, nz(Series[1], Series))
float smooth = na, smooth := COEF0 * sma2 +
COEF1 * nz(smooth[1]) +
COEF2 * nz(smooth[2])
// === MA Selector === //
ma(source, length, type) =>
type == "SMA" ? ta.sma(source, length) :
type == "EMA" ? ta.ema(source, length) :
type == "SMMA (RMA)"? ta.rma(source, length) :
type == "WMA" ? ta.wma(source, length) :
type == "VWMA" ? ta.vwma(source, length) :
type == "HMA" ? ta.hma(source, length) :
type == "ALMA" ? ta.alma(source, length, 0.85, 6) :
type == "LSMA" ? ta.linreg(source, length, 0) :
type == "Optimal MA"? math.avg(ta.alma(source, length, 0.85, 6), ta.rma(source, length), ta.sma(source, length)) :
type == "JMA" ? jma(source, length, 2, 50) :
type == "Super Smoother" ? superSmoother(source, length) :
type == "T3" ? t3(source, length, 0.7) :
na
// === Input Parameters === //
rvolRLength = input.int(112, title="Relative Volume %R Length", minval=1, group="Relative Volume", tooltip="%R used for scaling from 0 to 100, I prefer 73 or 112.")
rvolmaTypeInput = input.string("Optimal MA" , "Type", options = ["None", "SMA", "EMA", "SMMA (RMA)", "WMA", "VWMA", "HMA", "ALMA", "LSMA", "Optimal MA", "JMA", "Super Smoother", "T3"], group = "Relative Volume")
rvolFastLength = input.int(7, title="Relative Volume Fast MA", minval=1, group="Relative Volume")
rvolSlowLength = input.int(161, title="Relative Volume Slow MA", minval=1, group="Relative Volume")
exitBars = input.int(18, title="Bars Until Exit", group="Strategy Settings", tooltip="Exit trade after N bars")
rvolThreshold = input.int(27, "Minimum Relative Volume %R Threshold", group="Relative Volume")
// === Williams %R for Volume === //
wpr(src, length) =>
max_ = ta.highest(src, length)
min_ = ta.lowest(src, length)
(100 * (src - max_) / (max_ - min_)) * -1
// === Volume MAs === //
rvol = wpr(volume, rvolRLength)
rvolFast = ma(volume, rvolFastLength, rvolmaTypeInput)
rvolSlow = ma(volume, rvolSlowLength, rvolmaTypeInput)
// === Price Action Filters === //
up = close > open
upRange = low > low[1] and close > high[1]
upRange_Aggr = close > close[1] and close > open[1]
insideDayUp = close < close[1] and close[1] < close[2] and close[2] < close[3] and close[3] < close[4] and close[4] < close[5] //and not (close > close[1])
down = close < open
downRange = high < high[1] and close < low[1]
downRange_Aggr= close < close[1] and close < open[1]
insideDayDown = close > close[1] and close[1] > close[2] and close[2] > close[3] and close[3] > close[4] and close[4] > close[5] //and not (close < close[1])
breakoutHigh = ta.highest(high, 5)
breakoutLow = ta.lowest(low, 5)
// === Mode-Based Filter Logic === //
longBarOK =
dirBarModeL == "Simple" ? up :
dirBarModeL == "Filtered" ? upRange :
dirBarModeL == "Aggressive"? upRange_Aggr :
dirBarModeL == "Inside"? insideDayUp :
dirBarModeL == "Filtered & Aggressive" ? upRange or upRange_Aggr :
dirBarModeL == "Filtered & Aggressive & Inside" ? upRange or upRange_Aggr or insideDayUp :
dirBarModeL == "Without" ? true : false
shortBarOK =
dirBarModeS == "Simple" ? down :
dirBarModeS == "Filtered" ? downRange :
dirBarModeS == "Aggressive"? downRange_Aggr :
dirBarModeS == "Inside"? insideDayDown :
dirBarModeS == "Filtered & Aggressive"? downRange or downRange_Aggr or insideDayDown :
dirBarModeS == "Filtered & Aggressive & Inside"? upRange_Aggr or insideDayDown :
dirBarModeS == "Without" ? true : false
// === Entry & Exit Logic === //
longCondition = volume > rvolFast and rvolFast > rvolSlow and longBarOK and rvol > rvolThreshold and (not useBreakout or close < breakoutHigh)
shortCondition = volume < rvolFast and rvolFast < rvolSlow and shortBarOK and rvol < (100 - rvolThreshold) and (not useBreakout or close > breakoutLow)
exitLongCondition = strategy.opentrades > 0 and strategy.opentrades.entry_bar_index(0) + exitBars <= bar_index and strategy.opentrades.entry_id(0) == "Long"
exitShortCondition = strategy.opentrades > 0 and strategy.opentrades.entry_bar_index(0) + exitBars <= bar_index and strategy.opentrades.entry_id(0) == "Short"
atr = ta.atr(math.round(math.avg(rvolFastLength, rvolSlowLength)))
longSL = useSLTP ? close - atrSLMult * atr : na
longTP = useSLTP ? close + atrTPMult * atr : na
shortSL = useSLTP ? close + atrSLMult * atr : na
shortTP = useSLTP ? close - atrTPMult * atr : na
// === Strategy Execution === //
if (tradeDirection == "Long Only" or tradeDirection == "Both")
if (longCondition)
strategy.entry("Long", strategy.long, stop=longSL, limit=longTP)
if (tradeDirection == "Short Only" or tradeDirection == "Both")
if (shortCondition)
strategy.entry("Short", strategy.short, stop=shortSL, limit=shortTP)
if (exitLongCondition)
strategy.close("Long")
if (exitShortCondition)
strategy.close("Short")
// === Plotting === //
plot(rvol, title="Relative Volume %R", color=color.orange, style = plot.style_columns, format = format.price)
plot(rvolFast, title="Fast Volume MA", color=color.green, format = format.volume)
plot(rvolSlow, title="Slow Volume MA", color=color.red, format = format.volume)
Strategy parameters
The original address: Relative Volume Percentage Momentum Trading Strategy
Top comments (1)
Relative volume meets momentum — finally, a strategy that actually cares how hard the market’s moving, not just where! 🚀📊 Love the simplicity with punch. Might have to add this to my “please don’t betray me” strategy list 😅 Thanks for the solid write-up!