You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Backtesting.py的resample_apply()遇TypeError:索引类型不符

问题:已确认DataFrame索引为DatetimeIndex,使用resample_apply仍触发RangeIndex错误

我尝试通过以下代码将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 10:05:32