为何使用Python ta库计算RSI时结果随起始位置变化?
问题
我有一个DataFrame,想要基于Close列以14为窗口计算RSI,代码如下:
from ta.momentum import RSIIndicator import pandas as pd data = pd.read_csv() output = RSIIndicator(data.Close, 14).rsi() print(output.head(20))
得到的RSI结果如下:
0 NaN 1 NaN 2 NaN 3 NaN 4 NaN 5 NaN 6 NaN 7 NaN 8 NaN 9 NaN 10 NaN 11 NaN 12 NaN 13 30.565576 14 30.565576 15 30.565576 16 36.847817 17 53.471152 18 53.471152 19 59.140918
但当我从任意其他位置开始计算,例如使用data.iloc[1:]时,我理解由于偏移了1个位置,原13索引会变为NaN,RSI从14索引开始,但为何结果值会改变?
window = 14 t = RSIIndicator(data.Close.iloc[1:], window).rsi() print(t.head(20))
输出结果:
1 NaN 2 NaN 3 NaN 4 NaN 5 NaN 6 NaN 7 NaN 8 NaN 9 NaN 10 NaN 11 NaN 12 NaN 13 NaN 14 31.481498 15 31.481498 16 37.849374 17 54.534367 18 54.534367 19 60.171078 20 44.372719
RSI不是只需要前14个值吗?为何缺少最旧的第15个值会导致结果变化?这对我实时计算RSI很重要,我希望新数据到来时,仅传入前14个数据点就能得到下一个RSI值,但现在似乎必须传入完整数据集。
回答
核心原因是ta库的RSIIndicator默认用**平滑移动平均(EMA)**计算平均涨跌幅,而非简单移动平均(SMA)。EMA是带权重的计算方式,初始阶段会用到序列开头到当前窗口的所有历史数据,不是只取最近14个。当你砍掉原始序列的第一个数据点(用data.iloc[1:]),EMA的初始计算基础完全改变,后续所有RSI值自然会跟着变化。
针对你的实时计算需求,有两个可行方案:
1. 改用简单移动平均(SMA)实现RSI
SMA仅依赖最近N个周期的数据,完全符合你“只传前14个点算下一个RSI”的需求,手动实现代码如下:
def calculate_rsi_sma(prices, window=14): deltas = prices.diff() # 分离上涨和下跌幅度 gains = deltas.where(deltas > 0, 0) losses = -deltas.where(deltas < 0, 0) # 用滚动窗口计算简单平均涨跌幅 avg_gain = gains.rolling(window=window, min_periods=window).mean() avg_loss = losses.rolling(window=window, min_periods=window).mean() rs = avg_gain / avg_loss rsi = 100 - (100 / (1 + rs)) return rsi # 测试:切片前后对应位置的RSI值会一致(仅索引偏移) rsi_full = calculate_rsi_sma(data.Close, 14) rsi_slice = calculate_rsi_sma(data.Close.iloc[1:], 14)
2. 保留EMA逻辑但维护滚动状态
如果一定要用EMA版本的RSI,不能每次切片从头计算,必须维护历史序列的状态:
# 初始化:先传入前14个数据建立初始EMA状态 historical_prices = data.Close.iloc[:14].copy() rsi_indicator = RSIIndicator(historical_prices, 14) initial_rsi = rsi_indicator.rsi() # 实时更新:新数据到来时追加到历史序列后 new_price = data.Close.iloc[14] historical_prices = pd.concat([historical_prices, pd.Series([new_price])]) # 取最新的RSI值 current_rsi = RSIIndicator(historical_prices, 14).rsi().iloc[-1]
简单总结:
- 你看到的数值差异是因为
ta的RSI用了EMA,依赖全部历史数据计算初始值 - 实时计算要轻量化,优先选SMA版本的RSI,仅需最近N个数据点
- 坚持用EMA的话,必须维护完整的历史滚动序列,不能切片从头算
内容的提问来源于stack exchange,提问作者bcsta
相关产品推荐
相关产品推荐

