问题描述
回测的时候报错“UnboundLocalError: local variable 'int_LabelPettm' referenced before assignment”。 其中'int_LabelPettm'是我自定义的变量,用于存储对滚动市盈率的判断分类结果。
同样的代码,平时不论是交易模式还是回测模式都正常,怎么今天突然就歇菜了?经反复排查,发现是get_fundamentals返回了空值的缘故。
对应代码:
df_valuation = get_fundamentals(security, 'valuation', date = None, fields = ['pe_ttm'])#获取当前的市盈率,返回DataFrame数据
int_pettm = df_valuation.loc[security,'pe_ttm']#读取出数值
if int_pettm > 35 or int_pettm <= 0:
print(security,'PE_TTM=',int_pettm,',大于35或小于等于0,剔除')
int_LabelPettm = 3
AI写代码
python
运行
返回结果:
--> df_valuation =
trading_day pe_ttm total_value
secu_code
300437.SZ NaN NaN NaN
解决方案
问题分析
在 ptrade 平台中,您遇到的 UnboundLocalError 是因为 get_fundamentals 返回了 NaN(空值),导致您的 if 条件判断未能覆盖所有情况,从而使得 int_LabelPettm 变量未被赋值就被引用。
导致 get_fundamentals 返回 NaN 的根本原因在于 valuation(估值数据表)的特殊机制。
官方文档说明
根据 ptrade API 文档中关于 valuation 估值数据的说明:
关于 date 字段的说明
- 场景一:date 字段不入参(即
date=None)。回测中默认是获取context.blotter.current_dt交易日收盘后更新的数据,因此会产生未来函数,交易和研究会返回当日数据,若在盘中时间由于数据未更新将返回字段为 NAN 的数据,因此建议获取最新数据的场景都使用 date 参数入参上一个交易日日期。- 场景二:date 字段入参日期。回测和交易中若 date 为非交易日,将返回字段为 NAN 的数据;研究中若 date 为非交易日,将返回往前最近一个交易日的数据。
因此,当您在非交易日运行,或者在数据尚未更新的时间点运行,且 date=None 时,系统就会返回 NaN。
解决方案
为了保证策略的健壮性,建议采取以下两步进行优化:
1. 显式传入上一个交易日的日期
不要使用 date=None,而是使用 get_trading_day(-1) 获取上一个确定的交易日,并将其格式化后传入 date 参数。
2. 增加对 NaN 值的异常处理
在读取出数值后,先判断其是否为 NaN,如果是,则赋予一个默认值或直接跳过该标的。
优化后的代码示例
import pandas as pd
def handle_data(context, data):
security = '300437.SZ'
# 1. 获取上一个交易日,格式化为 YYYYMMDD
pre_date = get_trading_day(-1).strftime('%Y%m%d')
# 2. 传入确定的交易日日期
df_valuation = get_fundamentals(security, 'valuation', date=pre_date, fields=['pe_ttm'])
if df_valuation is not None and not df_valuation.empty:
int_pettm = df_valuation.loc[security, 'pe_ttm']
# 3. 增加对 NaN 的判断
if pd.isna(int_pettm):
print(f"{security} PE_TTM 数据为空 (NaN),跳过处理")
int_LabelPettm = -1 # 赋予一个默认的异常标识
else:
if int_pettm > 35 or int_pettm <= 0:
print(f"{security} PE_TTM={int_pettm},大于35或小于等于0,剔除")
int_LabelPettm = 3
else:
print(f"{security} PE_TTM={int_pettm},符合要求")
int_LabelPettm = 1 # 正常情况的赋值
# 后续使用 int_LabelPettm 的逻辑...
print(f"最终分类标签: {int_LabelPettm}")
else:
print(f"未能获取到 {security} 的估值数据")
总结
- 凡是调用
valuation表,强烈建议date参数传入get_trading_day(-1)。 - 在量化策略中,从 DataFrame 中提取数据后,务必养成使用
pd.isna()或math.isnan()检查空值的习惯,防止因数据源缺失导致策略崩溃。