第213期 / July 5, 2015

研發新視界

分享到臉書!分享到維特!分享到噗浪!分享到Google+!分享到微博!轉寄友人友善列印

淺談基於證交所FIX/FAST資訊傳輸系統之 FAST(FIX Adapted for Streaming)解壓縮方法

作者/吳旻聰

[發表日期:2015/7/5]

前言

FIX(Financial Information eXchange) 為證券交易電子化之後所發展出來的一種通訊協定,起初使用在證券交易和市場資訊進行資料的交換,最後由金融、證券相關資訊科技產業共同訂定一些規範而擬定出一套通訊協定的規範台灣證券交易所為了與國際接軌也在近年開發符合FIX規範的交易系統。

FIX訊息格式為Tag=Value每種Tag是用來表示股票代號、股價、傳輸時間等等…由於這種格式下雖然可讀性很高,但是會會造成每筆資料過長而造成網路頻寬負擔增加許多,因次為了解決資料長度問題而發展出FAST(FIX Adapted for Streaming)用來壓縮FIX訊息以提升傳輸效能。

FAST為一種二進位壓縮技術,主要利用雙方制定一種XML格式模板(Template)利用此模板來進行訊息的壓縮及解壓縮,本文以台灣交易所的FIX/FAST資訊傳輸系統的X1格式(集中市場一般交易即時行情資訊)來講解FAST解壓縮的方式。

在訊息的開始會有個PMap用來記錄此次訊息是否有傳送此資料,而需記錄在PMap中的各種Operator,而定義內還包含此Field的參數 Mandatory(必要)以及Optional(自選)最後對應表格如下:



模板的制定大致分為Field 和Sequence,而其中Sequence一組Filed的集合,下面為台灣證券交易所X1格式所公告的XML模板會記錄在PMap上的使用紅色標記:



其中FAST訊息的格式如下圖表示:



接著下面為台灣證券交易所公告X1格式的Fast訊息(16進位):







在FAST規範中每個byte的第1個bit為stop bit用來判斷是否還有資料若有則為0若結束則為1則實際有效位數為7個bits

接著開始解壓縮步驟:

一開始先將PMap給解讀出來(依序檢查每個Byte第一個bit,當遇到1則結束)



查看PMap的templateID的bit為1 因此第3個Byte代表templateID , 10010010 去掉第1個bit 為0010010轉換後為18代表為X1格式的ID,接著從樣板依序解讀。
8(BeginString) 為constant 因此固定為FIX.4.4
FIX:8=FIX.4.4

9(BodyLength)為Uint32且是Mandatory 依序找到第1個bit為1的byte為第5個Byte 00000011 10011100 去掉stop bit為00000110011100 結果為412
FIX:8=FIX.4.4^9=412

35(MsgType) 為constant 因此固定為X
FIX:8=FIX.4.4^9=412^35=X

34 (MsgSeqNum) 為Uint32且是Mandatory 依序找到第1個stop bit為1的byte為第6個Byte 10000001 去掉stop bit為 0000001 結果為1
FIX:8=FIX.4.4^9=412^35=X^34=1

49(SendingCompID)為default 因此檢查PMap值為0 未傳送故使用模板上的值XTAI
FIX:8=FIX.4.4^9=412^35=X^34=1^49=XTAI

52(SendingTime)為Uint64 且是Mandatory 依序找到stop bit為1的byte為第14個Byte 00100011 01100101 01010101 01101101 01110101 00100100 00000001 11000000去除stop bit為01000111100101101010111011011110101010010000000011000000
結果為20150401150550208
FIX:8=FIX.4.4^9=412^35=X^34=1^49=XTAI^52=20150401150550208

56(TargetCompID) 為default 因此檢查PMap值為0 未傳送故使用模板上的值MDSC
FIX:8=FIX.4.4^9=412^35=X^34=1^49=XTAI^52=20150401150550208^56=MDSC

