问题描述
get_fundamentals(g.stock_list, 'profit_ability', 'roic', context.previous_date)返回nan。
解决方案
在 ptrade 量化平台中,调用 get_fundamentals 获取财务数据时返回 NaN,通常是由以下几个原因导致的。针对您使用的代码 get_fundamentals(g.stock_list, 'profit_ability', 'roic', context.previous_date),我们可以从以下几个方面进行排查和优化:
1. 传入的日期(date)为非交易日
原因分析:根据 ptrade 官方 API 文档,在回测和交易环境中,如果 date 参数传入的是非交易日(例如周末或法定节假日),接口将直接返回 NaN。虽然 context.previous_date 表示前一天,但如果今天是周一,context.previous_date 就是周日(非交易日),从而导致获取不到数据。
解决方法:使用 get_trading_day(-1) 来获取严格意义上的上一个交易日,而不是自然日的前一天。
2. 触发了 API 的流控限制或网络拥堵
原因分析:get_fundamentals 是通过 HTTP 在线获取数据的。官方文档明确指出,该接口有流量限制:每秒不得调用超过100次,单次最大调用量是500条数据(一条数据 = 一个股票 + 一个表的一个字段)。如果您的 g.stock_list 包含大量股票,或者在短时间内频繁调用,可能会因为触发流控或网络拥堵而导致应答失败,返回空值或 NaN。
解决方法:对长列表进行分批查询,并在代码中加入 time.sleep() 以及异常重试机制。
3. 股票代码格式不正确或确实无数据
原因分析:
- 确保
g.stock_list中的股票代码带有正确的后缀(如'600570.SS','000001.SZ')。 - 某些新上市的股票,或者在指定日期尚未发布相关财务报表的股票,其对应的财务字段本身就是缺失的,系统会用
NaN填充。
优化后的代码示例
为了解决上述问题,建议对您的代码进行如下改造,加入交易日判断、分批获取和重试机制:
import time
import pandas as pd
def get_financial_data_safe(stock_list, table, fields, retries=3):
# 1. 获取上一个有效的交易日,避免传入周末或节假日
# get_trading_day(-1) 返回的是 datetime.date 对象,符合 get_fundamentals 的要求
last_trade_date = get_trading_day(-1)
# 2. 为了防止单次请求超过 500 条数据的限制,对股票列表进行分批(假设每次查100只)
batch_size = 100
result_df = pd.DataFrame()
for i in range(0, len(stock_list), batch_size):
batch_stocks = stock_list[i:i+batch_size]
# 3. 加入重试机制,应对网络拥堵
for attempt in range(retries):
try:
# 调用接口获取数据
df = get_fundamentals(
security=batch_stocks,
table=table,
fields=fields,
date=last_trade_date
)
if df is not None and not df.empty:
result_df = pd.concat([result_df, df])
break # 成功获取则跳出重试循环
else:
log.warning(f"第 {attempt+1} 次获取为空,正在重试...")
time.sleep(1)
except Exception as e:
log.error(f"获取财务数据发生异常: {e}")
time.sleep(1)
# 批次之间稍微休眠,防止触发每秒100次的流控
time.sleep(0.5)
return result_df
# 在 handle_data 或 before_trading_start 中调用:
# roic_data = get_financial_data_safe(g.stock_list, 'profit_ability', 'roic')
# log.info(roic_data)
总结建议
- 永远使用
get_trading_day(-1)替代context.previous_date作为财务数据的查询日期。 - 注意数据清洗:即使成功获取到数据,部分股票仍可能因为未披露财报而存在
NaN,在使用数据前建议使用df.dropna()或df.fillna()进行数据清洗。 - 注意未来函数:在研究或交易环境中,如果不传入
date参数,默认会获取当天的最新数据;在回测中,为了避免未来函数,强烈建议显式传入上一个交易日的日期。