股票交易回测报错:'Series'对象无'iteritems'属性
问题背景
我在运行Stefan Jansen《Machine Learning for Algorithmic Trading》第二版第五章第二节backtest_with_pf_optimization的代码(基于原仓库fork后的GitHub代码,开发环境为VSCode)时,调用extract_rets_pos_txn_from_zipline(backtest)触发了AttributeError: 'Series' object has no attribute 'iteritems'。
报错根源在pyfolio.utils.py中:代码尝试遍历backtest.positions,但该对象实际是Series类型,而非函数预期的DataFrame。我已通过print(type(backtest['column_name']))验证backtest中的相关列均为Series,但无法理解为何会出现这种类型不匹配——根据pyfolio文档,该函数应该返回returns(Series)、positions(DataFrame)、transactions(DataFrame),这个问题直接卡住了后续代码执行。
可能的原因与解决办法
1. 检查Zipline回测结果结构
Zipline不同版本的回测输出结构存在差异:
- 确认你使用的Zipline版本是否与原书一致。较新的Zipline版本中,
backtest.positions可能被封装为Series(每个元素是持仓信息字典),而非直接的DataFrame。 - 先执行
print(backtest.positions.head())查看内容:如果每个元素是包含持仓细节的字典,需要手动展开为DataFrame。
2. 手动将positions Series转换为DataFrame
如果backtest.positions是存储字典的Series,执行以下代码转换:
import pandas as pd # 展开Series中的字典为结构化DataFrame positions_df = pd.DataFrame(list(backtest.positions.values)) # 对齐索引与回测时间轴 positions_df.index = backtest.index # 替换原backtest中的positions字段 backtest['positions'] = positions_df
完成转换后再调用extract_rets_pos_txn_from_zipline(backtest)。
3. 适配pyfolio版本差异
pyfolio新版本可能修改了Zipline输出的处理逻辑:
- 尝试降级到原书使用的pyfolio版本(如0.9.2):
pip install pyfolio==0.9.2
- 若不想降级,直接修改
pyfolio.utils.py中的报错代码:将iteritems()替换为items()(pandas 2.0+移除了Series的iteritems()方法,改用items())。找到对应代码行,把for k, v in backtest.positions.iteritems()改为for k, v in backtest.positions.items()。
4. 验证回测结果的保存/加载逻辑
如果自定义了回测结果的保存或加载流程,可能意外将DataFrame字段转为Series:
- 若用
to_csv保存回测结果,嵌套结构会被解析为字符串,加载后变成Series。这种情况建议改用pickle或json格式保存,以保留原始数据结构。
内容的提问来源于stack exchange,提问作者LetsGetTr0pical

