摘要

研究报告里写“截至某日某时,某股票价格为 X”,一周后复查时发现数据源已刷新,当时的快照没保存,数字无法核对。这不是数据源的问题,是取数流程里缺了一环——没有在取值的同时留下原始快照和字段溯源记录。本文给出一套最小留痕模型:用 Python 保存请求参数、原始响应、字段路径和校验记录,让报告里的每一个行情数字都能追溯到“什么时候、从哪个接口、用哪个字段取的、原始返回值是什么”。

研究员用 Python 拉行情数据写报告,最常见的一个坑不是数据源挂了,而是报告交上去之后发现数据对不上,回头查的时候数据源已经刷新了——那条行情的快照早就没了。

问题不在数据源,在于取值的时候没有同步留痕。 你只记了数字本身,没记这个数字是从哪个字段路径拿到的、原始返回体长什么样、取值时间是多少。一周后想复核,只剩报告里一个孤零零的数字,既查不到来源,也复现不了当时的状态。

本文给出一套最小留痕模型,帮你把每一次行情取值都变成一条可追溯的记录。它不是数据库设计教程,也不是生产级数据管道,而是一套能让你的研究数据“经得起复核”的工程方法。

1. 最小留痕模型:一条记录至少包含什么

每次取数,至少保留以下六类信息。这六类合在一起,构成了一个行情数字的“完整身世”。

留痕维度 核心字段 回答什么问题
时空坐标 symbolmarketquery_time 什么品种、什么市场、什么时间取的
数据来源 data_sourcequery_method 从哪个数据源、用哪种接入方式拿的
时间语义 payload_timestamp 行情发生在什么时候(不是取数时间)
取值路径 field_namefield_path 从返回体的哪条路径取到这个数字
原始凭证 raw_snapshotraw_snapshot_hash 原始返回体是什么,有没有被改动过
复核状态 processing_notereview_status 这个值后来有没有被人工修改、有没有复核过

为什么 raw_snapshot 是必选项:保存原始响应是留痕和日志最大的区别。日志只记录“发生了什么”,原始快照记录的是“当时数据源交回了什么”。一周后如果价格对不上,你可以把原始快照重新灌进校验脚本,复现当时的取值过程。没有快照,只剩猜测。

2. 字段表:研究报告引用的行情数据溯源记录

以下字段表适合作为研究报告的数据引用附录,每引用一个行情数字,填一行。这张表的核心是让读者(或审查者)能从报告里的数字反查到你取数时的原始状态。

字段名 类型 说明 示例值
report_id str 报告唯一标识 RPT-2026-001
report_section str 报告中的位置 §3.2 估值分析
symbol str 品种代码 600519.SH
market str 市场 A
data_source str 数据源标识 tickdb
query_method str 接入方式 REST
request_params_hash str 请求参数哈希 sha256:abc123...
requested_at str 请求发起时间(ISO 8601) 2026-07-01T14:30:00+08:00
payload_timestamp int 行情时间戳(原始值) 1719823400000
field_name str 取值字段名 last_price
field_path str 字段在返回体中的路径 data[0].last_price
field_semantics str 字段含义说明 最新成交价(快照)
value_raw str 原始返回值 "1675.00"
value_normalized str 规范化后的值 1675.00
raw_snapshot_id str 原始快照唯一标识 SNAP-20260701-001
raw_snapshot_hash str 原始快照哈希 sha256:def456...
processing_note str 处理备注 字段核对通过
review_status str 复核状态 待复核 / 已复核 / 存疑

注意:上表中的 payload_timestampvalue_raw 为字段占位说明,实际值以接口返回为准。request_params_hashraw_snapshot_hash 用于防篡改校验,具体哈希算法可选用 SHA-256。

3. Python 伪代码:取值即留痕

以下代码为教学伪代码,展示取值留痕的核心逻辑流程,非生产级可运行代码。实际使用时需根据数据源的接口文档调整字段路径和鉴权方式。

import json
import hashlib
from datetime import datetime, timezone
from decimal import Decimal


def fetch_market_data(symbol: str, market: str) -> dict:
    """向行情数据源请求快照数据。
    返回原始响应体。实际实现需替换为真实请求逻辑。
    """
    # 教学伪代码:实际应使用 requests.get() 并处理超时、异常
    # response = requests.get(..., params={...}, headers={...}, timeout=10)
    # return response.json()
    pass


def save_raw_snapshot(data: dict) -> tuple:
    """保存原始响应快照,返回 (snapshot_id, snapshot_hash)。"""
    raw_json = json.dumps(data, ensure_ascii=False, sort_keys=True)
    snapshot_id = f"SNAP-{datetime.now(timezone.utc).strftime('%Y%m%d-%H%M%S')}"
    snapshot_hash = hashlib.sha256(raw_json.encode()).hexdigest()
    # 实际实现:将 raw_json 写入文件或数据库
    return snapshot_id, snapshot_hash


