问题描述
解决方案
在 QMT 中,获取股票财报公布日期主要通过 ContextInfo.get_financial_data 接口实现。
要获取财报公布日期用于交易,通常有两种思路:
- 事件驱动(推荐): 使用
report_type='announce_time'。在这种模式下,数据是按照公告日索引的。如果在某一天能取到数据,说明那天发布了财报。这是回测中最严谨的方式,能避免“未来函数”。 - 查询具体日期: 直接查询财务表中的
m_anntime(公告日)字段。
核心接口说明
ContextInfo.get_financial_data(fieldList, stockList, startDate, endDate, report_type='announce_time')
- fieldList: 字段列表。例如
['ASHAREINCOME.net_profit_incl_min_int_inc'](净利润)。如果要直接看公告日时间戳,可以查询['ASHAREINCOME.m_anntime']。 - report_type:
'announce_time'(默认): 按公告日期取数。交易策略通常使用此模式,模拟在该日期看到了财报。'report_time': 按报告期(如 3月31日、6月30日)取数。
策略代码示例
以下代码展示了一个完整的策略逻辑:
- 在
handlebar中,每天检查当天的财务数据。 - 使用
announce_time模式获取数据。 - 如果当天有新的财报数据发布,打印出公告日期和净利润,并执行模拟交易(如买入)。
# -*- coding: gbk -*-
import pandas as pd
def init(ContextInfo):
# 设置股票池,这里以平安银行和贵州茅台为例
ContextInfo.stock_list = ['000001.SZ', '600519.SH']
ContextInfo.set_universe(ContextInfo.stock_list)
# 设置资金账号(实盘或模拟盘需要)
ContextInfo.account_id = '你的资金账号'
ContextInfo.set_account(ContextInfo.account_id)
def handlebar(ContextInfo):
# 获取当前K线的时间
index = ContextInfo.barpos
current_timetag = ContextInfo.get_bar_timetag(index)
# 将时间戳转换为 YYYYMMDD 格式字符串,用于财务数据查询
current_date_str = timetag_to_datetime(current_timetag, '%Y%m%d')
# 跳过非最后一根K线(如果是实盘,只在最新K线运行;如果是回测,每根K线都运行)
# 注意:get_financial_data 在回测中需要历史数据支持
# --- 获取财务数据 ---
# 我们查询“净利润”字段,且 report_type='announce_time'
# 这意味着只有在“公告日”这一天,数据才会更新或出现
field_list = ['ASHAREINCOME.net_profit_incl_min_int_inc']
# 查询当天的财务数据
# 注意:这里 startDate 和 endDate 都设为当前日期,意在查询“今天是否有公告”
df = ContextInfo.get_financial_data(
field_list,
ContextInfo.stock_list,
current_date_str,
current_date_str,
report_type='announce_time'
)
# 如果返回的数据为空,说明今天没有股票发财报,直接返回
if df is None or df.empty:
return
# 遍历股票池检查是否有数据
for stock in ContextInfo.stock_list:
if stock in df.index:
# 获取该股票的净利润数据
# 注意:根据返回格式不同,有时需要处理 DataFrame 的结构
# 在 'announce_time' 模式下,如果当天有公告,会有数据
try:
net_profit = df.loc[stock, 'ASHAREINCOME.net_profit_incl_min_int_inc']
# 如果净利润是 NaN,说明今天该股票没有发财报
if pd.isna(net_profit):
continue
# --- 发现财报发布,执行逻辑 ---
print(f"【财报发布】日期: {current_date_str}, 股票: {stock}, 净利润: {net_profit/100000000:.2f} 亿")
# 示例交易逻辑:如果净利润大于 0,则买入
if net_profit > 0:
# 检查是否已有持仓,避免重复买入(简单示例)
positions = get_trade_detail_data(ContextInfo.account_id, 'stock', 'position')
has_position = False
for pos in positions:
if pos.m_strInstrumentID + '.' + pos.m_strExchangeID == stock:
has_position = True
break
if not has_position:
print(f"--> 触发买入信号: {stock}")
# 按最新价买入 100 股
order_shares(stock, 100, 'LATEST', 0, ContextInfo, ContextInfo.account_id)
except KeyError:
continue
# 辅助函数:将时间戳转为日期字符串
def timetag_to_datetime(timetag, format_str):
import time
# QMT的时间戳通常是毫秒,需要除以1000转为秒
return time.strftime(format_str, time.localtime(timetag / 1000))
关键点解析
-
report_type='announce_time':
这是最关键的参数。当设置为announce_time时,QMT 会根据财报的实际公告日期来对齐数据。- 如果在回测中,你在 2023-04-25 获取到了 2023-03-31(一季报)的数据,说明该财报是在 4月25日 公告的。
- 这完美模拟了真实交易场景,避免了在 3月31日 就用到 4月25日 才发布的数据(未来函数)。
-
查询字段:
示例中使用了ASHAREINCOME.net_profit_incl_min_int_inc(净利润)。只要这个数据在当天能取到且不为 NaN,就意味着当天有财报(或修正公告)发布。 -
获取具体的公告日时间戳:
如果你不仅想知道“今天是不是公告日”,还想知道“这份财报具体是哪一天公告的”(比如为了计算滞后性),可以查询m_anntime字段:# 查询字段增加 m_anntime field_list = ['ASHAREINCOME.net_profit_incl_min_int_inc', 'ASHAREINCOME.m_anntime'] # ... 获取数据后 ... ann_date_ms = df.loc[stock, 'ASHAREINCOME.m_anntime'] ann_date_str = timetag_to_datetime(ann_date_ms, '%Y%m%d')
常见问题 (Q&A)
Q: 为什么我取到的数据是空的?
A: 请检查 startDate 和 endDate。如果这两个日期之间没有公司发布财报,返回的数据就是空的。此外,请确保在“数据管理”中下载了完整的“财务数据”。
Q: 如何区分是一季报、半年报还是年报?
A: 可以同时查询 m_timetag(报告截止日)字段。
m_timetag对应 0331 -> 一季报m_timetag对应 0630 -> 半年报m_timetag对应 0930 -> 三季报m_timetag对应 1231 -> 年报
Q: get_financial_data 返回的数据格式是什么?
A: 返回格式取决于传入的股票数量和时间范围。
- 单股、单时间范围 ->
pandas.Series - 多股、单时间范围 ->
pandas.DataFrame(Index是股票代码) - 单股、多时间范围 ->
pandas.DataFrame(Index是时间) - 多股、多时间范围 ->
pandas.Panel(或字典)
建议在代码中先打印type(df)和df内容进行调试。