1180(ApplID) 為constant 因此固定為X1
FIX:8=FIX.4.4^9=412^35=X^34=1^49=XTAI^52=20150401150550208^56=MDSC^1180=X1
1350(ApplLastSeqNum)為Uint32且是Optional依序找到stop bit為1的byte為第15個Byte 10000000去除stop bit為0000000代表為null因此FIX內不需解譯
FIX:8=FIX.4.4^9=412^35=X^34=1^49=XTAI^52=20150401150550208^56=MDSC^1180=X1
48(SecurityID)為optional且為copy因此查看PMap=1有傳送,依序找到stop bit為1的byte為第21個byte 100110110 100110001 100110011 100111001 100100000 110100000 還原後為
00110110 00110001 00110011 00111001 00100000 00100000 分別代表6139(space)(space)
FIX:8=FIX.4.4^9=412^35=X^34=1^49=XTAI^52=20150401150550208^56=MDSC^1180=X1^148=6139

22(SecurityIDSource)為optional且為constant PMap=1代表使用模板內定義值8
FIX:8=FIX.4.4^9=412^35=X^34=1^49=XTAI^52=20150401150550208^56=MDSC^1180=X1^48=6139 ^22=8

273(MDEntryTime)為optional且為delta依序找到stop bit為1的byte為第27個Byte 00000100 00110000 01010100 00000001 00001010 11001011 還原後為
0000100 0110000 1010100 0000001 0001010 1001011其值為150500033867
FIX:8=FIX.4.4^9=412^35=X^34=1^49=XTAI^52=20150401150550208^56=MDSC^1180=X1^48=6139 ^22=8^273=150500033867

31(LastPx)為optional且為delta依序找到stop bit為1的byte為第30個Byte
00000001 01000101 10100011還原後為000000110001010100011 其值為25250
FIX:8=FIX.4.4^9=412^35=X^34=1^49=XTAI^52=20150401150550208^56=MDSC^1180=X1^48=6139 ^22=8^273=150500033867^31=25250

1020(TradeVolume)為optional且為delta依序找到stop bit為1的byte為第31個Byte 10000100 還原後為00000011 其值為3
FIX:8=FIX.4.4^9=412^35=X^34=1^49=XTAI^52=20150401150550208^56=MDSC^1180=X1^48=6139 ^22=8^273=150500033867^31=25250^1020=3

14(CumQty) 為optional且為delta依序找到stop bit為1的byte為第31個Byte
10000100 還原後為00000011 其值為3
FIX:8=FIX.4.4^9=412^35=X^34=1^49=XTAI^52=20150401150550208^56=MDSC^1180=X1^48=6139 ^22=8^273=150500033867^31=25250^1020=3^14=3

326(SecurityTradingStatus) 為optional且為copy因此查看PMap=0未傳送
1022(MDFeedType) 為optional且為copy因此查看PMap=1有傳送,依序找到stop bit為1的byte為第33個byte 11100110 還原後為01100101 其值為101
FIX:8=FIX.4.4^9=412^35=X^34=1^49=XTAI^52=20150401150550208^56=MDSC^1180=X1^48=6139 ^22=8^273=150500033867^31=25250^1020=3^14=3^1022=101

264(MarketDepth) 為optional且為default因此查看PMap=0 未傳送 使用模板上的值5
FIX:8=FIX.4.4^9=412^35=X^34=1^49=XTAI^52=20150401150550208^56=MDSC^1180=X1^48=6139 ^22=8^273=150500033867^31=25250^1020=3^14=3^1022=
101^264=5

接著解讀MDGrp 此為optional 的sequence因此查看PMap=1代表有傳送 依序解讀:
268(NoMDEntries)代表迴圈層數為copy因此查看PMap=1有傳送,依序找到stop bit為1的byte為第34個Byte 10001011還原後為00001010其值為10
FIX:8=FIX.4.4^9=412^35=X^34=1^49=XTAI^52=20150401150550208^56=MDSC^1180=X1^48=6139 ^22=8^273=150500033867^31=25250^1020=3^14=3^1022=
101^264=5^268=10

