前言 FIX协议是什么?我为什么会接触到FIX协议?由于工作上的关系,自己负责的一个模块需要实现fix报文与xml报文之间的转换。故需要了解fix是个什么东西才能实现这个转换功能。总体看来,FIX协议还是有些许的复杂,些许的不好理解。闲话少叙,开干!
什么是FIX Financial Information eXchange(FIX)金融信息交换协议的制定是由多个致力于提升其相互间交易流程效率的金融机构和经纪商于1992年共同发起。这些企业把他们及他们的行业视为一个整体,认为能够从对交易指示,交易指令及交易执行的高效电子数据交换的驱动中获利。FIX由此诞生,一个不受单一实体控制的开放消息标准,一个能够被调整组建适用于任何一个企业的商务需求的协议。
FIX协议包含2个层次:会话层和应用层。会话层与数据的通信相关;而应用层定义了商务相关数据内容。 2006年10月,FPL发布了FIX5.0。FIX5.0引入TI(the transport independence )传输无关框架。TI将FIX会话层从应用层协议中分离出来。在TI框架下,应用层协议消息可以通过任意合适的传输技术进行传送,在这里,FIX会话层协议是FIX应用层消息的可选传输传输协议之一。两个协议层的版本标注将会有所不同,FIX X.Y为FIX应用层协议版本;FIXT X.Y 为FIX会话层协议版本编号。如下图:
关于FIX协议会话层的细节和内容这篇博文不会展开介绍,会话层更多关于通信细节,此文重点介绍应用层的内容,这也是我用来进行报文转换的基础。
FIX协议格式
这是这篇博文的重点
目前,FIX协议存在2种语法格式。
“tag=value”语法格式
“FIXML语法” 语法格式
同一个商业信息流适用于任何一种语法。
普通FIX语法规则 普通FIX语法规则,是对“标记=值”和“FIXML”2种语法都适用的一般规则。
Data Type
int:整型 没有小数点,逗号,可以包含正负号的数字序列。注,int的值前面可以包含0。(如 “00023” = “23”)
Examples:723 in field 21 would be mapped int as |21=723|
-723 in field 12 would be mapped int as |12=-723|
float: 浮点数。可包含小数点和正负号的数字序。累计总长度为15个数字。前面可以有0,小数末尾可加零,或截尾
String: 字符串。是大小写敏感的。
char: 字符。除分界符号SOH外的字符。大小写敏感。
data: 原始数据。 没有格式和内容限制。之前紧接有一个长度域。长度域应制定data数据域包含的字节数(不好含分界符所占字节)。数据中可能包含分界符字节,所以需要用data类型数据长度来辅助区别。
Required Fields 必选数据域 协议每个消息由必选、可选和条件必选(根据其他域的不同)数据域构成。系统应当设计成可支持只提供必选和条件必选数据域的相关操作。
FIX “Tag=Value”语法
Message Format消息格式
FIX消息的一般格式为:一个标准头+消息体+一个标准的尾部 每个消息由一个包含多个 ,由分界符隔开的,“标记=值”数据域的流构成。标记为TagNum数据类型。所有的标记都有一个特定的值。可选域可以不出现。无值消息将会产生一个Reject消息。
FIX消息的一般格式为:一个标准头+消息体+一个标准的尾部
消息头的前三个域为 BeginString(tag #8)+BodyLenth(tag#9)+MsgType(tag#35)
标准消息尾的最后一个域为CheckSum(tag#10)
在重复数据组内的数据域必须按照FIX规范文档中规定的顺序明确定义。Noxxx域,xxx表示循环组的计数,必须放置在循环组数据内容的的前面。
一个特定的tag 数应当是唯一的。如果重复,将被认为是一个违反规范文档的错误。错误应当向FIX Global Technical Committee报告 此外,某些数据类型为Multiple CharValue的数据域,可以包含多个由空格隔开,由一个SOH技术的多个部分。 在同一个消息里,包含明文部分和密文部分的数据的数据域也是可能的。通常用于验证和认证。
2 . Field Delimiter: 域分界符
数据域分界符,在FIX一个消息内的所有数据域由一个分界字符标记结尾。用ASCII码的“SOH”(#001,hex:0x01)进行间隔。所有消息由“8=FIX.x.y”标记开始,最后由“10=nnn“标记结束。
3 .Repeating Groups:循环组
允许在一个循环组里出现重复的数据域。372=x为循环组的第一个域。
如果使用循环组,循环组的第一个域是必选项,作为新循环组的一个分界。该第一域紧跟在NoXXX后面,然后,当NoXXX值大于0时,变成当条件必选。
NoXXX域(比如,NoTradingSessions,NoAllocs)定义了循环组实例的数量,对一个循环组只出现一次,且必须在循环组内容之前。
如果在一个循环组中的一个域是必选的,则NoXXX是必选的。如果所有循环组中的成员是可选的,则NoXXX域也应该是可选的。
如果一个循环组域被列为必选,则它必须出现在该循环组的每一个实例中。
循环组在消息中被设计成通过缩排,和à符号进行定义。一些循环组可以在其他循环组中级联出现。可大于一层的级联。
4 .User Defined Fields: 用户自定义域
为给用户提供最大的灵活性,FIX协议允许用户自定义域。 这些域在认同的参与者之间实现、应用,并且应注意避免冲突。
Tag数在5000 到9999保留用于用户自定义域。这些tag值用于企业联盟的信息交换。可以通过FIX网站进行注册。
10000以上保留用于单一企业内部使用。不用注册。
5 .Example
char* fix = "8=FIX.4.2\0019=272\00135=E\00134=126\00166666=1095350459\00150=00303\00149=BUYSIDE\00152"
"=20040916-16:19:18.328\00168=2\00156=SELLSIDE\00173=2\00111"
"=1095350459\00155=fred\00140=1\00167=1\0011=00303\00178=3\00179=string\00179=string\00179=string\00154=1\001 59=3\001"
"11=1095350460\00167=2\00140=1\00159=3\0011=00303\00178=3\00179=string\00179=string\00179=string\00155=feed\0 0154=5\001394=3\00110=120\001";
FIXML语法格式 目前采用的都是tag=value来完成数据交换,FIXML格式用的不多。 其中FIXML可读性更强, 但占用更多的带宽资源。
Example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 <FIXML > <FIXMLMessage > <Header > <PossDupFlag Value ="N" /> <PossResend Value ="N" /> <SendingTime > 20020103-12 00 01</SendingTime > <Sender > <CompID > AFUNDMGR</CompID > </Sender > <Target > <CompID > ABROKER</CompID > </Target > </Header > <ApplicationMessage > <Order > <ClOrdID > 1968</ClOrdID > <Account > 4130287</Account > <HandlInst Value ="1" /> <ExDestination Value ="L" /> <Instrument > <Symbol > IBM</Symbol > <SecurityID > 459200101</SecurityID > <SecurityIDSource Value ="1" /> </Instrument > <Side Value ="2" /> <TransactTime > 20021120-12 13 12</TransactTime > <OrderQtyData > <OrderQty > 1000</OrderQty > </OrderQtyData > <OrdType Value ="2" /> <Price > 93.25</Price > <Currency Value ="USD" /> </Order > </ApplicationMessage > </FIXMLMessage > </FIXML >
数据字典
什么是数据字典?为何又需要数据字典?
回答这两个问题前,需要了解清楚上面关于fix协议格式的内容。fix协议第一种语法采用”tag=value”的方式,其中tag就是预定义的东西,为了节省传输过程中的字节数,fix把一些能够用到的字段名进行了预定义,写到了一个文件中,并命名为诸如FIX42.xml,FIX44.xml等。
实际应用中,FIX引擎在对fix报文的组包,解包过程中需要解析这些文件,比如解析到tag为8 ,则代表字段BeginString 。FIXXX.xml文件中还定义了不同的结构,”messages”,”fields”,”header”,”group”等。详细如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 <fix major ='4' type ='FIX' servicepack ='0' minor ='4' > <header > <field name ='BeginString' required ='Y' /> <field name ='BodyLength' required ='Y' /> <field name ='MsgType' required ='Y' /> <field name ='SenderCompID' required ='Y' /> <field name ='TargetCompID' required ='Y' /> <field name ='OnBehalfOfCompID' required ='N' /> <field name ='DeliverToCompID' required ='N' /> <field name ='SecureDataLen' required ='N' /> <field name ='SecureData' required ='N' /> <field name ='MsgSeqNum' required ='Y' /> <field name ='SenderSubID' required ='N' /> <field name ='SenderLocationID' required ='N' /> <field name ='TargetSubID' required ='N' /> <field name ='TargetLocationID' required ='N' /> <field name ='OnBehalfOfSubID' required ='N' /> <field name ='OnBehalfOfLocationID' required ='N' /> <field name ='DeliverToSubID' required ='N' /> <field name ='DeliverToLocationID' required ='N' /> <field name ='PossDupFlag' required ='N' /> <field name ='PossResend' required ='N' /> <field name ='SendingTime' required ='Y' /> <field name ='OrigSendingTime' required ='N' /> <field name ='XmlDataLen' required ='N' /> <field name ='XmlData' required ='N' /> <field name ='MessageEncoding' required ='N' /> <field name ='LastMsgSeqNumProcessed' required ='N' /> <component name ='Hop' required ='N' /> </header > <messages > ...... <fields > <field number ='1' name ='Account' type ='STRING' /> <field number ='2' name ='AdvId' type ='STRING' /> <field number ='3' name ='AdvRefID' type ='STRING' /> <field number ='4' name ='AdvSide' type ='CHAR' > <value enum ='B' description ='BUY' /> <value enum ='S' description ='SELL' /> <value enum ='X' description ='CROSS' /> <value enum ='T' description ='TRADE' /> </field > <field number ='5' name ='AdvTransType' type ='STRING' > <value enum ='N' description ='NEW' /> <value enum ='C' description ='CANCEL' /> <value enum ='R' description ='REPLACE' /> </field > <field number ='6' name ='AvgPx' type ='PRICE' /> <field number ='7' name ='BeginSeqNo' type ='SEQNUM' /> <field number ='8' name ='BeginString' type ='STRING' /> <field number ='9' name ='BodyLength' type ='LENGTH' /> <field number ='10' name ='CheckSum' type ='STRING' /> <field number ='11' name ='ClOrdID' type ='STRING' /> <field number ='12' name ='Commission' type ='AMT' /> <field number ='13' name ='CommType' type ='CHAR' > <value enum ='1' description ='PER_UNIT' /> <value enum ='2' description ='PERCENTAGE' /> <value enum ='3' description ='ABSOLUTE' /> <value enum ='4' description ='4' /> <value enum ='5' description ='5' /> <value enum ='6' description ='POINTS_PER_BOND_OR_CONTRACT_SUPPLY_CONTRACTMULTIPLIER' /> </field > <field number ='14' name ='CumQty' type ='QTY' /> <field number ='15' name ='Currency' type ='CURRENCY' /> <field number ='16' name ='EndSeqNo' type ='SEQNUM' /> ...... <field number ='946' name ='CollInquiryResult' type ='INT' > <value enum ='0' description ='SUCCESSFUL' /> <value enum ='1' description ='INVALID_OR_UNKNOWN_INSTRUMENT' /> <value enum ='2' description ='INVALID_OR_UNKNOWN_COLLATERAL_TYPE' /> <value enum ='3' description ='INVALID_PARTIES' /> <value enum ='4' description ='INVALID_TRANSPORT_TYPE_REQUESTED' /> <value enum ='5' description ='INVALID_DESTINATION_REQUESTED' /> <value enum ='6' description ='NO_COLLATERAL_FOUND_FOR_THE_TRADE_SPECIFIED' /> <value enum ='7' description ='NO_COLLATERAL_FOUND_FOR_THE_ORDER_SPECIFIED' /> <value enum ='8' description ='COLLATERAL_INQUIRY_TYPE_NOT_SUPPORTED' /> <value enum ='9' description ='UNAUTHORIZED_FOR_COLLATERAL_INQUIRY' /> <value enum ='99' description ='OTHER' /> </field > <field number ='947' name ='StrikeCurrency' type ='CURRENCY' /> <field number ='948' name ='NoNested3PartyIDs' type ='NUMINGROUP' /> <field number ='949' name ='Nested3PartyID' type ='STRING' /> <field number ='950' name ='Nested3PartyIDSource' type ='CHAR' /> <field number ='951' name ='Nested3PartyRole' type ='INT' /> <field number ='952' name ='NoNested3PartySubIDs' type ='NUMINGROUP' /> <field number ='953' name ='Nested3PartySubID' type ='STRING' /> <field number ='954' name ='Nested3PartySubIDType' type ='INT' /> <field number ='955' name ='LegContractSettlMonth' type ='MONTHYEAR' /> <field number ='956' name ='LegInterestAccrualDate' type ='LOCALMKTDATE' /> </fields > </fix >
FIX引擎 实现fix协议的开源闭源代码库有很多很多,选择的时候可以根据实际情况从性能,大小,易用性,二次开发等方面考虑。这里给出一个fix引擎的列表 ,如下:
我自己使用的是Quickfix !
完事 关于FIX协议的基本知识基本介绍完毕,由于我自己使用的时候不涉及到会话层的东西,故在研究开源库Quickfix的源代码的时候,刨去了关于通信层面的代码,着重研究了Quickfix中关于数据字典的解析,fix报文的处理等的代码。后面有时间打算回忆记录下Quickfix的源代码。
Blog:
2017-02-09 于杭州By 史矛革