def extract_field_value(data: dict, field_path: str) -> str:
    """从返回体中按路径提取字段值。
    例如 field_path='data[0].last_price'。
    """
    # 教学伪代码:简化路径解析,实际需处理嵌套和数组索引
    parts = field_path.replace("[", ".").replace("]", "").split(".")
    current = data
    for part in parts:
        if isinstance(current, dict):
            current = current.get(part)
        elif isinstance(current, list) and part.isdigit():
            current = current[int(part)]
        else:
            return None
    return str(current) if current is not None else None


def write_lineage_record(report_id: str, section: str, symbol: str,
                         market: str, data_source: str, query_method: str,
                         requested_at: str, payload_ts: int,
                         field_name: str, field_path: str, field_semantics: str,
                         value_raw: str, snapshot_id: str, snapshot_hash: str,
                         processing_note: str) -> dict:
    """写入一条数据溯源记录。"""
    # 教学伪代码:实际实现应写入数据库
    record = {
        "report_id": report_id,
        "report_section": section,
        "symbol": symbol,
        "market": market,
        "data_source": data_source,
        "query_method": query_method,
        "requested_at": requested_at,
        "payload_timestamp": payload_ts,
        "field_name": field_name,
        "field_path": field_path,
        "field_semantics": field_semantics,
        "value_raw": value_raw,
        "value_normalized": str(Decimal(value_raw)) if value_raw else None,
        "raw_snapshot_id": snapshot_id,
        "raw_snapshot_hash": snapshot_hash,
        "processing_note": processing_note,
        "review_status": "待复核"
    }
    # db.insert(record)
    return record


def build_report_citation_note(record: dict) -> str:
    """根据溯源记录生成报告中可引用的数据来源注释。"""
    return (
        f"数据来源:{record['data_source']}{record['query_method']}),"
        f"品种 {record['symbol']},字段 {record['field_path']},"
        f"请求时间 {record['requested_at']},"
        f"快照 ID {record['raw_snapshot_id']},"
        f"复核状态:{record['review_status']}"
    )

4. 失败分支速查

取数留痕流程中,以下八类异常各有对应的处理方式。任何一类失败,这条数据的 review_status 都应标记为“存疑”或直接阻断。

失败场景 处理方式
请求成功但 data 为空 记录 value_raw=Noneprocessing_note 写明“数据为空”,不填默认值
JSON 解析失败 保留原始响应文本,processing_note 记录“JSON解析失败”
symbol 缺失或与请求不一致 阻断,不写入溯源表。静默修正可能指向错误品种
timestamp 缺失或语义不明 写入溯源表但 review_status=存疑processing_note 标注“时间戳待确认”
字段路径不存在 value_raw 留空,processing_note 写明“字段路径不存在”,检查接口版本
raw_snapshot 未保存 整条记录标记为“不可追溯”,review_status=存疑
raw_snapshot_hash 与原始不一致 快照可能被篡改或损坏,标记“哈希不一致”并阻断下游引用
人工修改了值但未写 processing_note 复核时发现值与快照不一致且无备注,标记“数据存疑”

5. TickDB 在取数留痕流程中的合理位置

TickDB 可作为候选结构化行情入口,帮助统一保存请求参数、字段语义、原始快照和检查记录。它在取数留痕流程里的作用不是“保证数据一定正确”,而是让你的取值动作有一个结构化的起点——symbol 和 market 有统一格式,接口返回有明确的字段路径,原始响应体可以完整保存。这些恰好是留痕模型前四类信息的基础。

验证方式:用自己的 symbol 发一次请求,按本文字段表逐列填写。保存原始快照和哈希,计算 value_normalized,写一条完整溯源记录。如果当前接口返回的字段路径和本文示例不同,以官方文档或实测为准。

不适合什么:不替代研究报告的数据审核流程;不承诺数据永远正确;不保证所有字段在所有市场可用。具体端点、Header、字段枚举和错误码,以 TickDB 官方文档和审核为准。

6. 发布前检查清单

序号 检查项 通过标准
1 请求参数已保存 每次取数记录 symbol、market、请求时间、参数哈希
2 原始快照已保存 raw_snapshot 完整写入文件或数据库
3 快照哈希已计算 raw_snapshot_hash 可用于防篡改校验
4 字段路径明确 报告引用的每个数字都能追溯到具体 field_path
5 时间语义区分 requested_atpayload_timestamp 分开记录
6 异常有备注 data 为空、解析失败等情况在 processing_note 中写明
7 人工修改有留痕 任何手工调整都在 processing_note 中说明原因
8 复核状态可查 每条记录有 review_status,存疑数据不进入最终报告
9 报告中可引用 每个行情数字附 build_report_citation_note() 生成的来源注释

📡 本文取数留痕示例以 TickDB.ai 作为候选行情入口,具体字段以官方文档和审核为准
⚠️ 本文为技术教程,不构成任何投资建议

更多推荐