Momentum:RVGI(相对活力指数)技术指标详解
一、RVGI的定义
RVGI(Relative Vigor Index,相对活力指数) 是由John Ehlers开发的一种动量震荡指标,用于衡量趋势的强度和延续可能性。RVGI与随机振荡器(Stochastic Oscillator)在公式上具有相似性,常被视为其"近亲"。
核心思想
RVGI的设计基于一个核心观察:在上涨趋势中,收盘价倾向于处于K线的高位(高于开盘价);而在下跌趋势中,收盘价倾向于处于K线的低位(低于开盘价)。
基于这一原理,RVGI通过比较收盘价与开盘价的差值 和最高价与最低价的差值之间的比率,来量化当前价格走势的"活力"或"强度"。
RVGI的核心特征
| 特征 | 说明 |
|---|---|
| 理论基础 | 收盘价在价格区间中的相对位置反映趋势强度 |
| 指标类型 | 动量震荡指标 |
| 数值形式 | 围绕0轴波动的震荡指标,可为正值或负值 |
| 开发者 | John Ehlers |
| 默认参数 | length=14, swma_length=4 |
RVGI的双线结构
RVGI指标由两条线组成:
| 组成部分 | 名称 | 计算方式 | 功能 |
|---|---|---|---|
| RVGI线 | 主震荡线 | 原始RVGI值 | 反映当前趋势强度 |
| 信号线(Signal Line) | 触发线 | RVGI值的SWMA平滑 | 产生交叉交易信号 |
二、RVGI的计算方法
1. 核心计算公式
RVGI的计算包含多个层次,使用对称加权移动平均(SWMA)进行平滑处理。
第一步:计算原始能量值
对于每个周期,首先计算两个基础量:
CloseOpen=Close−OpenHighLow=High−Low \begin{aligned} \mathrm{CloseOpen} &= \mathrm{Close−Open} \\1.5ex \mathrm{HighLow} &= \mathrm{High−Low} \end{aligned} CloseOpenHighLow=Close−Open=High−Low
其中:
- Close 和 Open 分别为收盘价和开盘价
- High 和 Low 分别为最高价和最低价
- 确保分母不为零(需保证HighLow > 0)
第二步:应用对称加权移动平均(SWMA)
对上述两个差值分别应用SWMA平滑:
SWMACloseOpen=SWMA(CloseOpen,swma_length) \mathrm{SWMA}_{\mathrm{CloseOpen}} = \mathrm{SWMA}(\mathrm{CloseOpen}, \mathrm{swma\_length}) SWMACloseOpen=SWMA(CloseOpen,swma_length)
SWMAHighLow=SWMA(HighLow,swma_length) \mathrm{SWMA}_{\mathrm{HighLow}} = \mathrm{SWMA}(\mathrm{HighLow}, \mathrm{swma\_length}) SWMAHighLow=SWMA(HighLow,swma_length)
其中 swma_length\mathrm{swma\_length}swma_length 为 SWMA\mathrm{SWMA}SWMA 周期,默认值为 444。
对称加权移动平均(SWMA)说明:SWMA对窗口内的数据赋予对称权重,通常中间位置的权重最高,两端权重最低,能够有效平滑数据同时保留趋势特征。
第三步:计算周期累加和
对平滑后的值在 length\mathrm{length}length 周期内求和(默认 length=14\mathrm{length}=14length=14):
SumCloseOpent=∑i=t−length+1tSWMACloseOpen,i \mathrm{SumCloseOpen}t = \sum{i=t-\mathrm{length}+1}^{t} \mathrm{SWMA}_{\mathrm{CloseOpen},i} SumCloseOpent=i=t−length+1∑tSWMACloseOpen,i
SumHighLowt=∑i=t−length+1tSWMAHighLow,i \mathrm{SumHighLow}t = \sum{i=t-\mathrm{length}+1}^{t} \mathrm{SWMA}_{\mathrm{HighLow},i} SumHighLowt=i=t−length+1∑tSWMAHighLow,i
第四步:计算RVGI值
RVGI=SumCloseOpenSumHighLow \mathrm{RVGI} = \frac{\mathrm{SumCloseOpen}}{\mathrm{SumHighLow}} RVGI=SumHighLowSumCloseOpen
第五步:计算信号线
信号线是RVGI值的SWMA平滑:
Signal=SWMA(RVGI,swma_length) \mathrm{Signal = SWMA(RVGI,swma\_length)} Signal=SWMA(RVGI,swma_length)
其中 swma_length\mathrm{swma\_length}swma_length 同样默认为4。
2. 完整公式汇总
将上述步骤合并,RVGI的完整计算公式为:
RVGIt=∑i=0length−1SWMA(CloseOpen,swma_length)t−i∑i=0length−1SWMA(HighLow,swma_length)t−i \mathrm{RVGI}t = \frac{\sum{i=0}^{\mathrm{length}-1} \mathrm{SWMA}(\mathrm{CloseOpen}, \mathrm{swma\length}){t-i}}{\sum_{i=0}^{\mathrm{length}-1} \mathrm{SWMA}(\mathrm{HighLow}, \mathrm{swma\length}){t-i}} RVGIt=∑i=0length−1SWMA(HighLow,swma_length)t−i∑i=0length−1SWMA(CloseOpen,swma_length)t−i
Signalt=SWMA(RVGI,swma_length)t \mathrm{Signal}_t = \mathrm{SWMA}(\mathrm{RVGI}, \mathrm{swma\_length})_t Signalt=SWMA(RVGI,swma_length)t
3. 参数说明
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
length |
int | 14 | RVGI累加周期,控制指标灵敏度 |
swma_length |
int | 4 | SWMA平滑周期,控制信号线的平滑度 |
offset |
int | 0 | 结果偏移周期数 |
4. 计算示例
假设某股票使用默认参数(length=14, swma_length=4),当前周期的基础数据:
| 变量 | 说明 | 假设值 |
|---|---|---|
| SWMA₍CloseOpen₎ | 平滑后的收盘-开盘均值 | 0.15 |
| SWMA₍HighLow₎ | 平滑后的最高-最低均值 | 0.10 |
| SumCloseOpen | 14周期累加和 | 2.10 |
| SumHighLow | 14周期累加和 | 1.40 |
RVGI计算 :
RVGI=SumCloseOpenSumHighLow=2.101.40=1.5 \mathrm{RVGI} = \frac{\mathrm{SumCloseOpen}}{\mathrm{SumHighLow}} = \frac{2.10}{1.40} = 1.5 RVGI=SumHighLowSumCloseOpen=1.402.10=1.5
信号线计算:
假设前4个周期的RVGI值为:1.5, 1.4, 1.3, 1.2
Signal=SQMA(1.5,1.4,1.3,1.2)≈1.35 \mathrm{Signal} = \mathrm{SQMA}(1.5,1.4,1.3,1.2) \approx 1.35 Signal=SQMA(1.5,1.4,1.3,1.2)≈1.35
该RVGI=1.5 > Signal=1.35,RVGI线在信号线上方,表明买方力量较强。
三、RVGI的使用方法
1. 交叉信号------最核心用法
RVGI与信号线的交叉是生成交易信号最基础也是最核心的方法:
| 信号类型 | 触发条件 | 含义 | 操作建议 |
|---|---|---|---|
| 看涨交叉(金叉) | RVGI线从下方上穿信号线 | 买方力量开始增强,趋势可能转为上涨 | 考虑买入/做多 |
| 看跌交叉(死叉) | RVGI线从上方下穿信号线 | 卖方力量开始增强,趋势可能转为下跌 | 考虑卖出/做空 |
信号增强条件:当交叉发生在RVGI值远离0轴的区域时,信号的可靠性更高。
2. 超买超卖判断
RVGI作为围绕0轴波动的震荡指标,其极端值可反映市场的过热或过冷状态:
| RVGI值 | 状态 | 含义 | 操作建议 |
|---|---|---|---|
| 高正值 | 超买 | 买方力量过度释放,市场可能过热 | 警惕回调,考虑减仓 |
| 低负值 | 超卖 | 卖方力量过度释放,市场可能过度恐慌 | 关注反弹机会,考虑分批布局 |
注意:与RSI等固定范围的指标不同,RVGI的"高"和"低"需结合具体品种的历史波动范围来判断,没有统一固定的阈值。
3. 背离信号------反转预警
RVGI与价格走势的背离是重要的反转预警信号:
| 背离类型 | 价格表现 | RVGI表现 | 信号含义 |
|---|---|---|---|
| 顶背离 | 价格创出新高 | RVGI未能创出新高(形成更低高点) | 上涨动能衰竭,卖出信号 |
| 底背离 | 价格创出新低 | RVGI未能创出新低(形成更高低点) | 下跌动能减弱,买入信号 |
背离信号通常在趋势末端出现,是捕捉潜在反转点的有效工具。
4. 形态分析
RVGI指标线本身也会形成技术形态,可用于辅助判断:
| 形态 | 信号含义 |
|---|---|
| 双底(W底) | 在低位区域形成后,预示可能上涨 |
| 双顶(M顶) | 在高位区域形成后,预示可能下跌 |
| 头肩顶/底 | 典型的反转形态信号 |
5. 与其他指标的配合策略
RVGI的可靠性在使用单一指标时可能受限,建议与其他工具配合使用:
| 配合工具 | 策略逻辑 | 增强效果 |
|---|---|---|
| 移动平均线 | SMA交叉确认趋势方向后,结合RVGI信号入场 | 减少震荡市假信号 |
| RSI(相对强弱指数) | RVGI与RSI同时发出超买/超卖信号时可靠性更高 | 多重验证,提高胜率 |
| KDJ(随机指标) | 两个震荡指标同时出现信号时互相验证 | 降低误判风险 |
| MACD | RVGI提供早入场信号,MACD确认趋势延续 | 取长补短 |
| 布林带 | RVGI超买/超卖信号 + 价格突破布林带中轨确认 | 增加入场信心 |
6. 注意事项与局限性
使用RVGI前需了解以下要点*:
| 局限性 | 说明 |
|---|---|
| 可能产生假信号 | 与其他技术指标一样,RVGI在震荡市场中可能产生虚假的交叉信号 |
| 需要其他工具确认 | 不建议将RVGI作为唯一决策依据,应结合价格行为、趋势线等确认 |
| 适用性限制 | 在24/7连续交易且无明确开收盘的加密市场中,RVGI的有效性可能受限 |
| 参数敏感 | length和swma_length参数的调整会显著影响指标的灵敏度和平滑度 |
| 初期预热需求 | RVGI需要足够的历史数据完成累加和平滑计算,初期可能出现无效值 |
四、使用pandas_ta计算RVGI(附示例代码)
1. pandas_ta中的RVGI函数
pandas_ta库内置了RVGI指标的完整实现,函数位于momentum模块中。
2. 函数完整参数
python
pandas_ta.momentum.rvgi(open_, high, low, close, length=None, swma_length=None, offset=None, **kwargs)
参数详解:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
open_ |
pd.Series | 必需 | 开盘价序列(注意下划线后缀) |
high |
pd.Series | 必需 | 最高价序列 |
low |
pd.Series | 必需 | 最低价序列 |
close |
pd.Series | 必需 | 收盘价序列 |
length |
int | 14 | RVGI累加周期 |
swma_length |
int | 4 | 对称加权移动平均周期 |
offset |
int | 0 | 结果偏移周期数 |
返回值 :pd.DataFrame------包含两列:
RVGI_{length}_{swma_length}:RVGI主线RVGIs_{length}_{swma_length}:信号线(Signal Line)
3. 示例代码
python
import pandas as pd
import pandas_ta as ta
import numpy as np
import matplotlib.pyplot as plt
# ========== 第一步:准备数据 ==========
np.random.seed(42)
dates = pd.date_range(start='2023-01-01', end='2024-12-31', freq='D')
n = len(dates)
# 生成带趋势和周期波动的价格序列
trend = np.linspace(0, 35, n)
cycle = np.sin(np.linspace(0, 6 * np.pi, n)) * 12
noise = np.random.randn(n) * 2.5
price_series = 100 + trend + cycle + noise
# 添加一个更真实的趋势段:中期上涨 + 下跌 + 盘整
price_series = price_series.astype(float)
for i in range(200, 350):
price_series[i] = price_series[200] + (i-200) * 0.15
for i in range(350, 500):
price_series[i] = price_series[350] - (i-350) * 0.12
for i in range(500, 650):
price_series[i] = price_series[500] + np.random.randn() * 0.5
# 生成OHLC数据
df = pd.DataFrame(index=dates[:n])
df['close'] = price_series[:n]
df['open'] = df['close'] + np.random.randn(n) * 1.5
df['high'] = df[['open', 'close']].max(axis=1) + np.abs(np.random.randn(n)) * 2 + 1
df['low'] = df[['open', 'close']].min(axis=1) - np.abs(np.random.randn(n)) * 2 - 1
df['volume'] = np.random.randint(1000000, 20000000, n)
print("=" * 60)
print("数据预览:")
print(df.head())
print("\n" + "=" * 60 + "\n")
# ========== 第二步:计算RVGI(基础用法) ==========
# 使用默认参数 length=14, swma_length=4
rvgi_df = ta.rvgi(df['open'], df['high'], df['low'], df['close'])
# 查看返回的DataFrame结构
print("RVGI返回列名称:")
print(rvgi_df.columns.tolist())
print("\n" + "=" * 60 + "\n")
# 将计算结果添加到原DataFrame
df['RVGI'] = rvgi_df.iloc[:, 0] # RVGI主线
df['Signal'] = rvgi_df.iloc[:, 1] # 信号线
print("RVGI计算结果(最近10行):")
print(df[['close', 'RVGI', 'Signal']].tail(10))
print("\n" + "=" * 60 + "\n")
# ========== 第三步:手动验证RVGI计算 ==========
def manual_rvgi(open_, high, low, close, length=14, swma_length=4):
"""手动计算RVGI验证pandas_ta结果"""
# 步骤1:计算原始差值
close_open = close - open_
high_low = high - low
# 步骤2:应用SWMA(使用简单加权移动平均近似)
def swma(series, length):
"""对称加权移动平均近似"""
weights = np.arange(1, length + 1)
weights = np.minimum(weights, weights[::-1])
weights = weights / weights.sum()
return series.rolling(window=length).apply(
lambda x: np.sum(weights * x), raw=True
)
# 步骤3:SWMA平滑
swma_co = swma(close_open, swma_length)
swma_hl = swma(high_low, swma_length)
# 步骤4:累加求和
sum_co = swma_co.rolling(window=length).sum()
sum_hl = swma_hl.rolling(window=length).sum()
# 步骤5:计算RVGI
rvgi = sum_co / sum_hl
# 步骤6:计算信号线
signal = swma(rvgi, swma_length)
return rvgi, signal
df['RVGI_manual'], df['Signal_manual'] = manual_rvgi(
df['open'], df['high'], df['low'], df['close']
)
# 验证一致性
diff_rvgi = (df['RVGI'] - df['RVGI_manual']).abs().max()
print(f"RVGI主线手动验证最大差异:{diff_rvgi:.10f}")
print("\n" + "=" * 60 + "\n")
# ========== 第四步:不同参数的RVGI对比 ==========
# 短周期参数 (length=9, swma_length=3)
rvgi_short = ta.rvgi(df['open'], df['high'], df['low'], df['close'], length=9, swma_length=3)
df['RVGI_short'] = rvgi_short.iloc[:, 0]
df['Signal_short'] = rvgi_short.iloc[:, 1]
# 长周期参数 (length=21, swma_length=5)
rvgi_long = ta.rvgi(df['open'], df['high'], df['low'], df['close'], length=21, swma_length=5)
df['RVGI_long'] = rvgi_long.iloc[:, 0]
df['Signal_long'] = rvgi_long.iloc[:, 1]
print("不同参数RVGI对比(最近5行):")
print(df[['close', 'RVGI', 'RVGI_short', 'RVGI_long']].tail())
print("\n" + "=" * 60 + "\n")
# ========== 第五步:生成交叉信号 ==========
# 检测RVGI与信号线的交叉
df['RVGI_above_signal'] = df['RVGI'] > df['Signal']
df['cross_above'] = (df['RVGI_above_signal'] == True) & (df['RVGI_above_signal'].shift(1) == False)
df['cross_below'] = (df['RVGI_above_signal'] == False) & (df['RVGI_above_signal'].shift(1) == True)
df['signal'] = ''
df.loc[df['cross_above'], 'signal'] = '金叉(买入信号)'
df.loc[df['cross_below'], 'signal'] = '死叉(卖出信号)'
print("交叉信号统计:")
print(f"金叉数量:{df['cross_above'].sum()}")
print(f"死叉数量:{df['cross_below'].sum()}")
print("\n最近10个交叉信号:")
signals = df[df['signal'] != ''].tail(10)
if not signals.empty:
print(signals[['close', 'RVGI', 'Signal', 'signal']])
else:
print("近期无交叉信号")
print("\n" + "=" * 60 + "\n")
# ========== 第六步:零轴穿越分析 ==========
df['RVGI_above_zero'] = df['RVGI'] > 0
df['cross_above_zero'] = (df['RVGI_above_zero'] == True) & (df['RVGI_above_zero'].shift(1) == False)
df['cross_below_zero'] = (df['RVGI_above_zero'] == False) & (df['RVGI_above_zero'].shift(1) == True)
print("零轴穿越信号统计:")
print(f"上穿零轴数量:{df['cross_above_zero'].sum()}")
print(f"下穿零轴数量:{df['cross_below_zero'].sum()}")
print("\n" + "=" * 60 + "\n")
# ========== 第七步:RVGI与价格背离检测 ==========
# 计算局部极值点(简化检测)
def find_local_extrema(series, window=5):
"""寻找局部极值点"""
peaks = []
troughs = []
for i in range(window, len(series) - window):
if series.iloc[i] == max(series.iloc[i-window:i+window+1]):
peaks.append(i)
if series.iloc[i] == min(series.iloc[i-window:i+window+1]):
troughs.append(i)
return peaks, troughs
# 简化版背离检测(20日方向对比)
df['price_change_20d'] = df['close'].diff(20)
df['rvgi_change_20d'] = df['RVGI'].diff(20)
# 潜在顶背离:价格涨但RVGI跌
df['potential_bearish_div'] = (df['price_change_20d'] > 0) & (df['rvgi_change_20d'] < 0)
# 潜在底背离:价格跌但RVGI涨
df['potential_bullish_div'] = (df['price_change_20d'] < 0) & (df['rvgi_change_20d'] > 0)
print("潜在背离信号统计(20日尺度):")
print(f"潜在顶背离(价格涨RVGI跌)数量:{df['potential_bearish_div'].sum()}")
print(f"潜在底背离(价格跌RVGI涨)数量:{df['potential_bullish_div'].sum()}")
print("注:完整背离需结合局部高/低点分析,此处为简化方向对比")
print("\n" + "=" * 60 + "\n")
# ========== 第八步:策略回测(交叉信号策略) ==========
# 策略:RVGI > Signal 时持仓多头
df['position_cross'] = (df['RVGI'] > df['Signal']).astype(int)
# 策略2:RVGI > 0 时持仓多头
df['position_zero'] = (df['RVGI'] > 0).astype(int)
# 计算收益
df['returns'] = df['close'].pct_change()
df['strategy_cross_returns'] = df['position_cross'].shift(1) * df['returns']
df['strategy_zero_returns'] = df['position_zero'].shift(1) * df['returns']
total_return_buyhold = (1 + df['returns']).prod() - 1
total_return_cross = (1 + df['strategy_cross_returns']).prod() - 1
total_return_zero = (1 + df['strategy_zero_returns']).prod() - 1
print("=" * 60)
print("策略绩效统计(RVGI策略回测):")
print(f"买入持有策略总收益率:{total_return_buyhold:.2%}")
print(f"RVGI > Signal 策略总收益率:{total_return_cross:.2%}")
print(f"RVGI > 0 策略总收益率:{total_return_zero:.2%}")
print("\n注意:此为简化回测,仅供参考")
print("=" * 60 + "\n")
# ========== 第九步:可视化 ==========
plt.figure(figsize=(14, 12))
# 子图1:价格走势
plt.subplot(2, 1, 1)
plt.plot(df.index[-200:], df['close'][-200:], label='Close Price',
linewidth=1.5, color='black')
plt.title('Price Chart (Last 200 days)', fontsize=14)
plt.ylabel('Price')
plt.legend()
plt.grid(True, alpha=0.3)
# 子图2:RVGI指标
plt.subplot(2, 1, 2)
plt.plot(df.index[-200:], df['RVGI'][-200:], label='RVGI线',
linewidth=1.5, color='blue')
plt.plot(df.index[-200:], df['Signal'][-200:], label='信号线',
linewidth=1.5, color='orange', linestyle='--')
plt.axhline(y=0, color='gray', linestyle='-', linewidth=1, label='零轴')
# 标记交叉点
buy_signals = df[df['cross_above']]
sell_signals = df[df['cross_below']]
plt.scatter(buy_signals.index[-30:], buy_signals['RVGI'][-30:],
color='green', marker='^', s=80, label='金叉(买入)', alpha=0.8)
plt.scatter(sell_signals.index[-30:], sell_signals['RVGI'][-30:],
color='red', marker='v', s=80, label='死叉(卖出)', alpha=0.8)
plt.title('RVGI (Relative Vigor Index) - 相对活力指数', fontsize=14)
plt.xlabel('Date')
plt.ylabel('RVGI Value')
plt.legend(loc='upper left')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# ========== 第十步:数据清洗提示 ==========
nan_count = df['RVGI'].isna().sum()
print(f"\nRVGI初始NaN数量:{nan_count}")
print("原因:RVGI需要至少 max(length, swma_length) 个周期的数据才能计算")
print("处理建议:df_clean = df.iloc[14:].copy()")
print("\n" + "=" * 60)
print("RVGI使用提示:")
print("1. RVGI > Signal 为多头信号,RVGI < Signal 为空头信号")
print("2. 金叉/死叉是最核心的交易信号,建议在零轴附近使用时更谨慎")
print("3. 建议与移动平均线、RSI等工具配合使用,提高信号可靠性")
print("4. 在强趋势行情中,RVGI可能长期处于极端值区域,此时应以趋势跟踪为主")
print("=" * 60)
五、总结
RVGI(相对活力指数)是一种基于价格区间位置理论的动量震荡指标,其核心价值与定位如下:
| 维度 | 特点 |
|---|---|
| 核心原理 | 上涨趋势中收盘价趋于高位,下跌趋势中收盘价趋于低位 |
| 核心公式 | SWMA(Close-Open)累加和 ÷ SWMA(High-Low)累加和 |
| 双线结构 | RVGI线 + 信号线(SWMA平滑) |
| 三大核心信号 | 金叉/死叉、零轴穿越、顶底背离 |
| 默认参数 | length=14, swma_length=4 |
| 最佳应用场景 | 趋势确认、动量判断、多指标组合策略 |
| 主要局限 | 可能产生假信号、需配合其他工具确认、参数敏感 |
实战使用三原则:
- 交叉信号优先:RVGI线与信号线的金叉/死叉是最可靠的交易信号,应作为核心入场/出场依据
- 多指标验证提高胜率:RVGI应与移动平均线、RSI或MACD等指标配合使用,通过多重确认减少假信号
- 背离是最强预警:当RVGI与价格出现背离时,往往是重要的趋势反转信号,应给予高度重视
最后提醒:RVGI的价值在于帮助交易者回答"当前趋势的强度是否可持续"这一问题。它通过比较收盘价在价格区间中的相对位置来量化市场动力。与其他震荡指标类似,RVGI在单一使用时可能产生假信号,建议将其作为多指标分析体系中的一环。当RVGI与价格行为、趋势线、移动平均线等工具发出共振信号时,其指示意义才最为可靠。