# 百度财经 WebSocket 行情实时刷新 — 设计 **日期:** 2026-07-22 **状态:** 已口头确认,待书面复核 --- ## 1. 背景与目标 当前行情通过 HTTP `getStockQuotation` 在页面打开时拉取一次全量(分时、盘口、成交明细)。`quotation.ts` 已预留「实时更新后续对接 WebSocket」。 本阶段目标:接入百度财经 WebSocket,在 HTTP 全量底之上做增量刷新,使报价、盘口、分时图、分时成交与大单分析实时更新。 --- ## 2. 已确认决策 | 项 | 选择 | |---|---| | 初始全量 | 现有 HTTP `getStockQuotation`,成功后再连 WS | | 数据映射 | `tick` → 成交明细(TradeTape)+ 大单分析;`snapshot` → 报价 / 盘口 / 分时点 | | 架构 | 独立 `BaiduQuoteWs` 客户端 + mapper;状态仍落 `quota` store | | 合约 | 仍读 `contractConfig` 单合约,不做多合约订阅 | | WS 库 | 原生 `WebSocket`,不引入第三方 | | 连接 UI | 本期不做连接指示灯 / 手动重连按钮 | --- ## 3. 架构与数据流 ``` 页面挂载 → quota.fetchQuote() // HTTP 全量 → BaiduQuoteWs.connect() // wss://finance-ws.pae.baidu.com/ → subscribe tick + snapshot → 每 6s ping → 每 60s patch snapshot → onMessage → product=tick → 合并 trades(最新在前) → product=snapshot → 更新报价 / 盘口 / upsert intraday 页面卸载 / 主动 refresh → BaiduQuoteWs.disconnect()(refresh 成功后再 connect) ``` ### 模块划分 | 文件 | 职责 | |------|------| | `src/api/baidu/ws.ts` | 连接、心跳、订阅、重连、回调 | | `src/api/baidu/wsTypes.ts` | tick / snapshot 消息 TypeScript 类型 | | `src/api/baidu/mapWsQuote.ts` | 增量消息映射并合并进 `QuoteData` | | `src/stores/quota.ts` | HTTP 成功后启 WS;应用增量;卸载断开 | | `src/composables/useQuote.ts` | `onMounted` 拉行情;`onUnmounted` 断开 WS | 大单分析已基于 `quote.trades` 计算,只需更新 `trades`,不改组件。 --- ## 4. 协议细节 **端点:** `wss://finance-ws.pae.baidu.com/` **订阅(open 后立即发送两条):** ```json {"method":"subscribe","source":"pc-web","product":"tick","items":[{"code":"FG609","name":"玻璃2609","market":"ab","financeType":"futures"}]} {"method":"subscribe","source":"pc-web","product":"snapshot","items":[{"code":"FG609","name":"玻璃2609","market":"ab","financeType":"futures"}]} ``` `code` / `name` 来自 `contractConfig`;`market` 固定 `ab`;`financeType` 固定 `futures`。 **保活:** - 每 6s:`{"method":"ping","source":"pc-web"}` - 每 60s:snapshot 的 `patch`(items 同订阅) **重连:** 断线后指数退避(1s → 2s → 4s … 上限 30s);重连成功后重新 subscribe;不自动重拉 HTTP。 **disconnect:** 清 ping/patch 定时器、关闭 socket、停止重连调度。 --- ## 5. 合并规则 ### 5.1 tick → trades - 解析 `data.detailinfos` → `TradeTick[]`(字段映射与现有 HTTP `parseTrades` 一致:`formatTime` / `price` / `volume` / `bsFlag`;最新在前) - 前置合并到 `quote.trades` - 去重键:`time + price + volume + side` - 上限:最近 **200** 条 ### 5.2 snapshot → 报价 / 盘口 / 分时 - `cur`:更新 `last` / `change` / `changePercent` / `avg`;状态可从 `cur.status` 或 `update` 取 - `pankouinfos`(数组,按 `ename`):更新 open / high / low / volume / amount / openInterest / amplitude / settlement / prevSettlement / outerVol / innerVol 等 - `askinfos` / `buyinfos`:刷新五档;过滤无效价;重算 `buyRatio` / `sellRatio` - `update`:刷新 `updatedAt` / `status` - `point`:按分钟时间(从 `point.time` 取 `HH:mm`)upsert 到 `intraday` - 覆盖 / 写入:`price` ← `point.price`,`avg` ← `point.avgPrice` - `volume`:WS 只给 `totalVolume`(全日累计)。分钟成交量 = `max(0, totalVolume - 此前各分钟 volume 之和)`;同分钟再次推送时用同一公式重算并覆盖 - 新分钟:append;同分钟:覆盖该点 ### 5.3 错误处理 - `resultCode !== "0"`:打日志,忽略本条 - JSON / 字段解析失败:打日志,不中断连接 - 组件层无感;不暴露 WS 连接状态到 UI --- ## 6. 生命周期 1. `useQuote` `onMounted` → `store.fetchQuote()` 2. `fetchQuote` 成功 → `store.connectWs()`(若已连接则先 disconnect 再 connect) 3. `useQuote` `onUnmounted` → `store.disconnectWs()` 4. `refresh()` / `fetchForAnalysis()`:仍走 HTTP 全量;成功后重连 WS,用最新全量覆盖后再接增量 5. K 线逻辑不变;WS 不修改 `candles` --- ## 7. 明确不做(本期) - 连接状态 UI / 手动重连按钮 - 多合约同时订阅 - 修改 TradeTape / LargeOrderAnalysis / ChartPanel(仅消费响应式 `quote`) - 引入第三方 WS 库 - WS 失败时回退 HTTP 轮询 --- ## 8. 验证要点 - HTTP 成功后会发送 tick + snapshot 两条 subscribe - 约每 6s 有 ping,约每 60s 有 snapshot patch - tick 合并 trades:去重生效、长度 ≤ 200;TradeTape / 大单分析随之刷新 - snapshot.point:同分钟覆盖、新分钟追加;分时图更新 - snapshot 盘口 / 现价字段正确刷新 - 页面卸载后无残留 timer / 未关闭的 socket - 手动 refresh 后 WS 会断开并重新订阅 --- ## 9. 风险与注意 - WS 与 HTTP 的 snapshot 字段结构不完全一致(HTTP 用 `origin_pankou`,WS 用 `pankouinfos` 数组);mapper 需独立,勿强行复用 HTTP mapper 全文 - 去重键不含服务端唯一 id(协议未提供);极端情况下同秒同价同量同向两笔可能被误去重,可接受 - 跨域 / 鉴权:浏览器直连百度 WS;若环境拦截再评估代理,本期先直连