🚀 AI 一键生成 qmt 策略代码

如何获取股票财报发布日期并构建事件驱动策略?

qmt | 发布时间: 2025/12/21
以下内容由 EasyQuant 生成。

问题描述

如何获取股票的财报公布日期来做交易?

解决方案

在 QMT 中,获取股票财报公布日期主要通过 ContextInfo.get_financial_data 接口实现。

要获取财报公布日期用于交易,通常有两种思路:

  1. 事件驱动(推荐): 使用 report_type='announce_time'。在这种模式下,数据是按照公告日索引的。如果在某一天能取到数据,说明那天发布了财报。这是回测中最严谨的方式,能避免“未来函数”。
  2. 查询具体日期: 直接查询财务表中的 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日)取数。

策略代码示例

以下代码展示了一个完整的策略逻辑:

  1. handlebar 中,每天检查当天的财务数据。
  2. 使用 announce_time 模式获取数据。
  3. 如果当天有新的财报数据发布,打印出公告日期和净利润,并执行模拟交易(如买入)。
# -*- 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))

关键点解析

  1. report_type='announce_time':
    这是最关键的参数。当设置为 announce_time 时,QMT 会根据财报的实际公告日期来对齐数据。

    • 如果在回测中,你在 2023-04-25 获取到了 2023-03-31(一季报)的数据,说明该财报是在 4月25日 公告的。
    • 这完美模拟了真实交易场景,避免了在 3月31日 就用到 4月25日 才发布的数据(未来函数)。
  2. 查询字段:
    示例中使用了 ASHAREINCOME.net_profit_incl_min_int_inc(净利润)。只要这个数据在当天能取到且不为 NaN,就意味着当天有财报(或修正公告)发布。

  3. 获取具体的公告日时间戳:
    如果你不仅想知道“今天是不是公告日”,还想知道“这份财报具体是哪一天公告的”(比如为了计算滞后性),可以查询 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: 请检查 startDateendDate。如果这两个日期之间没有公司发布财报,返回的数据就是空的。此外,请确保在“数据管理”中下载了完整的“财务数据”。

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 内容进行调试。