一段完整的FAST数据
首先附上一段完整的上海个股期权FAST十六进制数据流,接下来的解码示例也会采用这段数据流,编码模板采用的就是上一篇中举例的模板
另外这里只讲解码,不讲编码,因为解码和编码就是一个相反的过程,解码需要先解出PMAP,然后根据PMAP进行解析,编码则需要生成PMAP。而且官方文档里已经有很多编码例子了。需要解码教程的人也应该多于需要编码教程的人。

基础知识

FAST数据采用停止位编码。所谓停止位编码,就是指每一个字节的最高位是用来标识下一个字节是否还属于同一字段,因此每个字节只有7位的有效位。
比如6F 81[01101111 10000001],第一个字节的最高位是0,所以这个字段到这里还没结束,第二个字节为81,最高位为1,表示这个字段编码后的字节到这里就结束了,该字段编码后的长度为两个字节。

PMAP的解析

PMAP位于数据流的开头。
首先有一点,PMAP不一定会出现在FAST数据流中,因为字段是否在PMAP上占位需要根据字段的presence值和操作符决定,如果某个模板上的字段都不需要PMAP即可判定存在状态,则PMAP就不需要出现。
示例中的开头为[F6 1F A1….],第一个字节为F6[1111 0110]最高位是1,表示这个字段到这个字节就结束了,所以PMAP编码后的字节就是F6
单字节解码很简单,首先把最高位1去除,留下有效位,变成76[0111 0110],然后左移一位,就得到了实际的PMAP为EC[1110 1100]

PMAP的使用方法

首先算出各个字段是否在PMAP中占位,然后依次对照PMAP的每一位,PMAP EC[1110 1100]对应模板解析示例如下:

字段是否占位    是否在数据流中出现    PMAP中对应位的值
模板ID出现1
MDStreamID出现1
SecurityID出现1
Symbol不出现0
NumTrades
TradeVolume
TotalValueTraded
PrevClosePx
PrevSetPx
TotalLongPosition
MDFullGrp出现1
TradingPhaseCode出现1
字段解析

接下来解析下一个字段,根据停止位编码,下一个字段编码后的字节为1F A1[00011111 10100001],对应字段为模板ID,用来表示这段数据流是使用哪个模板进行编码的。
·模板ID
[1F A1] -> 4001
模板ID是正整形。
多个字节的正整形解码可以这样做:首先将停止位去除,变成1F 21[00011111 00100001],然后依次将高位字节左移7位再加上低位字节,即1F左移7位变成0F 80[00001111 10000000],再加上21变成0F A1,FA1就是模板ID,转换成十进制就是4001,也就是这个模板。实际解码过程中,首先需要获得模板ID,才能根据具体模板得到上面那个表格。

·MDStreamID
[4D 30 33 30 B1] -> "M0301"
下一个字段是MDStreamID,类型为string,操作符为copy。操作符copy指的是若存在前值,且没有出现在数据流中,则该值直接取前值,否则数据流中的值作为新的前值。所谓前值,主要在重复组里会用到,待会解析到sequence重复组会细说。
同样的,首先去除停止位,就变成了[4D 30 33 30 31],string类型解析无需做移位操作,去除停止位后就得到了字段的ASCII码,转换成对应字符就是"M0301"。

·SecurityID
[31 30 30 30 30 39 31 B1] -> “10000911”
SecurityID类型同样为string,所以解码很简单。但操作符为tail,即接尾操作符,这个操作符配合前值可以压缩大量相同的字符。这段数据流后面会用到,到时候会细说这个操作符的解析方法。

·Symbol
根据PMAP得知Symbol不出现在数据流中。

·NumTrades
[06 B6] -> 821
NumTrades类型为uInt64,64位的正整形,操作符为delta,差值操作符,这个也需要配合前值使用,下面数据解析用到的时候再提。首先参照模板ID的解析方式去解析,简化流程如下:
[06 B6] -> [06 36] -> 06<<7 + 36 = 0x336 即 822
另外由于presence为可选且值非负,所以需要做自减一操作,因此最终的字段值应该为821。你要问我为什么要自减一,那我只能回答因为编码的时候做了自加一操作(╯‵□′)╯︵┻━┻

·TradeVolume
[1B CA] -> 3529
TradeVolume与NumTrades同样的解析方法
[1B CA] -> [1B 4A] -> 1B<<7 +4A = 0xDCA -> 非负且optional自减一 ->0xDC9 -> 3529

·TotalValueTraded
[81] [1C 72 BC] -> 473404
TotalValueTraded类型为decimal,非要翻译的话就叫十进位类型好了,这种类型由两部分构成,指数部分和尾数部分,所以对应数据流需要取出两个停止位字段。按出现顺序,在前的是指数,在后的是尾数,分别对指数和尾数按照整形进行解码。指数非负需要自减一,尾数部分无需自减一。
指数:[81] -> [01] -> 0x01 -> 1 -> 非负且optional自减一 -> 0
尾数:[1C 72 BC] -> [1C 72 3C] -> (1C<<7+72)<<7+3C = 0x7393C -> 473404
结合指数尾数,可以得到TotalValueTraded的值为 473404 * 100 = 473404
操作符为delta,这里解析暂时用不到,下面用到的时候再说。

·PrevClosePx
[7F 7F 7F 7F FC] [00 F4] -> 0.0116
这里出现了负整形,指数部分的最高字节为7F [0111 1111],其中有效位的最高位即符号位(已加粗)为1,说明该值为负数。
首先知道一点,在计算机中,负数采用绝对值的补码表示,补码即原码取反加一
所以要获得原码只需要先自减1再取反,[7F 7F 7F 7F FC]自减一得到7F … 7F FB[01111111 … 01111111 11111011],取出每个字节的有效位拼在一起再取反,得到0x4[0100],所以指数部分的值就是-4,因为值为负数,所以无需自减一。
尾数部分按正整形解析即可[F4] -> [74] -> 116
PrevClosePx值为 116 * 10-4 = 0.0116

·PrevSetPx
[7F 7F 7F 7F FC] [00 F4] -> 0.0116
类型decimal,和PrevClosePx一样的解法,值也是一样的

·TotalLongPosition
[01 24 BD] -> 21052
类型uInt64,参照NumTrades:
[01 24 BD] -> [01 24 3D] -> (01<<7+24)<<7+3D = 0x523D -> 非负且optional自减一 -> 0x523C 即 21052