相对成交量百分比动量交易策略是一种结合了成交量动量分析、价格行为过滤、突破检测和动态止损/止盈逻辑的综合性交易系统。该策略核心是通过计算相对成交量的Williams %R类似指标(RVPR),结合双均线过滤器(快速和慢速移动平均线)来识别成交量扩张或收缩的时刻。策略进一步通过可配置的价格行为过滤器来精确入场条件,基于不同类型的蜡烛图形态进行判断。该策略特别适合寻找基于相对成交量突增和蜡烛行为的反转或延续设置的交易者,提供了高度适应性的多空交易信号。
这个策略的核心原理是将成交量数据转化为百分比区间,使用类似Williams %R的计算方法来分析当前成交量与其历史范围的关系。策略使用以下几个关键组件来生成交易信号:
相对成交量%R振荡器:将当前成交量与历史成交量的最高和最低水平进行比较,计算相对位置。这个指标类似于价格领域中的Williams %R,但应用于成交量数据。
双移动平均线过滤:策略使用两条成交量移动平均线(快速和慢速),可以选择多种平滑算法(SMA、EMA、JMA、T3、Super Smoother等)。当成交量大于快速均线,且快速均线大于慢速均线时,表明成交量趋势向上,可能是做多信号;反之亦然。
价格行为过滤器:根据不同的蜡烛图形态来进一步筛选交易信号:
突破过滤器:可选择性地排除在5根蜡烛的高点/低点附近的交易,以避免风险回报比不佳的交易。
止损和止盈系统:基于ATR(平均真实波幅)的动态止损/止盈机制,可配置乘数以调整止损和止盈的距离。
时间退出:可选择在固定数量的蜡烛棒后退出交易。
多头入场条件包括:成交量大于快速移动平均线,快速移动平均线大于慢速移动平均线,相对成交量%R大于阈值,价格通过多头方向过滤器,以及可选的低于近期突破高点。空头入场条件则相反,并且在设定的退出条件触发时平仓。
多维度分析:该策略结合了成交量、价格行为和动态止损/止盈,提供了全面的市场分析框架。
高度可定制:策略提供了多种参数可供调整,包括交易方向控制、不同的价格行为过滤模式、成交量移动平均线类型选择等,允许交易者根据自己的风格和市场偏好进行定制。
智能入场过滤:通过结合成交量动量和价格行为模式,策略能够识别更高概率的交易机会,避免低质量的交易信号。
灵活的退出机制:策略提供了基于时间和价格的退出选项,包括固定棒数退出和基于ATR的动态止损/止盈,使得风险管理更加灵活有效。
适应多种市场环境:通过不同的价格行为模式(简单、过滤、激进、内部),策略可以适应不同的市场条件,包括趋势和区间市场。
高级技术指标整合:策略整合了多种高级移动平均线类型,如JMA(Jurik移动平均线)、T3和Super Smoother,这些指标在减少噪音和捕捉真实趋势方面表现优异。
参数优化风险:由于策略包含多个可调参数,存在过度优化的风险,可能导致历史回测表现优异但实盘效果欠佳。解决方法是使用前向测试和鲁棒性分析,确保参数在不同市场条件下都能保持稳定性。
假突破风险:成交量突增不一定总是伴随着可持续的价格走势,策略可能在假突破中产生错误信号。可以通过增加额外的确认指标或延迟入场来减轻这一风险。
市场环境依赖性:该策略在不同的市场环境(如高波动性vs低波动性)中表现可能不一致。建议在实施前测试策略在各种市场条件下的表现。
止损触发风险:使用ATR基础的止损可能在波动性突然扩大时被触发。考虑使用波动性调整的止损倍数或将止损设置在关键支撑/阻力位可能更为有效。
时间退出不灵活:固定棒数退出可能过早关闭盈利交易或过晚关闭亏损交易。可以考虑结合趋势或动量指标来动态调整退出时机。
计算复杂性:策略使用多种复杂的移动平均线算法和条件组合,可能增加计算负担并导致执行延迟。在实时交易中,可能需要简化某些计算密集型指标。
动态阈值调整:当前策略使用固定的相对成交量%R阈值(27),可以考虑实现自适应阈值,根据最近的成交量波动性自动调整。这将使策略更好地适应不同的市场条件和季节性变化。
多时间框架确认:引入更高时间框架的确认信号,只在较大趋势方向上交易,可以提高策略的胜率和风险回报比。例如,只有当日线趋势向上时才执行小时线上的多头信号。
成交量质量分析:除了相对成交量之外,还可以加入成交量扩散指标或成交量分布分析,以评估成交量的质量而非仅仅是数量。这有助于区分健康的趋势确认成交量和潜在的耗尽信号。
智能止损/止盈:当前的ATR基础止损/止盈可以改进为更智能的系统,例如基于关键支撑/阻力位置,或使用波动性调整的止损,在低波动时期收紧止损,在高波动时期放宽止损。
结合市场结构:将价格结构分析(如支撑/阻力、趋势线、价格通道)整合到策略中,可以提高入场点和退出点的质量。
风险管理增强:实现动态仓位大小调整,基于当前的市场波动性和最近的策略表现,在高胜率环境中增加仓位,在不确定时期减少仓位。
机器学习整合:使用机器学习算法来动态优化策略参数或预测哪种价格行为过滤器在当前市场条件下最有效,可以进一步提高策略性能。
相对成交量百分比动量交易策略是一个全面而灵活的交易系统,通过结合成交量分析、多种价格行为过滤器和动态风险管理技术,为交易者提供了一个强大的工具来识别潜在的市场机会。该策略的核心优势在于其适应性和可定制性,允许交易者根据个人偏好和市场条件进行调整。
该策略尤其适合那些寻找基于成交量确认的反转或趋势延续信号的交易者。通过使用Williams %R风格的相对成交量指标,策略能够识别成交量突增点,这些通常代表着市场情绪的重要转变或趋势加速。同时,多种价格行为过滤选项使交易者能够根据自己的风险偏好和交易风格选择更保守或更激进的入场条件。
尽管该策略提供了许多优势,但交易者应该注意潜在的过度优化风险和市场环境依赖性。通过持续测试和调整,结合建议的优化方向,交易者可以进一步增强这个策略的稳健性和长期盈利能力。最终,像所有交易策略一样,成功的关键在于深入理解其原理、明智地管理风险,并在不同市场条件下持续评估其表现。
/*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)