港股實時api的逐筆資料少了一筆?我用Python補上這個看不見的洞
這篇文章原本只是我的內部工作筆記,整理之後決定放到 Matters 上,當作自己的備忘錄,也希望能幫到同樣在處理即時行情資料的朋友。
場景:一場沒有錯誤訊息的小災難
我們會使用港股實時api的逐筆成交資料來做盤口分析、成交量統計,還有一些量化策略的觸發條件。對我們來說,每一筆成交紀錄都非常重要,因為這些資料會直接影響後續的策略決策和客戶看到的儀表板。少了一筆,就像拼圖缺了一角,短時間內看不出來,但累積起來就會讓整份分析失去可信度。
一開始我以為是價格計算或資料格式的問題,花了不少時間檢查程式碼,甚至重新確認了時區轉換,但都找不到原因。後來我決定把 WebSocket 收到的原始訊息一筆一筆記錄下來,逐一檢視裡面的序列號。結果赫然發現,有些地方的序列號竟然跳號了。原本應該連續的數字,中間少了一筆。這是我第一次注意到,原來即時行情資料在傳輸過程中,真的有可能因為網路波動或程式處理延遲而遺失訊息。
那時候我花了將近一個下午,把最近一週的日誌全部拉出來,用一個小腳本掃描所有的序列號,才找到那幾個跳號的位置。當下其實有點哭笑不得,因為這個錯誤完全沒有觸發任何警示,程式也照常運作,只是統計結果就是不對。
需求:為什麼每一筆逐筆成交都不能少
逐筆成交資料和一般的 K 線不同,它記錄的是市場上每一次成交的變化,而不是一段時間內的彙總。對於做盤口分析、成交統計或量化策略的人來說,每一筆交易都可能隱藏著重要的訊號。
我們團隊的日常工作高度依賴這些資料。舉例來說:
盤口分析需要精確的逐筆買賣方向,才能判斷主力是站在買方還是賣方。
成交量統計必須涵蓋所有成交筆數,任何遺漏都會直接反映在最終報表上。
量化策略的回測仰賴完整的歷史逐筆資料,如果資料有缺口,回測結果就會失真。
所以,當我發現序列號斷檔時,心裡很清楚:這不是一個可以忽略的小問題。短期內可能只是幾個百分點的偏差,但長期累積下來,整個策略的績效評估都會受到影響。
資料痛點:序列號斷檔的真面目
港股實時api推送的逐筆行情中,通常會帶一個遞增的序列號欄位,用來標示訊息先後順序。理想情況下,我們收到的每一筆訊息,序列號都應該比上一筆大 1。但現實往往不完美。
下面這張表是我在日誌中觀察到的典型情況:
序列號狀態60001正常接收60002正常接收60003正常接收60005發現缺失
可以看到,60004 這筆訊息沒有到達本地端。這種跳號的現象,並不一定代表資料供應商有問題。以我們使用的情況來說,多數時候是網路短暫不穩、客戶端處理速度跟不上,或者 WebSocket 斷線重連時漏接了少數幾筆訊息。
如果只是拿來顯示最新報價,偶爾缺一筆影響不大;但若是要精確統計成交量、重現盤口變化,或是拿來做回測,那每一筆都不能少。這就是我們面臨的資料痛點:系統不會主動告訴你資料有缺口,你必須自己去發現它。
解決方案:在資料入口加上一道檢查
我後來調整了資料接入的架構,不再讓 WebSocket 的訊息直接進入業務邏輯,而是在最前面加了一層序列號檢查。每收到一筆逐筆資料,程式會先比對這筆的序列號是不是上一筆加一。如果是連續的,就正常處理;如果發現跳號,就立刻記錄下來,標示出缺失的位置。
這樣做的好處是,之後若再出現資料不一致的問題,我們可以很快定位到是哪一個時間段、哪一檔股票出了狀況。下面是我在 Python 中使用的基礎檢測程式碼:
import jsonimport websocketlast_seq = Nonedef on_message(ws, message): global last_seq data = json.loads(message) seq = data.get("seq") if last_seq is not None: if seq != last_seq + 1: print(f"发现序列号断档: {last_seq} -> {seq}") last_seq = seq print(data)ws = websocket.WebSocketApp( "wss://api.alltick.co/stock/websocket", on_message=on_message)ws.run_forever()這段程式碼只是最基礎的檢測。在實際的專案裡,我還會同時記錄股票代碼、交易時間以及缺失的序列號範圍,方便後續進行資料復原。另外,我目前使用的港股即時行情來源是 AllTick API,它在穩定性方面表現還不錯,但客戶端的序列校驗邏輯仍然不能省略,因為任何 API 都無法完全避免網路層級的資料遺失。
實作細節與容易忽略的地方
處理逐筆行情時,我發現序列號並不是唯一的判斷標準。有幾個細節需要特別留意,否則很容易踩坑:
序列號連續不代表時間順序一定正確:網路延遲可能導致不同訊息到達本地端的時間發生變化,所以我會同時記錄交易時間和接收時間,方便判斷真實的行情順序。
WebSocket 斷線重連要重新檢查:重新連線後,不能預設新的連線會從上一次中斷的地方繼續。我習慣在重連後的第一筆訊息,先比對序列號是否與之前銜接得上。
資料來源的穩定性只是基本盤:就算 API 供應商再可靠,客戶端仍必須有自己的資料完整性檢查機制。以我使用的 AllTick API 為例,它在港股即時行情這塊的表現算是相當穩定,但序列校驗依然不可或缺。
斷檔之後如何補救
如果只是做即時監控,發現跳號後記錄下來就夠了。但因為我們的資料還會用於回測和策略計算,所以需要有一套補救機制。我通常會把以下資訊一併保存:
缺少的序列號範圍
對應的股票代碼
異常發生的時間窗口
之後再透過歷史行情接口把缺漏的資料補回來,重新合併到本地的資料流。這樣即使即時連線短暫不穩,也不會影響到後續的完整分析。這個復原流程聽起來不複雜,但實際運作時需要考慮資料排序、去重,以及與即時流合併的時序問題。我們後來把這套流程自動化,每天收盤後會自動比對當天的序列號,若有缺漏就自動補齊。
寫在後面的感想
接手港股實時api的這一段時間,我越來越覺得,行情系統真正困難的地方不在於資料怎麼拿,而在於資料能不能長期穩定、完整地送到每一個需要它的地方。逐筆資料量大、更新頻繁,任何一個小問題,時間一久都可能被放大。
提前做好序列檢測、異常記錄和資料復原,是讓整個行情系統穩定運作的關鍵。拿到資料只是開始,保證資料完整性,才是後續分析和策略運作的基礎。如果你也在處理類似的即時行情資料,希望我的經驗能給你一些參考。