作者:AhaSignals。示例针对 ahasignals-pit 0.1.1,Python 3.9+。

如果在 Python 回测中加入财务因子,除了行情和信号时间,还需要检查财务数据本身何时可用。本文用五个可运行的合成测试演示这一检查,适合放在因子输入进入回测引擎之前。代码独立运行,不依赖 VeighNa,也不是 VeighNa 插件。

财务因子使用的数据有多个时间。一个季度在 3 月结束,不意味着该季度的财务结果在 3 月已经可用;今天下载到的历史数值也可能包含后来才公开的修订。

一张速查表

字段或证据 回答的问题 不能替代什么
start / end 这个数值描述哪个报告期间? 披露或可交易时间
acceptedAt 调用方记录的文件受理时间是什么? 系统实际取得时间、新闻提前披露时间、市场传播或成交时间
observedAt 系统何时观测到这个版本? 不能用今天的下载时间伪造历史观测
accession / document hash 回答使用哪个版本? 哈希格式正确不等于源文件真实或时间戳可信
concept / unit / dimensions 概念、单位和合并范围是否一致? 数值相近不代表口径相同

重建披露可用性与重建系统实际历史访问,是两个不同的问题。前者可以按可信披露时间筛选,后者还需要当时的采集记录。若采用文件受理时间作为披露代理,应明确这一假设,不将其等同于所有信息首次进入市场的时间。

一个足以改变答案的反例

以下为完全合成的数据,金额单位 USD:

版本 报告期 值 受理时间(UTC) 采集时间(UTC)
原始 2025-01-01 至 03-31 120 05-01 00:00 05-02 00:00
修订 同上 90 08-01 00:00 08-02 00:00

4 月 30 日不能使用任一记录。5 月 3 日可以使用原始值 120,不能把修订值 90 回填到这个截点。9 月 1 日则可以选择已经取得的修订版本。

5 月 1 日中午还有一个区别:按受理时间重建可以得到 120;要求系统已观测到该版本时,应拒绝回答,因为采集发生在次日。

这些规则可以用数据库的版本记录与 as-of 查询实现。下面用一个限定范围的 Python 库演示相同检查,便于复现和阅读拒绝原因。

完整可运行示例

在自己的 Python 虚拟环境中安装:

python3 -m pip install ahasignals-pit==0.1.1

将下方完整代码保存为 pit_time_demo.py,执行 python3 pit_time_demo.py。不需要 JSON 文件、GitHub 访问权限或外部数据接口。

"""AhaSignals synthetic point-in-time tutorial, tested with ahasignals-pit 0.1.1.

No issuer data, real documents or authenticated timestamps are used.
"""
from ahasignals_pit import select_fact

identity = {
    "cik": "0000000001", "taxonomy": "us-gaap",
    "concept": "NetCashProvidedByUsedInOperatingActivities", "unit": "USD",
    "start": "2025-01-01", "end": "2025-03-31", "dimensions": [],
}
original = {
    **identity, "value": 120, "accession": "0000000001-25-000001",
    "acceptedAt": "2025-05-01T00:00:00Z",
    "observedAt": "2025-05-02T00:00:00Z",
    "sourceUrl": "https://example.org/synthetic-original",
    "documentSha256": "0" * 64, "contextId": "synthetic-q1",
}
revision = {
    **original, "value": 90, "accession": "0000000001-25-000002",
    "acceptedAt": "2025-08-01T00:00:00Z",
    "observedAt": "2025-08-02T00:00:00Z",
    "sourceUrl": "https://example.org/synthetic-revision",
    "documentSha256": "1" * 64,
}
facts = [original, revision]
cases = [
    ("before disclosure", "2025-04-30T12:00:00Z",
     "disclosure-reconstruction", "withheld", None, "source-after-cutoff"),
    ("disclosure reconstruction", "2025-05-01T12:00:00Z",
     "disclosure-reconstruction", "answer", 120, "latest-eligible-exact-context"),
    ("not yet observed", "2025-05-01T12:00:00Z",
     "observed-pipeline", "withheld", None, "observation-after-cutoff"),
    ("future revision excluded", "2025-05-03T12:00:00Z",
     "observed-pipeline", "answer", 120, "latest-eligible-exact-context"),
    ("revision now eligible", "2025-09-01T12:00:00Z",
     "observed-pipeline", "answer", 90, "latest-eligible-exact-context"),
]
for label, cutoff, mode, status, value, reason in cases:
    result = select_fact(facts, {**identity, "cutoff": cutoff, "mode": mode})
    assert (result["status"], result["value"], result["reason"]) == (status, value, reason)
    print(f'{label}: {result["status"]}, value={result["value"]}, reason={result["reason"]}')

预期输出:

before disclosure: withheld, value=None, reason=source-after-cutoff
disclosure reconstruction: answer, value=120, reason=latest-eligible-exact-context
not yet observed: withheld, value=None, reason=observation-after-cutoff
future revision excluded: answer, value=120, reason=latest-eligible-exact-context
revision now eligible: answer, value=90, reason=latest-eligible-exact-context

withheld 表示规则下无法回答;它的数值为 None,不是零。invalid 表示输入格式或必填字段不符合契约。实际来源字段及时间戳需由调用方独立验证,示例中的 URL 和占位哈希不对应任何真实文件。

另一个常见错误:把累计现金流当作季度

若经营现金流上半年累计为 280、一季度为 120,二季度为 160。同期购建 PP&E 现金支出累计为 90、一季度为 50,二季度为 40。因此二季度经营现金流减该现金支出为 120,而上半年累计差额为 190。

可运行安装包自带的合成例子:

python3 -m ahasignals_pit --example quarterly-cash > pit-quarterly-example.json
python3 -m ahasignals_pit pit-quarterly-example.json

新文件由第一条命令的重定向创建;已有同名文件会被覆盖。预期 JSON 包含 operatingCash: 160、cashPpe: 40、cashAfterPpe: 120。这里的差额不是涵盖所有投资支出的通用自由现金流定义。

能力边界

这个 alpha 版本根据调用方提供的时间戳和精确上下文选择事实,也提供限定概念的季度现金流计算。它不下载或验证 SEC 文件,不提供股价、交易策略、回测绩效,也不自动解决幸存者偏差、复权数据修订、交易成本或执行时点。案例不证明策略收益,不重现相关论文的模型评分。

独立实现还需要同样保留版本和可用时间。严格的系统历史回放需要在当时保存证据;现在补采历史文件无法恢复一个从未保存过的历史数据状态。

参考

本文仅用于研究与教育。