接著依序解這10個Entry
PMap1(第35Byte):



269(MDEntryType: 為copy因此查看PMap=1有傳送,依序找到stop bit為1的byte為第36個Byte 10110000還原後為00110000其值為String ”0”
279(MDUpdateAction): 為copy因此查看PMap=1有傳送,依序找到stop bit為1的byte為第37個Byte 10110000還原後為00110000其值為String ”0”
270(MDEntryPx) 為optional且為delta依序找到stop bit為1的byte為第40個Byte 00000001 01000100 11110001 還原後為110001001110000 值為25200
271(MDEntrySize) 為optional且為delta依序找到stop bit為1的byte為第41個Byte 10000101還原後為00000100值為4

PMap2:



269(MDEntryType) 為copy因此查看PMap=0未傳送 為上次的值String ”0”
279(MDUpdateAction) 為copy因此查看PMap=0未傳送 為上次的值String ”0”
270(MDEntryPx) 為optional且為delta依序找到stop bit為1的byte為第44個Byte 01111111 10011100 還原後為11111110011100值為-100 將上一個值拿來計算25200-100 = 25100
271(MDEntrySize) 為optional且為delta依序找到stop bit為1的byte為第45個Byte 10001011還原後為0001010值為+10將上一個值拿來計算4+10 = 14

PMap3:


269(MDEntryType) 為copy因此查看PMap=0未傳送 為上次的值String ”0”
279(MDUpdateAction) 為copy因此查看PMap=0未傳送 為上次的值String ”0”
270(MDEntryPx) 為optional且為delta依序找到stop bit為1的byte為第47個Byte 11001110還原後為1001110值為-50 將上一個值拿來計算25100-50 = 25050
271(MDEntrySize) 為optional且為delta依序找到stop bit為1的byte為第48個Byte 10001001還原後為0001001值為+8將上一個值拿來計算14+8= 22

PMap4:



269(MDEntryType) 為copy因此查看PMap=0未傳送 為上次的值String ”0”
279(MDUpdateAction) 為copy因此查看PMap=0未傳送 為上次的值String ”0”
270(MDEntryPx) 為optional且為delta依序找到stop bit為1的byte為第50個Byte 11001110還原後為1001110值為-50 將上一個值拿來計算25050-50 = 25000
271(MDEntrySize) 為optional且為delta依序找到stop bit為1的byte為第51個Byte 11110001還原後為1110001值為-15將上一個值拿來計算22-15= 7

PMap5:



269(MDEntryType) 為copy因此查看PMap=0未傳送 為上次的值String ”0”
279(MDUpdateAction) 為copy因此查看PMap=0未傳送 為上次的值String ”0”
270(MDEntryPx) 為optional且為delta依序找到stop bit為1的byte為第54個Byte 1111110 10111000還原後為1111100111000值為-200 將上一個值拿來計算25000-200 = 24800
271(MDEntrySize) 為optional且為delta依序找到stop bit為1的byte為第55個Byte 11111011 還原後為1111011值為-5將上一個值拿來計算 7-5=2

PMap6:



269(MDEntryType) 為copy因此查看PMap=1有傳送,依序找到stop bit為1的byte為第36個Byte 10110001還原後為00110001其值為String ”1”
279(MDUpdateAction) 為copy因此查看PMap=0未傳送 為上次的值String ”0”
270(MDEntryPx) 為optional且為delta依序找到stop bit為1的byte為第59個Byte 00000011 11000011還原後為00000111000011值為+450將上一個值拿來計算24800+450= 25250
271(MDEntrySize) 為optional且為delta依序找到stop bit為1的byte為第60個Byte 11111111還原後為1111111值為-1將上一個值拿來計算2-1=1

PMap7:



269(MDEntryType) 為copy因此查看PMap=0未傳送 為上次的值String ”1”
279(MDUpdateAction) 為copy因此查看PMap=0未傳送 為上次的值String ”0”
270(MDEntryPx) 為optional且為delta依序找到stop bit為1的byte為第62個Byte 10110011還原後為0110010值為+50將上一個值拿來計算25250+50=25300
271(MDEntrySize) 為optional且為delta依序找到stop bit為1的byte為第63個Byte 10000100還原後為0000011值為+3將上一個值拿來計算1+3=4

PMap8:



269(MDEntryType) 為copy因此查看PMap=0未傳送 為上次的值String ”1”
279(MDUpdateAction) 為copy因此查看PMap=0未傳送 為上次的值String ”0”
270(MDEntryPx) 為optional且為delta依序找到stop bit為1的byte為第66個Byte 00000001 11111011還原後為00000011111010值為+250將上一個值拿來計算25300+250=25500
271(MDEntrySize) 為optional且為delta依序找到stop bit為1的byte為第67個Byte 10000100還原後為0000011值為+3將上一個值拿來計算4+3=7

PMap9:



269(MDEntryType) 為copy因此查看PMap=0未傳送 為上次的值String ”1”
279(MDUpdateAction) 為copy因此查看PMap=0未傳送 為上次的值String ”0”
270(MDEntryPx) 為optional且為delta依序找到stop bit為1的byte為第69個Byte

10110011還原後為0110011值為+50將上一個值拿來計算25500+50=25600
271(MDEntrySize) 為optional且為delta依序找到stop bit為1的byte為第70個Byte 10000011還原後為0000010值為+2將上一個值拿來計算7+2=9

PMAp10:



269(MDEntryType) 為copy因此查看PMap=0未傳送 為上次的值String ”1”
279(MDUpdateAction) 為copy因此查看PMap=0未傳送 為上次的值String ”0”
270(MDEntryPx) 為optional且為delta依序找到stop bit為1的byte為第73個Byte 00000001 10010111還原後為00000010010111值為+150將上一個值拿來計算25600+150=25750

271(MDEntrySize) 為optional且為delta依序找到stop bit為1的byte為第74個Byte 11111000還原後為1111000值為-8將上一個值拿來計算9-8=1

8=FIX.4.4^9=412^35=X^34=1^49=XTAI^52=20150401150550208^56=MDSC^1180=X1^48=6139
22=8^273=150500033867^31=25250^1020=3^14=3^1022=101^264=5
^268=10^269=0^279=0^270=25200^271=4^269=0^279=0^270=25100^271=14^269=0^279=0
^270=25050^271=22^269=0^279=0^270=25000^271=7^269=0^279=
0^270=24800^271=2^269=1^279=0^270=25250^271=1^269=1^279=0
^270=25300^271=4^269=1^279=0^270=25550^271=7^269=1^279=0
^270=25600^271=9^269=1^279=0^270=25750^271=1^10=252

結論

FAST一開始雖然用在FIX訊息的優化,但實際上此種協定可以使在需要高頻率交換資料的網路通訊上,而壓縮方式不同於一般ZIP或RAR利用每則訊息內容中連續重複出現的字母壓成數量和字母來達到壓縮,這種方式對上下則訊息沒有相關性,而FAST是用XML模板制定資料的相關性來實現壓縮資料的方式,第一則的訊息和第二則訊息間的關係來達到壓縮,例如第一則要傳送的271=200而第二則訊息要送271=195在FAST的傳送上可定義為delta即可在第二筆傳送-5即可 不用傳輸195在訊息上即可省下1Byte的傳輸成本。

最後從FIX訊息可看出經過FAST壓縮技術後能將429個Byte的資料,壓縮至76個Byte壓縮率達17.7%對於網路頻寬進行相當大的節省,雖然減少了網路頻寬卻增加了壓縮及解壓縮的計算且FAST為編碼過後的資料,無法直接的看出實際的值,在找尋程式錯誤上難度會增加一些,故應考量整體系統的需求而決定使用FIX協定或者FAST的壓縮技術來實作系統。(本文轉載自RUN!PC網站6/25發表文章)

參考資料

1. fixtradingcommunity
2. 台灣證券交易所
3. wikipedia