使用Backtesting回测yfinance数据时遇TypeError: other must be a MultiIndex报错
解决yfinance数据回测中的MultiIndex类型错误
问题场景
使用yfinance下载行情数据,结合回测框架编写策略时,触发TypeError: other must be a MultiIndex or a list of tuples错误,尝试修改索引、重置索引均无法解决。原代码如下:
temp = yf.download( symb, interval=interval, period=period ) temp.reset_index(inplace=True) print(temp.columns) class MyStrategy(Strategy): stop_factor = 0.02 # stop loss factor take_profit_factor = 0.04 # take profit factor for 1:2 risk-reward ratio def init(self): # Set up signals self.signal = self.data['signals'] def next(self): # If the signal is a buy, we want to buy if self.signal == 1: self.buy(size=1, stop=self.data.close[-1] * (1 - self.stop_factor), takeprofit=self.data.close[-1] * (1 + self.take_profit_factor)) # If the signal is a sell, we want to sell elif self.signal == -1: self.sell(size=1, stop=self.data.close[-1] * (1 + self.stop_factor), takeprofit=self.data.close[-1] * (1 - self.take_profit_factor)) # Backtest setup bt = Backtest(temp, MyStrategy, cash=10000, commission=0.002) stats = bt.run() # Print the backtest results print(stats)
报错信息
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) /usr/local/lib/python3.10/dist-packages/pandas/core/indexes/multi.py in _convert_can_do_setop(self, other) 3853 try: -> 3854 other = MultiIndex.from_tuples(other, names=self.names) 3855 except (ValueError, TypeError) as err: 8 frames ValueError: Length of names must match number of levels in MultiIndex. The above exception was the direct cause of the following exception: TypeError Traceback (most recent call last) /usr/local/lib/python3.10/dist-packages/pandas/core/indexes/multi.py in _convert_can_do_setop(self, other) 3856 # ValueError raised by tuples_to_object_array if we 3857 # have non-object dtype -> 3858 raise TypeError(msg) from err 3859 else: 3860 result_names = get_unanimous_names(self, other) TypeError: other must be a MultiIndex or a list of tuples
问题根源
回测框架(如Backtesting.py)要求输入的行情数据必须以DatetimeIndex作为索引,用于时间序列的对齐和处理。执行temp.reset_index(inplace=True)后,原本的DatetimeIndex被转换成普通列,导致框架内部处理索引时触发类型不匹配错误。
解决方案
方案1:保留DatetimeIndex并合并信号列
这是最稳妥的方式,直接保留yfinance下载数据的原生索引,同时将外部data中的Signal列合并到行情数据中:
# 下载行情数据,保留DatetimeIndex temp = yf.download(symb, interval=interval, period=period) # 将外部data中的Signal列合并到temp(确保两者索引对齐) temp['signals'] = data['Signal'] class MyStrategy(Strategy): stop_factor = 0.02 # 止损比例 take_profit_factor = 0.04 # 止盈比例(1:2风险收益比) def init(self): # 绑定信号列,使用属性访问更稳定 self.signal = self.data.signals def next(self): # 获取当前bar的信号值(注意不是整个列) current_signal = self.signal[-1] if current_signal == 1: current_close = self.data.close[-1] self.buy( size=1, stop=current_close * (1 - self.stop_factor), takeprofit=current_close * (1 + self.take_profit_factor) ) elif current_signal == -1: current_close = self.data.close[-1] self.sell( size=1, stop=current_close * (1 + self.stop_factor), takeprofit=current_close * (1 - self.take_profit_factor) ) # 初始化回测,此时temp的索引是DatetimeIndex,符合框架要求 bt = Backtest(temp, MyStrategy, cash=10000, commission=0.002) stats = bt.run() print(stats)
方案2:重置索引后显式指定时间列
如果必须重置索引(比如有特殊数据处理需求),可以在初始化回测时显式指定时间列参数(Backtesting.py支持datetime_column参数):
temp = yf.download(symb, interval=interval, period=period) temp.reset_index(inplace=True) # 合并信号列(确保数据长度一致) temp['signals'] = data['Signal'].values # 初始化回测时指定时间列名称(通常是'Datetime') bt = Backtest(temp, MyStrategy, cash=10000, commission=0.002, datetime_column='Datetime') stats = bt.run() print(stats)
内容的提问来源于stack exchange,提问作者user28679678
相关产品推荐
相关产品推荐

