问题描述
download_financial_data2 下载的有些数据不准确如下:
000657.SZ Income 转换后的数据:
[{'m_anntime': '20250827', 'm_timetag': '20250630', 'oper_profit': 669710518.64}, {'m_anntime': '20240823', 'm_timetag': '20240630', 'oper_profit': 200754552.18}, {'m_anntime': '20250827', 'm_timetag': '20240630', 'oper_profit': 572782264.69}]
解决方案
问题分析
在使用 QMT 获取财务数据时,遇到如 000657.SZ 利润表数据中出现未来时间(如 20250827)以及同一报告期(20240630)存在多条数据的情况,通常是由以下两个原因造成的:
- 未来时间异常:底层数据源可能提前录入了上市公司的预约披露时间、业绩预告,或者是数据供应商端产生的脏数据。
- 同一报告期多条数据:上市公司在发布财报后,经常会因为会计准则变更或审计发现问题而发布财务重述(修正)公告。因此,同一个报告期(
m_timetag)会对应多个不同的公告日(m_anntime),这在量化财务数据中属于正常现象。
解决方案
为了在策略中准确使用财务数据,建议在获取数据后进行数据清洗,主要包括剔除未来数据和保留最新公告数据。
1. 重新补充本地数据
首先,建议在 QMT 客户端的【数据管理】->【补充数据】中,重新勾选并下载最新的财务数据。这可以修复部分由于本地数据未同步导致的脏数据问题。
2. 代码层面的数据清洗逻辑
在策略代码中,对获取到的财务数据列表进行以下处理:
- 过滤未来数据:剔除
m_anntime大于当前真实日期的记录。 - 去重并保留最新:按照报告期
m_timetag进行分组,如果同一报告期有多条记录,保留公告日m_anntime最大(最新)的一条。
3. Python 处理代码示例
以下是一个通用的 Python 数据清洗函数示例,适用于处理此类财务数据列表:
import datetime
import pandas as pd
def clean_financial_data(raw_data):
if not raw_data:
return []
# 获取当前日期字符串,例如 '20231025'
current_date = datetime.datetime.now().strftime('%Y%m%d')
# 1. 过滤掉公告日大于当前日期的未来数据
valid_data = [d for d in raw_data if d.get('m_anntime', '99999999') <= current_date]
if not valid_data:
return []
# 2. 转换为 DataFrame 方便处理
df = pd.DataFrame(valid_data)
# 3. 按报告期(m_timetag)排序,并按公告日(m_anntime)升序排列
df = df.sort_values(by=['m_timetag', 'm_anntime'])
# 4. 去重:同一报告期只保留最后一次公告的数据(即修正后的最终数据)
df_cleaned = df.drop_duplicates(subset=['m_timetag'], keep='last')
# 转换回字典列表
return df_cleaned.to_dict('records')
# 测试用户提供的数据
raw_data = [
{'m_anntime': '20250827', 'm_timetag': '20250630', 'oper_profit': 669710518.64},
{'m_anntime': '20240823', 'm_timetag': '20240630', 'oper_profit': 200754552.18},
{'m_anntime': '20250827', 'm_timetag': '20240630', 'oper_profit': 572782264.69}
]
cleaned_data = clean_financial_data(raw_data)
print("清洗后的数据:", cleaned_data)
4. 使用 QMT 官方 API 的建议
在 QMT 策略中,建议直接使用官方提供的 ContextInfo.get_financial_data 接口,并合理利用 report_type 参数:
report_type='announce_time':按公告期取数,严格避免未来函数,适合回测。report_type='report_time':按报告期取数,适合截面分析。
def handlebar(ContextInfo):
# 示例:获取净利润,按报告期取数
fieldList = ['ASHAREINCOME.net_profit_incl_min_int_inc']
stockList = ['000657.SZ']
# 确保传入的时间范围是合理的历史时间
df = ContextInfo.get_financial_data(fieldList, stockList, '20230101', '20231231', report_type='report_time')