← 返回博客列表

【魔码量化工程实战进阶 #07】实时行情的生产级消费:订阅推送与快照一致性

2026年08月31日 18:02 · 魔码数服 · 魔码量化工程实战进阶

【魔码量化工程实战进阶 #07】实时行情的生产级消费:订阅、推送与快照一致性

入门系列第 03 篇讲了"实时行情字段全解读",但那是"拉一次看一眼"。本篇进入生产视角:如何让行情持续、稳定、一致地流入你的策略——本地快照怎么维护、乱序/重复怎么处理、跨日怎么重置。这是把"看行情"变成"用行情"的关键一跃。

本文你将得到什么

  1. 轮询 vs 长连接(推送)两种消费模式的取舍
  2. 本地快照(snapshot)的工程实现:一份始终最新的 code → quote 映射
  3. 三个生产级一致性坑:跨日 yc 重置 / 停牌股不更新 / 字段语义跨接口漂移
  4. 一个可跑的 QuoteConsumer 骨架(基于真实返回的字段结构)

一、痛点:拉一次 ≠ 持续消费

回测可以"一次性把历史全读完"。但实盘/盯盘要的是"此刻全市场最新价"。新手写法:

while True:
    q = get_realtime("600519")
    my_strategy.on_tick(q)
    time.sleep(3)

问题:① 每次只拿到一只,全市场 5000 只轮一圈要很久;② 没有"全局最新视图",策略问"现在 银行板块均价多少"答不上;③ 跨日时 yc(昨收)会变,不重置会算错涨跌幅;④ 停牌股返回的还是昨天价,你当成"没波动"会误判。


二、工程方案:本地快照 + 增量合并

核心思想:维护一份进程内"最新快照表",每次拉取只做增量合并。策略不直接调接口,而是读这份快照——解耦、快、一致。

接口 ──(定时拉取/推送)──> 快照表(snapshot[code]=quote) ──> 策略读
                                      ▲
                                      └── 每次更新覆盖旧值,保留最新

两种拉取模式:

模式 做法 适合 注意
轮询 定时 GET 全市场/订阅列表 分钟级、低频 受限于证书 QPS(见 #03)
长连接/推送 WebSocket 订阅,服务端主动推 秒级、tick 级 需服务端支持,断线要重连+补快照

魔码实时行情为纯 HTTP 接口,本文以轮询 + 快照为主;若后续开放推送通道,把"拉取"换成"收推送"即可,下游快照逻辑不变。


三、可跑代码(零 SDK;演示证书标"演示数据")

下面用真实返回的字段结构(实测 510300 沪深300ETF 实时快照字段为 pe,ud,pc,zf,tr,pb_ratio,p,o,h,l,yc,cje,v,pv,tv,t)构建一个 QuoteConsumer

import requests, time
TOKEN = "TEST-API-TOKEN-MOMA-836089C22111"   # 换成你的正式证书
BASE  = "https://api.momaapi.com"

class QuoteConsumer:
    def __init__(self):
        self.snap = {}          # code -> 最新行情
        self.last_day = None    # 跨日重置用
    def refresh(self, codes):
        today = time.strftime("%Y-%m-%d")
        if today != self.last_day:     # 跨日:清空昨收依赖,避免涨跌幅算错
            self.last_day = today
            self._reset_day()
        for c in codes:
            try:
                q = requests.get(f"{BASE}/hsstock/real/time/{c}/{TOKEN}", timeout=20).json()
                if "error" in q:       # 停牌/无权限:保留旧值并标记
                    q["stale"] = True
                self.snap[c] = q       # 增量覆盖,始终最新
            except Exception:
                pass                   # 单只失败不阻断(接 #04 重试更佳)
        return self.snap
    def _reset_day(self):
        for c in self.snap:
            self.snap[c]["yc"] = self.snap[c].get("p")   # 昨收更新为昨日收盘
    def latest(self, code):
        return self.snap.get(code)

四、本机实测(真实接口返回结构)

我用演示证书拉了 510300(沪深300ETF)的实时快照,真实返回字段与样例值:

ETF实时 字段: ['pe', 'ud', 'pc', 'zf', 'tr', 'pb_ratio', 'p', 'o', 'h', 'l', 'yc', 'cje', 'v', 'pv', 'tv', 't']
ETF实时 样本: p=11.65, o=11.54, h=11.68, l=11.48, yc=11.59,
            pc=0.5177(涨跌幅%), zf=1.7256(振幅%), pe=4.4, pb_ratio=0.48,
            tr=0.43(换手率%), cje=成交额, v=成交量

演示证书返回演示数据,需替换为正式证书;字段语义以官方文档为准。


五、原理深挖:三个生产级一致性坑

  1. 跨日 yc 重置yc 是昨收,涨跌幅 pc = (p − yc)/yc。T 日收盘后到 T+1 开盘前,yc 必须从"今日收盘"翻成"昨日收盘"。不重置,T+1 第一笔算出的涨跌幅会离谱。上面 _reset_day 在日期切换时把 yc 设为昨日 p
  2. 停牌股不更新:停牌股票实时接口返回的还是停牌前最后价,时间戳停在停牌日。若直接当"当前价"会误判。正确:检查返回里的 t(时间)是否过期,或接口是否返回 error/停牌标记,过期则标 stale 不参与计算。
  3. 字段语义跨接口漂移(重要):同一个 pc 字段,在实时接口里是"涨跌幅%"(如 0.5177),在历史 K 线接口里却是"前收"(如 11.72)。混用会直接算错。务必按接口分别解析——这正是 #12 要讲的"字段契约"。

六、小结

生产级行情消费 = 一份始终最新的本地快照 + 增量合并 + 跨日/停牌/语义三道一致性护栏。策略只读快照、不直连接口,既快又稳。这套快照是后面"指标计算(#13–#17)"和"实时风控(#21)"的直接数据源。

要维护"全市场 5000+ 只的实时快照",轮询频率与证书 QPS 是硬约束——魔码量化 Pro 包(全市场实时、更高配额)正是为此设计。详见文末。


免责声明:本文所有示例数据仅用于接口演示,不构成任何投资建议;市场有风险,投资需谨慎。

系列持续更新中。 想要亲手跑通上面的代码?前往 魔码证书申请页 免费领取你的专属证书,复制即用、按次计费、稳定可用。

想亲自试一下?免费获取证书
客服微信
客服微信二维码