工作中需要對接港交所OMD-C的Standard版行情,現在把一些知識點和踩過的坑記錄下來,供大家參考。文中部分圖片因為縮小看不清,請【右鍵-在新窗口/標簽打開圖片】查看清晰版本。
- 「香港交易所領航星」巿場數據平台—證券市場(HKEX Orion Market Data Platform – Securities Market, OMD-C), Technical Documents
1. 概述
OMD-C數據服務分為實時服務、重傳服務、刷新服務三部分。
- 實時服務:UDP多播。提供掛單、成交、指數、市場狀態等實時數據,以及最高價、最低價、最后成交價、成交量、等統計數據。實時服務以UDP為載體,通過多播推給接收端。消息被歸類到若干通道(Channel),每個通道包含若干消息類型,比如成交消息和掛單消息都是在通道10。每個Channel的報文有唯一Sequence,Sequence總是從1開始。不同Channel之間的Sequence沒有關聯。為了減少丟包影響,報文都通過兩個獨立通道多播,比如Channel 10和Channel 510是一對,接收端根據Sequence做好去重工作。
- 重傳服務:TCP請求應答。緩存每個Channel最近50,000個報文,多播丟包時,接收端指定Sequence區間,向重傳服務請求丟失的報文。重傳是有限制的:
-
單Channel緩存最近消息數量:50,000
-
單次請求Sequence范圍:10,000
-
單用戶一天重傳次數限制:1,000
-
- 刷新服務:UDP多播。定期把當前市場狀態以多播方式推送給接收端,當接收端在盤中啟動時,以此恢復當前全部狀態,但逐筆交易等歷史信息無法恢復。
2. 網絡拓撲
client可通過SDNet/2和HKEX機房托管(co-location at HKEX Host Data Centre (HDC))兩種方式訪問OMD-C,一般選取SDNet/2方式連接。通過SDNet/2連接一般要在HKEX指定合作機房租用專線連接,路由器開啟IGMPv2,並且接口速度達到1Gbps以避免突發性丟包。
SNNet/2: https://www.hkex.com.hk/chi/market/sec_tradinfra/sdnet2_c.htm
HKEX提供了一冷一熱兩套站點,每套站點又包含兩個獨立多播源和一個重傳服務器,每個實時數據,都會經兩個多播源各發送一次,接收端根據序列號去重。重復多播極大減少了偶然丟包的影響,經過實際運行測量,為期1年的運行周期中,訪問重傳服務以恢復丟包的次數為0.
(圖片來源: OMD Connectivity Guide for Securities Market & Index Datafeed Products)
3. 交易時間段
隨機收市是一個比較特殊的狀況,HKEX可能在不同時間點發送收市指令。
4. 消息序列
OMD-C Standard版推送的消息比較多,我們關心的消息如下。
4.1 參考數據 (Reference Data)
- 市場定義, Market Definiton(10)。 系統開機后會把主板、創業板、納斯達克、連續交易市場的信息發送一遍。
- 證券定義, Security Definition(11)。 系統開機后會把所有證券信息發送一遍,包括調整后的昨日收盤價、證券類型、名字、每手多少股(LotSize),是否參與收市競價等,如果是衍生產品,還有換股比、行權價格、到期日等信息。
4.2 狀態數據 (Status Data)
- 交易會話狀態, Trading Session Status(20)。 開機、開市、休市、收市,都有相應通知,具體請參考交易狀態一節。
- 個股狀態, Security Status(21)。 系統開機后,會發送一遍當日所有停牌證券的信息,沒有發送的就是正常。
4.3 掛單數據(Order Book Data)
- 掛單聚合更新, Aggregate Order Book Update(53)。 OMDC采用增量更新的方式把聚合后的掛單信息推送下來,這里的聚合,是指按照相同價位聚合,比如:價格$100,有3個掛單,數量分別是100股,200股,300股,OMDC會發送一個 600@100的消息,告訴接收端$100價位總共有600股掛單。 OMD-C提供買賣10檔價位的掛單信息。
- 經紀隊列, Broker Queue(54)。 提供了單方(買或者賣)最多40個最佳報價的經紀人ID。
4.4 交易和報價數據(Trade and Price Data)
- 交易報價, Trade Ticker(52)。標准版數據不會把每筆交易都通知,而是把一小段時間內相同證券的連續幾個相同價位成交的事件聚合為單一事件通知。比如在0.1秒之內,分別達成如下成交:
- 00700 200元 成交100股
- 00700 200元 成交200股
- 00100 100元 成交100股
- 00100 101元 成交200股
- 00700 200元 成交100股
那么最后輸出的Trade Ticker類似於:
-
- 00700 200元 成交300股
- 00100 100元 成交100股
- 00100 101元 成交200股
- 00700 200元 成交100股
交易報價只在09:00-16:10之間發送,DayClose之后不會有Trade Ticker消息。其中,09:00-09:20之間是開市前交易,包括前一個交易日達成后來不及輸入系統的交易。
注意,港股成交有多種類型,其中自動對盤交易和競價交易是最重要的,決定了一個證券的開高低收四個價格屬性,其他成交只會影響成交量和成交額。
- 按盤價, Nominal Price(40)。 按盤價是根據成交價、掛單價格總和計算得到,掛單變化、成交達成、成交取消時,按盤價都可能會變化。在第一個按盤價發出之前,按盤價等於昨日收盤價。部分股票整日無交易無掛單,其按盤價就是昨日收盤價。Trade Ticker(52) 事件、Aggregate Order Book Update(52)事件、Nominal Price(40)三者的順序是: (Nominal -> OrderBook) -> ... -> (TradeTicker -> Nominal) -> ... -> (Nominal -> OrderBook)。也就是說,如果掛單變化引起按盤價變化,HKEX總是先發送新的按盤價過來,再發送掛單事件。如果是交易達成、交易取消引起的按盤價變化,總是先發送交易事件,再發送新的按盤價。
- 收盤價, Closing Price(62)。 為了防止收盤價被操縱,收盤價並不是最后一筆成交的價格。如果證券參與了收市競價,收盤價是收市競價的成交價,如果沒有,計算規則如下:
正常情況下,股份的收市價會按一天交易活動時段最后一分鍾內五個按盤價的中位數計算。系統會由下午15:59正開始每隔十五秒記錄一次股份的按盤價, 一共記錄五個按盤價。
如:
五個時段的按盤價由最低至最高順序如下:39.35,39.40,39.40,39.45,39.45,中位數(即中間的價格)是39 .40,所以收市價定於39.40 。
請特別注意:每個股票都會發送Closing Price,並且是在交易會話切換到DayClose之后才發送的。
4.5 增值數據(Value Added Data)
- 統計數據, Statistics(60)。統計報文記錄了某個證券當前的成交量、成交額和權重股均價,同時可能還有最高最低和最后成交價。但是,最高最低和最后成交價也可能為0。比如,9:00,收到了Late Trade消息,此時會發送Statistics包,成交量非0,但最后成交價卻是0。由於開盤價一旦產生不再變化,Statistics包不攜帶該字段。接收端應該處理Trade Ticker,把第一個競價交易或者自動對盤交易的成交價作為開盤價。
- 市場成交量, Market Turnover(61)。記錄了某個市場(主板、創業板、納斯達克、持續交易)的總成交額,其中,主板的成交額最常用,我們一般不顯示HSI(恆生指數)自身的成交額,而是顯示主板成交額。DayClose之后,還會有Market Turnover消息到達。
4.6 指數和市場信息(Index Data and Market Information)
- 指數定義, Index Definition(70)。定義了指數的數據源,貨幣代碼等。
- 指數數據, Index Data(71)。比如恆生指數、紅籌指數,每隔2秒鍾發布一次開高低收價格、成交額、漲跌幅、漲跌額。請注意:交易狀態切換到DayClose后,還有Index Data發送。
4.7 消息序列圖
5. 數據量
5.1 證券數量和活躍度
以5月19日為例,港交所有將近一萬個證券,總市值30萬億港幣, 大約分類如下:
- 2024 正股,其中主板1740,創業板284
- 6500 衍生產品,包括渦輪和牛熊證
- 1000 債券
- 180 ETF
當天共成交663,896筆, 成交額700億港幣。與此相對,A股數量大約3000只,總市值53萬億(人民幣),流通市值40萬億,當日成交3500億,比港股活躍很多。
5.2 消息類型分布及處理速度
以5月19日數據為例,一個交易日有1650萬個報文,累計1.5GB數據量。一個交易日的數據,20分鍾之內可以處理完。
5.3 成交分布
一個交易日,共成交663,896筆, 50%以上證券一天成交不到100筆,只有不到200個證券日成交超過1000筆,其中騰訊控股(00700)最為活躍,成交16,000筆。
從時間分布上看,開盤后半小時和收盤前半小時較為活躍,其中又以收盤前最為活躍,1分鍾完成10,863筆交易。
6. 系統結構
這個項目是從零開始的,開發人員此前完全沒有證券行情相關的經驗,所以走了不少彎路,這里就不一一列舉了。行情服務分為幾大塊:生產子系統、推送子系統和查詢子系統。生產系統只需要開市及開市前后一段時間運行,接收OMD-C數據並整理成合適格式寫入存儲。推送系統和生產系統生命期相同,行情變化時推送給Client,保證實時性。查詢系統要24小時可用,隨時響應查詢請求。
6.1 生產子系統
- Adapter:數據適配層,對接OMD-C,對UDP進行排序去重,然后以TCP流發給下游,同時做好數據DUMP。對可靠性要求非常高,承擔責任比較少,只關心消息格式,不關心消息內容。
- Importer:行情導入層,對接Adapter,解析流式數據包,根據消息內容組織基本的行情邏輯,屏蔽數據源的差異性,構造一直輸出給下游使用。項目后來逐漸對接了美股和A股,每個數據源都有獨立的Importer,並且要求這些Importer輸出邏輯一致的消息給下游,這樣下游的模塊可以不區分來源統一處理,簡化編碼和運維負擔。
- 板塊指數:對接不同Importer,根據深成指數規范,對行業板塊、概念板塊進行指數化,計算並發布。
- 實時行情:對接不同Importer和板塊指數,處理港股、美股、A股等不同來源的數據並做更復雜的處理,進行數據入庫,並把行情寫入消息隊列。
- 歷史行情: 對接不同Importer和板塊指數,根據掛單、成交、按盤價等信息構建分時K線、日周月K,計算MA/MACD/SAR等技術指標,入庫,並寫入消息隊列。
- 存儲:選用MySQL存儲今日之前的日周月分時K之類的冷數據,Redis全內存存儲實時行情和當日分時K,收市后DUMP到MySQL進行持久化。兩路生產子系統,兩套獨立存儲,可以同時對外提供查詢。
6.2 推送子系統
- 實時推送:從消息隊列得到行情數據,通過長連接推給訂閱了相關證券的Client。
- 股價提醒:從消息隊列得到行情數據,通過長連接或者離線方式推給Client。推送通道有騰訊信鴿、小米、華為、APNs幾種。
6.3 查詢子系統
- 查詢系統目前做的比較簡單,多實例提供服務,LVS負載均衡。
6.4 容災
- 生產子系統:有獨立兩套,包括運算和存儲都是分離的,只要有一套正常就可以。收市之后,把當天的數據用導數據方式從正常環境復制到異常環境即可。在最壞的情況下,兩套生產環境都出現故障,只要最初的數據適配層正常,可以臨時停服,回放數據+接收實時數據來離線重建當日行情,20分鍾之內就可以追上實時行情進度。為了避免錯誤數據誤導用戶決策,重建期間是不提供服務的,直到重建完成才繼續提供服務。
- 查詢子系統:本身就是分布式,沒有單點。需要注意的是,當某路生產系統故障時,查詢系統要禁用該路存儲,恢復后繼續啟用。
- 推送系統:和查詢系統類似,稍微注意的是,它要同時消費兩個生產系統的消息,所以在一個系統正常時,只用一個系統的,另一個系統的消息直接丟棄,避免重復推送。
本文為skylerjiang原創作品,轉載請聯系作者。