01学完你能做到
区分事件时间、发布时间、接收时间和可用时间;正确使用时区本地化与换算;结合交易日历、证券状态和数据版本做时间对齐,识别迟到、陈旧和前视偏差。
02这课解决什么问题
一条记录写着 09:30:03,能不能用于 09:30:04 的决策?两只证券日期相同,收益区间就一定相同吗?答案取决于时间字段的含义、市场会话和数据何时真正可用。
03一条数据的四种时间
| 时间 | 回答的问题 | 例子 |
|---|---|---|
| 事件时间 event_time | 市场或业务事件何时发生 | 成交发生在 09:30:03 |
| 来源发布时间 published_at | 来源何时公开信息 | 公告在晚间公开 |
| 接收时间 received_at | 我方系统何时收到 | 网络延迟后 09:30:05 收到 |
| 可用时间 available_at | 完成获取和处理后何时可供使用 | 校验并写入查询服务后 |
此外还要考虑交易日、报告期、主数据生效区间和修订时间。一个 date 字段不能承担全部语义。
用于模拟公开信息市场时点,重点是信息公开时间;复现某套系统当时的决策,还要考虑接收和处理时间。只有公告日期、没有时刻时,不能假设开盘前已知,应采用明确的保守规则。
04时区:先解释钟面,再做换算
没有时区的“09:30”需要先确认来源钟面,再赋予时区,这叫本地化。已经带 UTC 时区的瞬间转换为上海时间,是换算;瞬间本身不变。
如果来源时间实际是纽约钟面,却直接贴上 UTC 标签,错误的瞬间会被写入系统。IANA 名称如 Asia/Shanghai、America/New_York 用于时区历史规则。纽约夏令时会产生不存在或重复的本地时刻,遇到歧义要按来源协议处理。
交易日是业务日期,不必强行转成 UTC 午夜后再取日期。还要检查时间精度、时间戳来源、时钟偏差、网络延迟和所属市场会话;有夜盘的品种不能从自然日机械推断交易日。
05交易日历、证券状态与收益区间
交易日历描述市场开市、休市、提前收市和交易阶段;停牌是证券级状态。市场开市不代表每只证券都在交易。行政调休的周末上班也不能直接推出交易所开市。
跨市场分析时,同一个自然日期的两张日线不一定在同一信息时点。统一时区后仍要按会话和可用时间匹配。
假设某证券在 09-29、09-30 和 10-12 有价格 5.0、5.1、5.2,10-09 已确认停牌:
- 原始稀疏表直接计算,10-12 会和上一条观测 09-30 比,结果约为 1.96%,跨过了中间日期。
- 先补齐共同日期、保留空值且不填充,10-12 前一行为空,结果为空。
- 把停牌日用前价作为估值标记,复牌日约 1.96%。这是估值口径,不代表停牌日存在成交价。
采集失败不能直接当作停牌;简单删掉空值也可能改变研究样本。
06`merge_asof` 的能力与边界
direction="backward" 会匹配不晚于左表时间的最近记录,但它只保证键的方向。用于历史特征时,匹配键必须代表信息可用时间;多证券要用 by,并设置 tolerance、同刻匹配规则和交易阶段约束。
保留匹配到的 event_time、available_at、版本和时间差,比只返回一个数更容易解释。事件陈旧度和可用时间陈旧度是两个不同问题。
例如指数在 09:30:03 发生、09:30:05 才可用:决策在 09:30:04.8 时,按事件时间向后匹配会找到它,但按可用时间匹配应为空;09:30:05.1 才可能使用它。向后匹配工具本身不能消除前视偏差。
07代码:按可用时间匹配
import pandas as pd
local = lambda xs: pd.to_datetime(xs).tz_localize("Asia/Shanghai")
decisions = pd.DataFrame({
"instrument_id": ["DEMO_A"] * 3,
"decision_time": local(["2026-10-26 09:30:04.800",
"2026-10-26 09:30:05.100",
"2026-10-26 09:30:10.000"]),
})
quotes = pd.DataFrame({
"instrument_id": ["DEMO_A"],
"event_time": local(["2026-10-26 09:30:03.000"]),
"available_time": local(["2026-10-26 09:30:05.000"]),
"value": [3001.5],
})
point_in_time = pd.merge_asof(
decisions.sort_values("decision_time"),
quotes.sort_values("available_time"),
left_on="decision_time", right_on="available_time",
by="instrument_id", direction="backward",
tolerance=pd.Timedelta("2s"), allow_exact_matches=False,
)
print(point_in_time[["decision_time", "value"]])08财报修订与历史可得性
如果某报告期利润 4 月首次公告为 10,6 月更正为 8,模拟 5 月的信息环境应使用当时公开的首发版本 10,而不是今天看到的最终值 8。
支持历史时点研究的数据,需要保留发布与修订版本,或提供可复现的 as-of 查询规则。只有最新值的接口仍可用于最新信息查询,但不能自动承诺能复原历史时点。
09延伸阅读
10一分钟讲给别人
试着用自己的话讲一遍先问记录上的时间代表什么,再按市场会话和数据可用时间对齐。向后匹配只是工具,迟到、陈旧、停牌和修订都需要额外信息。每个计算结果应能说明用了哪个版本,以及当时是否真的可获得。
6 道自测
先在心里答一遍,再展开对照。能说清理由,比答对数字更重要。
01`tz_localize` 和 `tz_convert` 分别解决什么问题?
02为什么事件时间 09:30:03 的数据可能不能用于 09:30:04.8 的决策?
03市场开市和证券可交易是同一件事吗?
04只有公告日期、没有公告时刻时,能否默认开盘前已知?
054 月公告利润 10、6 月更正为 8,模拟 5 月应使用哪个值?
06`merge_asof` 使用 `backward` 时还需要检查什么?
已看 0/6 题
练动手练习
任务:为一个行情或财报接口写一份时间语义检查表,逐项注明事件时间、可用时间、时区、精度、交易日、修订方式和历史版本。用一个迟到数据例子验证“按事件时间匹配”和“按可用时间匹配”的结果差异。
完成这一课
读完讲义、看完自测答案,这一课就算学完了。
- 读完讲义
- 看完全部自测
本课为投资者教育内容,案例数字均为示意,不构成任何投资建议,也不承诺收益。规则和费率会变,实际操作前请以交易所、监管机构和产品的最新公告为准。