使用Backtesting.py的resample_apply()遇TypeError:索引类型不符
我尝试通过以下代码将5分钟级别的OHLCV数据重采样为1小时级别并计算RSI指标:
self.rsi_5m = self.I(ta.rsi, pd.Series(self.data.Close), self.rsi_window) self.rsi_1h = resample_apply("4H", ta.rsi, pd.Series(self.data.Close), self.rsi_window)
已验证数据集索引为DatetimeIndex:<class 'pandas.core.indexes.datetimes.DatetimeIndex'>,数据集示例:
Open High Low Close Volume Time 2022-07-18 12:15:00 22059.0 22068.9 21936.7 21971.6 4718.322 2022-07-18 12:20:00 21971.6 21997.5 21900.7 21915.6 4562.186 ... ... ... ... ... ... 2022-11-02 15:15:00 20399.7 20401.6 20397.5 20398.0 145.078
但运行时触发TypeError:
TypeError: Only valid with DatetimeIndex, TimedeltaIndex or PeriodIndex, but got an instance of 'RangeIndex'
完整策略代码:
class RsiOscillator(Strategy): upper_bound = 60 lower_bound = 33 rsi_window = 14 # Do as much initial computation as possible def init(self): self.rsi_5m = self.I(ta.rsi, pd.Series(self.data.Close), self.rsi_window) self.rsi_1h = resample_apply("H", ta.rsi, pd.Series(self.data.Close), self.rsi_window) # Step through bars one by one # Note that multiple buys are a thing here def next(self): if crossover(self.rsi_5m, self.upper_bound): self.sell() elif crossover(self.lower_bound, self.rsi_5m): self.position.close() df = pd.read_csv("5m_ohlcv.csv", parse_dates=["Time"], index_col=["Time"]) print(df) bt = Backtest(df, RsiOscillator, cash=100_000, commission=.002) stats = bt.optimize( upper_bound=range(50, 85, 5), lower_bound=range(10, 45, 5), maximize='Equity Final [$]' ) print(stats) print(stats["_strategy"].upper_bound) print(stats["_strategy"].lower_bound) bt.plot(resample=False)
为什么已经确认索引是DatetimeIndex,还会触发这个错误?
核心原因
问题出在resample_apply的参数传递上:你直接把pd.Series(self.data.Close)传入,但这个Series丢失了原DatetimeIndex——因为在回测框架中,self.data.Close返回的是不带索引的数值序列(或内部封装的对象),手动转成pd.Series时默认生成了RangeIndex,而非原数据的DatetimeIndex。
解决方法
1. 正确构造带原索引的Series
不要直接用pd.Series(self.data.Close),而是基于整个数据的索引构造完整的Series:
self.rsi_1h = resample_apply("H", ta.rsi, pd.Series(self.data.Close.values, index=self.data.index), self.rsi_window)
2. 使用框架内置的重采样逻辑(更推荐)
多数回测框架的resample_apply可以直接接收self.data.Close对象,无需手动转Series,比如:
self.rsi_1h = resample_apply("H", ta.rsi, self.data.Close, self.rsi_window)
(注:不同框架的resample_apply实现略有差异,若上述写法报错,可尝试传入整个self.data,再在回调中提取Close列)
3. 排查优化环节的索引丢失
代码中使用了bt.optimize,优化过程中框架可能对数据进行切片/复制,导致临时数据的索引被重置。可以先注释掉optimize代码,单独运行回测验证是否解决问题,再逐步排查优化环节的索引处理。
验证步骤
在init方法中添加打印语句,确认传入resample_apply的Series索引类型:
close_series = pd.Series(self.data.Close) print(type(close_series.index)) # 此处会输出RangeIndex,验证问题所在
内容的提问来源于stack exchange,提问作者Slava Burmistrov

