一、QuickFIX编译构建

1、Windows

使用VS Studio打开quickfix_vs12.sln、quickfix_vs14.sln、quickfix_vs15.sln。

链接库:链接lib\quickfix.lib和lib\debug\quickfix.lib到应用。

头文件:拷贝头文件到include目录。

编译控制:src目录的config_windows.h文件用于控制编译时选项。

#define HAVE_STLPORT 1

使用stlport替换Visual C++ STL进行编译。

#define HAVE_ODBC 1

QuickFIX支持ODBC数据库。

#define HAVE_MYSQL 1

QuickFIX支持MySQL。如果开启,MySQL的include和lib目录必须在Visual Studio的搜索路径内。

#define HAVE_POSTGRESQL 1

QuickFIX支持PostgreSQL数据库,如果开启,则PostgreSQL的include和lib目录必须在Visual Studio的搜索路径内。

2、Linux

Linux、Solaris、FreeBSD、Mac OS X使用相同的编译构建流程。

编译配置:

configure

编译配置选项如下:

--prefix=directory:指定安装目录。

--with-python2:编译Python2 API

--with-python3:编译Python3 API

--with-ruby:编译Ruby API

--with-mysql:支持MySQL

--with-postgresql:支持PostgreSQL

--with-stlport=directory:使用stlport进行编译,替换标准的GCC STL实现。

编译:

make

安装:

make install

二、QuickFIX数据库支持

1、MySQL

运行src/sql/mysql目录中的创建脚本,需要传递创建数据库的授权用户,必须安装MySQL数据库。

create.sh mysqlcreate.bat mysql

2、MSSQL

必须安装MSSQL,运行src/sql/mssql目录的创建脚本,必须传递创建MSSQL数据库的用户。

create.bat sa

3、PostgreSQL

必须安装PostgreSQL,运行src/sql/postgresql脚本,必须传递创建PostgreSQL数据库的用户。

create.sh postgrescreate.bat postgres

三、测试

1、测试

QuickFIX开发由功能测试和单元测试组件驱动。

2、Windows

(1)单元测试

test目录下执行:

runut release [port]

端口用于测试socket功能。

(2)验证测试

在test目录执行:

runat release [port]runat_threaded release [port]

端口用于Socket Server监听连接。

3、Linux

Linux、Solaris、FreeBSD、Mac OS X中单元测试和功能测试相同。

(1)单元测试

test目录执行:

runut.sh [port]

端口用于测试Socket功能,如果QuickFIX要支持数据库,需要更新数据库设置cfg/ut.cfg。

(2)验证测试

在test目录执行:

runat.sh [port]runat_threaded.sh [port]

端口用于监听Socket服务器的连接。

四、工程设置

1、Windows

在Microsoft Visual Studio打开project | properties。

(1)设置C/C++ | Code Generation | Enable C++ Exceptions为Yes。

(2)C/C++ | Code Generation | Runtime Library设置Multithreaded DLL或Debug Multithreaded DLL。

(3)C/C++ | General | Additional Include Directories增加quickfix根目录。

(4)Linker | Input | Additional Dependencies必须包含quickfix.lib和ws2_32.lib。

(5)Linker | General | Additional Library Directories增加quickfix/lib目录。

2、Linux

(1)使用-fexceptions选项开启异常。

(2)推荐使用-finline-functions选项优化。

(3)QuickFIX必须使用-lquickfix选项进行链接。如果QuickFIX使用pthreads、libxml,则使用-lpthread选项、-lxml2选项、-lz选项链接。

(4)Solaris系统必须使用-lnsl选项和-lsocket选项链接。

五、应用创建

1、Application接口

使用QuickFIX创建FIX应用只需要实现QuickFIX Application接口。QuickFIX Application接口如下:

namespace FIX
{class Application{public:virtual ~Application() {};virtual void onCreate( const SessionID& ) = 0;virtual void onLogon( const SessionID& ) = 0;virtual void onLogout( const SessionID& ) = 0;virtual void toAdmin( Message&, const SessionID& ) = 0;virtual void toApp( Message&, const SessionID& )throw( DoNotSend ) = 0;virtual void fromAdmin( const Message&, const SessionID& )throw( FieldNotFound, IncorrectDataFormat, IncorrectTagValue, RejectLogon ) = 0;virtual void fromApp( const Message&, const SessionID& )throw( FieldNotFound, IncorrectDataFormat, IncorrectTagValue, UnsupportedMessageType ) = 0;};
}

onCreate:QuickFIX创建Session时调用。无论对等方是否连接,在应用程序生命周期内Session会一直存在。一旦Session建立,并可以向其发送消息。如果没有登录,消息会在对等方建立连接时发送。

onLogon:通知和对等方的有效登录连接已经建立。连接建立,FIX logon进程完成双方有效登录消息交换后调用。

onLogout:通知FIX Session不再在线。正常登出或网络连接中断时调用。

toAdmin:可以查看FIX引擎发送给对等方的管理类型消息,应用程序通常不使用,可以在管理类型消息发送前增加字段。

toApp:发送到对等方的应用类型消息的回调函数,如果函数内抛出DoNotSend异常,则应用程序不会发送消息。

fromAdmin:通知FIX Session,对等方已经发送一条管理类型消息到FIX引擎,用于进行验证密码等登录消息的其它验证操作。抛出RejectLogon异常将会断开与对等方连接。

fromApp:用于接收应用类型消息。如果应用程序是卖方OMS,本函数内可以获取新的订单请求;如果应用程序是买方应用,则在本函数内获取成交回报。如果抛出FieldNotFound异常,对等方会收到表明消息缺失必需字段拒绝通知。如果试图索引缺失字段,Message类会抛出异常,因此不需要显式抛出异常。可以抛出UnsupportedMessageType异常,但会导致对等方收到拒绝通知,通知对等方本地应用程序无法处理这些类型的消息。当字段包含不支持的值时会抛出IncorrectTagValue异常。

2、示例

FIX Acceptor示例代码如下:

#include "quickfix/FileStore.h"
#include "quickfix/FileLog.h"
#include "quickfix/SocketAcceptor.h"
#include "quickfix/Session.h"
#include "quickfix/SessionSettings.h"
#include "quickfix/Application.h"int main( int argc, char** argv )
{try{if(argc < 2) return 1;std::string fileName = argv[1];FIX::SessionSettings settings(fileName);MyApplication application;FIX::FileStoreFactory storeFactory(settings);FIX::FileLogFactory logFactory(settings);FIX::SocketAcceptor acceptor(application, storeFactory, settings, logFactory /*optional*/);acceptor.start();// while( condition == true ) { do something; }acceptor.stop();return 0;}catch(FIX::ConfigError& e){std::cout << e.what();return 1;}
}

如果需要使用FIX Initiator,则需要使用SocketInitiator。

六、QuickFix配置

1、QuickFix配置简介

QuickFix中initiator或acceptor会维护多个Fix Session。QuickFix中使用BeginString(Fix版本号)、SenderCompID、TargetCompID的组合标识一个Session,Session标识用于区分其它不同的Session。

Session配置文件包含[DEFAULT]和[SESSION]两种分节,[SESSION]分节表示QuickFix中定义一个Session,[DEFAULT]表示所有Session默认使用的配置项,如果不提供QuickFix所需的配置,QuickFix会抛出ConfigError异常,表示配置缺失或格式不正确。

如果[DEFAULT]和[SESSION]分区中都包含相同的配置项,则[SESSION]分区的配置项会覆盖[DEFAULT]分区相应的配置项。

QuickFix配置参见官网:

http://quickfixengine.org/quickfix/doc/html/configuration.html

2、QuickFix配置文件

QuickFix配置文件sessions.ini如下:

# default settings for all sessions
[DEFAULT]
ConnectionType=initiator
ReconnectInterval=60
SenderCompID=TW# session definition
[SESSION]
# inherit ConnectionType, ReconnectInterval and SenderCompID from default
BeginString=FIX.4.1
TargetCompID=ARCA
StartTime=12:30:00
EndTime=23:30:00
HeartBtInt=20
SocketConnectPort=9823
SocketConnectHost=123.123.123.123
DataDictionary=somewhere/FIX41.xml[SESSION]
BeginString=FIX.4.2
TargetCompID=INCA
StartTime=12:30:00
EndTime=21:30:00
# overide default setting for RecconnectInterval
ReconnectInterval=30
HeartBtInt=30
SocketConnectPort=6523
SocketConnectHost=3.3.3.3
# (optional) alternate connection ports and hosts to cycle through on failover
SocketConnectPort1=8392
SocketConnectHost1=8.8.8.8
SocketConnectPort2=2932
SocketConnectHost2=12.12.12.12
DataDictionary=somewhere/FIX42.xml

3、Session配置

BeginString:Session使用的Fix版本,可选值为FIXT.1.1、FIX.4.4、FIX.4.3、FIX.4.2、FIX.4.1、FIX.4.0。

SenderCompID:关联Fix Session的本地ID,包含字母和数字大小写敏感的字符串。

TargetCompID:Fix Session的对等方ID,包含字母和数字大小写敏感的字符串。

SessionQualifier:Session标识,用于区分其它Session,包含字母和数字大小写敏感的字符串。

DefaultApplVerID:只支持FIXT 1.1及后续版本,早期版本会忽略,用于指定Session的默认应用版本ID,可选值可以是ApplVerID枚举值或BeginString值,包括FIX.5.0SP2、FIX.5.0SP1、FIX.5.0、FIX.4.4、FIX.4.3、FIX.4.2、FIX.4.1、FIX.4.0、9、8、7、6、5、4、3、2。

ConnectionType:定义Session的角色,可选值为initiator、acceptor。

StartTime:Session每天开始工作的时间,时间为UTC时间,时间格式为HH:MM:SS。

EndTime:Session每天停止工作的时间,时间为UTC时间,时间格式为HH:MM:SS。

StartDay:对于周级别的长连接Session,指示Session在周中的开始工作日。

EndDay:对于周级别的长连接Session,指示Session在周中的结束工作日。

LogonTime:Session每天登录的时间,时间为UTC时间,时间格式为HH:MM:SS。

LogoutTime:Session每天登出的时间,时间为UTC时间,时间格式为HH:MM:SS。

LogonDay:对于周级别的长连接Session,指示Session在周中登录的工作日。

LogoutDay:对于周级别的长连接Session,指示Session在周中登出的工作日。

UseLocalTime:指示StartTime和EndTime使用本地时间,而不是UTC时间,FIX消息中的按照FIX协议要求仍然使用UTC时间,可选值为Y、N,默认值为N。

MillisecondsInTimeStamp:是否将毫秒增加到时间戳上,只对Fix 4.2及后续版本有效。

TimestampPrecision:指定时间戳的小数部分,可选值为0-9,如果设置,则会覆盖MillisecondsInTimeStamp。

SendRedundantResendRequests:可选值为Y、N,如果设置为Y,QuickFix会发送所有需要的重发请求,即使冗余。如果设置为N,QuickFix会试图最小化重发请求,通常用于高吞吐量的系统中。

ResetOnLogon:如果Session为Acceptors,当收到一个登录请求时确定是否要重置序列号,可选值为Y、N,默认值为N。

ResetOnLogout:当收到一个登出请求时确定是否要重置序列号为1,可选值为Y、N,默认值为N。

ResetOnDisconnect:当Session异常终止时确定是否要重置序列号为1,可选值为Y、N,默认值为N。

RefreshOnLogon:当登录时确定Session状态是否从持久层恢复,用于创建热切换的Session,可选值为Y、N,默认值为N。

4、认证配置

UseDataDictionary:Session是否使用数据字典,如果使用重复分组,应该使用数据字典。可选值为Y、N,默认值为Y。

DataDictionary:XML格式的数据字典文件,如果没有提供数据字典,只能提供基本FIX消息验证。对于FIXT.1.1以及后续版本,使用TransportDataDictionary和AppDataDictionary进行指定。

可选数据字典包括FIX44.xml、FIX43.xml、FIX42.xml、FIX41.xml、FIX40.xml。

TransportDataDictionary:XML格式定义的数据字典,只对FIXT.1.1以及后续版本有效,可选数据字典为FIXT1.1.xml。

AppDataDictionary:XML格式的验证应用消息的数据字典,只对FIXT.1.1以及后续版本有效,可选数据字典为FIX50SP2.xml、FIX50SP1.xml、FIX50.xml、FIX44.xml、FIX43.xml、FIX42.xml、FIX41.xml、FIX40.xml。可以使用前缀指定多个应用字典,如:

DefaultApplVerID=FIX.4.2
# For default application version ID
AppDataDictionary=FIX42.xml
# For nondefault application version ID
# Use BeginString suffix for app version
AppDataDictionary.FIX.4.4=FIX44.xml

ValidateLengthAndChecksum:可选值为Y、N,默认值为Y。如果设置为N,则消息的长度和校验和不正确将不会被拒绝。

ValidateFieldsOutOfOrder:可选值为Y、N,默认值为Y。如果设置为N,则订单外的字段(如body字段在header中,header字段在body中)将不会被拒绝。

ValidateFieldsHaveValues:可选值为Y、N,默认值为Y。如果设置为N,则没有值的字段将不会被拒绝。用于那些会不恰当发送控tag的系统。

ValidateUserDefinedFields:可选值为Y、N,默认值为Y。如果设置为N,则没有在数据字典中定义的用户自定义字段将不会被拒绝,或是显示在不属于的消息中。

PreserveMessageFieldsOrder:是否按照配置文件中定义的保留发送消息体中的字段顺序,可选值为Y、N,默认值为N。

CheckCompID:可选值为Y、N,默认值为Y。如果设置为N,则消息必须从包含正确的SenderCompID和TargetCompID的对等方中接收。

CheckLatency:可选值为Y、N,默认值为Y。如果设置为Y,消息必须在最大延迟内被接收。

MaxLatency:如果CheckLatency设置为Y,则表示消息可以延迟处理的时间,默认为120秒。

5、Initiator配置

ReconnectInterval:重连接时间间隔,只用于Initiator,正数,默认只为30。

HeartBtInt:心跳时间间隔,只用于Initiator,正数。

LogonTimeout:登录超时时间,正数,默认为10。

LogoutTimeout:登出超时时间,正数,默认为2。

SocketConnectPort:Session连接端口,只用于SocketInitiator。

SocketConnectHost:Session连接主机,只用于SocketInitiator,可以是IP地址或域名。

SocketConnectPort<n>:对于热切换Session的备份连接端口。

SocketConnectHost<n>:对于热切换Session的备份连接主机。

SocketNodelay:指定使用TCP_NODELAY创建Socket,目前只能定义在[DEFAULT]分区,可选值为Y、N,默认值为N。

SocketSendBufferSize:指定使用SO_SNDBUF创建Socket,目前只能定义在[DEFAULT]分区,正数,默认值为0。

SocketReceiveBufferSize:指定使用SO_RCVBUF创建Socket,目前只能定义在[DEFAULT]分区,正数,默认值为0。

6、Acceptor

SocketAcceptPort:监听端口,只用于SocketAcceptor,目前只能定义在[DEFAULT]分区。

SocketReuseAddress:指定SO_REUSADDR使用创建Socket,只用于SocketAcceptor,可选值为Y、N,默认值为Y。

SocketNodelay:指定使用TCP_NODELAY创建Socket,目前只能定义在[DEFAULT]分区,可选值为Y、N,默认值为N。

7、Storage

PersistMessages:可选值为Y、N,默认值为N。如果设置N,消息不会被持久化,QuickFix会使用填充空白消息取代正在重发的消息。

FileStorePath:存储序列号和消息的目录,必须有写权限。

MySQLStoreDatabase:存储消息和Session状态的MySQL数据库名称,默认为quickfix。

MySQLStoreUser:登录MySQL数据库的用户名,默认为root。

MySQLStorePassword:登录MySQL数据库的用户名的用户密码,默认为空。

MySQLStoreHost:MySQL数据库的地址,可以是IP或域名,默认值为localhost。

MySQLStorePort:MySQL数据库的端口,默认值为标准数据库端口3306。

MySQLStoreUseConnectionPool:是否使用数据库连接池,可选值为Y、N,默认值为N。

PostgreSQLStoreDatabase:存储消息和Session状态的PostgreSQL数据库名称,默认为quickfix。

PostgreSQLStoreUser:登录PostgreSQL数据库的用户名,默认为postgres。

PostgreSQLStorePassword:登录PostgreSQL数据库的用户名的用户密码,默认为空。

PostgreSQLStoreHost:PostgreSQL数据库的地址,可以是IP或域名,默认值为localhost。

PostgreSQLStorePort:PostgreSQL数据库的端口,默认值为标准数据库端口5432。

PostgreSQLStoreUseConnectionPool:是否使用数据库连接池,可选值为Y、N,默认值为N。

OdbcStoreUser:登录ODBC数据库的用户名

OdbcStorePassword:登录ODBC数据库的用户密码

OdbcStoreConnectionString:ODBC数据库连接字符串,默认值为DATABASE=quickfix;DRIVER={SQL Server};SERVER=(local);

8、日志配置

FileLogPath:日志存储路径,必须有写权限。

FileLogBackupPath:日志备份路径,必须有写权限。

ScreenLogShowIncoming:打印Incoming消息到标准输出,可选值为Y、N,默认值为Y。

ScreenLogShowOutgoing:打印Outgoing消息到标准输出,可选值为Y、N,默认值为Y。

ScreenLogShowEvents:打印Events消息到标准输出,可选值为Y、N,默认值为Y。

日志也可以存储到数据库中,支持MySQL、PostgreSQL、ODBC数据库。

9、SSL配置

SSLProtocol:用于控制应用程序在建立其环境时应使用的SSL协议,可选值为SSLv2、SSLv3、TLSv1、TLSv1_1、TLSv1_2、all,默认为all -SSLv2。

七、消息验证

QuickFIX会在消息到达应用程序前对其进行验证,拒绝任何格式错误的消息。XML文件定义会话支持的消息、字段和值。spec目录包含多个标准的FIX协议字典。FIX字典基本骨架如下:

<fix type="FIX" major="4" minor="1"><header><field name="BeginString" required="Y"/>...</header><trailer><field name="CheckSum" required="Y"/>...</trailer><messages><message name="Heartbeat" msgtype="0" msgcat="admin"><field name="TestReqID" required="N"/></message>...<message name="NewOrderSingle" msgtype="D" msgcat="app"><field name="ClOrdID" required="Y"/>...</message>...</messages><fields><field number="1" name="Account" type="CHAR" />...<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>...</fields>
</fix>

验证器不会有条件地拒绝必填字段,因为其规则没有明确定义。使用非预设的有条件必填字段将导致消息被拒绝。

八、接收消息

开发者感兴趣的大部分消息在应用程序重载的fromApp函数内收到,所有消息都有消息头和消息尾,消息头和消息尾分别使用 getHeader和getTrailer函数进行访问。

1、类型安全消息和字段

QuickFIX为不同FIX标准规范中定义的所有消息提供了一个类MessageCracker。

#include "quickfix/Application.h"
#include "quickfix/MessageCracker.h"class FIXApplication: public FIX::Application, public FIX::MessageCracker
{
public:/***************************************************  reimplementation from Application* ***********************************************//// Notification of a session begin createdvirtual void onCreate( const SessionID& ){}/// Notification of a session successfully logging onvirtual void onLogon( const SessionID& ){}/// Notification of a session logging off or disconnectingvirtual void onLogout( const SessionID& ){}/// Notification of admin message being sent to targetvirtual void toAdmin( Message&, const SessionID& ){}/// Notification of app message being sent to targetvoid fromApp( const FIX::Message& message, const FIX::SessionID& sessionID )throw( FIX::FieldNotFound&, FIX::IncorrectDataFormat&, FIX::IncorrectTagValue&, FIX::UnsupportedMessageType& ){crack(message, sessionID);}/// Notification of admin message being received from targetvirtual void fromAdmin( const Message&, const SessionID& )throw ( FIX::FieldNotFound, FIX::IncorrectDataFormat, FIX::IncorrectTagValue, FIX::RejectLogon ){}/// Notification of app message being received from targetvirtual void fromApp( const Message&, const SessionID& )throw ( FIX::FieldNotFound, FIX::IncorrectDataFormat, FIX::IncorrectTagValue, FIX::UnsupportedMessageType ){}/***************************************************  reimplementation from MessageCracker* ***********************************************/void onMessage( const FIX42::NewOrderSingle& message, const FIX::SessionID& ){FIX::ClOrdID clOrdID;message.get(clOrdID);FIX::ClearingAccount clearingAccount;message.get(clearingAccount);}void onMessage( const FIX42::OrderCancelRequest& message, const FIX::SessionID& ){FIX::ClOrdID clOrdID;message.get(clOrdID);// compile time error!! field not defined for OrderCancelRequestFIX::Price price;message.get(price);}
};

通过继承MessageCracker,可以使用crack函数并可以重载不同的FIX协议的消息函数,未重载的函数默认抛出UnsupportedMessageType异常。

2、类型安全字段

使用getField函数从消息中获取字段。

void fromApp( const FIX::Message& message, const FIX::SessionID& sessionID )throw( FIX::FieldNotFound&, FIX::IncorrectDataFormat&, FIX::IncorrectTagValue&, FIX::UnsupportedMessageType& )
{// retrieve value into field classFIX::Price price;message.getField(price);// another field...FIX::ClOrdID clOrdID;message.getField(clOrdID);
}

3、非类型安全

使用tag数字创建自定义字段。

void fromApp( const FIX::Message& message, const FIX::SessionID& sessionID )throw( FIX::FieldNotFound&, FIX::IncorrectDataFormat&, FIX::IncorrectTagValue&, FIX::UnsupportedMessageType& )
{// retreive value into string with integer field IDstd::string value;value = message.getField(44);// retrieve value into a field base with integer field IDFIX::FieldBase field(44, "");message.getField(field);// retreive value with an enumeration, a little bettermessage.getField(FIX::FIELD::Price);
}

九、发送消息

1、发送消息

使用静态函数方法Session::sendToTarget发送消息到对等方。

// send a message that already contains a BeginString, SenderCompID, and a TargetCompID
static bool sendToTarget( Message&, const std::string& qualifier = "" )throw(SessionNotFound&);// send a message based on the sessionID, convenient for use
// in fromApp since it provides a session ID for incoming
// messages
static bool sendToTarget( Message&, const SessionID& )throw(SessionNotFound&);// append a SenderCompID and TargetCompID before sending
static bool sendToTarget( Message&, const SenderCompID&, const TargetCompID&, const std::string& qualifier = "" )throw(SessionNotFound&);// pass SenderCompID and TargetCompID in as strings
static bool sendToTarget( Message&, const std::string&, const std::string&, const std::string& qualifier = "" )throw(SessionNotFound&);

2、类型安全消息和字段

Message构造函数接收所有必需字段,并为开发者添加正确的MsgType和BeginString。使用set方法,编译器不允许添加不是FIX字典中定义的消息的字段。

void sendOrderCancelRequest()
{FIX41::OrderCancelRequest message(FIX::OrigClOrdID("123"),FIX::ClOrdID("321"),FIX::Symbol("LNUX"),FIX::Side(FIX::Side_BUY));message.set(FIX::Text("Cancel My Order!"));FIX::Session::sendToTarget(message, SenderCompID("TW"), TargetCompID("TARGET"));
}

3、类型安全字段

setField方法用于增加任何字段到消息。

void sendOrderCancelRequest()
{FIX::Message message;FIX::Header header& = message.getHeader();header.setField(FIX::BeginString("FIX.4.2"));header.setField(FIX::SenderCompID(TW));header.setField(FIX::TargetCompID("TARGET"));header.setField(FIX::MsgType(FIX::MsgType_OrderCancelRequest));message.setField(FIX::OrigClOrdID("123"));message.setField(FIX::ClOrdID("321"));message.setField(FIX::Symbol("LNUX"));message.setField(FIX::Side(FIX::Side_BUY));message.setField(FIX::Text("Cancel My Order!"));FIX::Session::sendToTarget(message);
}

4、非类型安全

可以使用setField方法传递消息原语。

void sendOrderCancelRequest()
{FIX::Message message;// BeginStringmessage.getHeader().setField(8, "FIX.4.2");// SenderCompIDmessage.getHeader().setField(49, "TW");// TargetCompID, with enumerationmessage.getHeader().setField(FIX::FIELD::TargetCompID, "TARGET");// MsgTypemessage.getHeader().setField(35, 'F');// OrigClOrdIDmessage.setField(41, "123");// ClOrdIDmessage.setField(11, "321");// Symbolmessage.setField(55, "LNUX");// Side, with value enumerationmessage.setField(54, FIX::Side_BUY);// Textmessage.setField(58, "Cancel My Order!");FIX::Session::sendToTarget(message);
}

十、循环组

QuickFIX能够发送包含循环组甚至递归循环组的消息。所有循环组都以一个字段开头,用于指示一个集合中有多少个循环组,可以通过引用在父消息或父组中以该字段命名的类来创建组。

1、使用循环组创建消息

创建消息时,声明循环组的数量的必填字段设置为零。当添加组时,QuickFIX会自动增加字段值。

// create a market data message
FIX42::MarketDataSnapshotFullRefresh message(FIX::Symbol("QF"));// repeating group in the form of MessageName::NoField
FIX42::MarketDataSnapshotFullRefresh::NoMDEntries group;group.set(FIX::MDEntryType('0'));
group.set(FIX::MDEntryPx(12.32));
group.set(FIX::MDEntrySize(100));
group.set(FIX::OrderID("ORDERID"));
message.addGroup(group);// no need to create a new group class if we are reusing the fields
group.set(FIX::MDEntryType('1'));
group.set(FIX::MDEntryPx(12.32));
group.set(FIX::MDEntrySize(100));
group.set(FIX::OrderID("ORDERID"));
message.addGroup(group);

2、使用循环组读取消息

要从消息中拉出组,需要提供要拉出的组对象。应该首先检查实体字段的数量以获得组的总数。

// should be 2
FIX::NoMDEntries noMDEntries;
message.get(noMDEntries);FIX42::MarketDataSnapshotFullRefresh::NoMDEntries group;
FIX::MDEntryType MDEntryType;
FIX::MDEntryPx MDEntryPx;
FIX::MDEntrySize MDEntrySize;
FIX::OrderID orderID;message.getGroup(1, group);
group.get(MDEntryType);
group.get(MDEntryPx);
group.get(MDEntrySize);
group.get(orderID);message.getGroup(2, group);
group.get(MDEntryType);
group.get(MDEntryPx);
group.get(MDEntrySize);
group.get(orderID);

十一、用户自定义字段

FIX允许用户定义未在FIX协议规范中定义的字段。

非类型安全的字段操作如下:

message.setField(6123, "value");
message.getField(6123);

QuickFIX提供了创建类型安全字段对象的宏。

#include "quickfix/Field.h"namespace FIX
{USER_DEFINE_STRING(MyStringField, 6123);USER_DEFINE_PRICE(MyPriceField, 8756);
}

用户自定义字段必须定义在FIX命名空间内,应用程序中可以如下使用:

MyStringField stringField("string");
MyPriceField priceField(14.54);message.setField(stringField);
message.setField(priceField);message.getField(stringField);
message.getField(priceField);

下列宏允许定义所有支持的FIX类型的字段。只要提供一个新的宏和转换器,可以将类型与字符串进行转换,就可以编写任何类型的字段。

USER_DEFINE_STRING( NAME, NUM )
USER_DEFINE_CHAR( NAME, NUM )
USER_DEFINE_PRICE( NAME, NUM )
USER_DEFINE_INT( NAME, NUM )
USER_DEFINE_AMT( NAME, NUM )
USER_DEFINE_QTY( NAME, NUM )
USER_DEFINE_CURRENCY( NAME, NUM )
USER_DEFINE_MULTIPLEVALUESTRING( NAME, NUM )
USER_DEFINE_EXCHANGE( NAME, NUM )
USER_DEFINE_UTCTIMESTAMP( NAME, NUM )
USER_DEFINE_BOOLEAN( NAME, NUM )
USER_DEFINE_LOCALMKTDATE( NAME, NUM )
USER_DEFINE_DATA( NAME, NUM )
USER_DEFINE_FLOAT( NAME, NUM )
USER_DEFINE_PRICEOFFSET( NAME, NUM )
USER_DEFINE_MONTHYEAR( NAME, NUM )
USER_DEFINE_DAYOFMONTH( NAME, NUM )
USER_DEFINE_UTCDATE( NAME, NUM )
USER_DEFINE_UTCTIMEONLY( NAME, NUM )
USER_DEFINE_NUMINGROUP( NAME, NUM )
USER_DEFINE_SEQNUM( NAME, NUM )
USER_DEFINE_LENGTH( NAME, NUM )
USER_DEFINE_PERCENTAGE( NAME, NUM )
USER_DEFINE_COUNTRY( NAME, NUM )

十二、测试

1、单元测试

QuickFIX提供了一套综合的自动化单元测试套件。测试组件运行在UnitTest++框架上。UnTest++框架允许开发人员通过编写调用对象上的函数并声明正确行为的代码来测试C++代码。测试不仅验证了代码的正确工作,还验证了它在所有平台上的工作原理。

下列示例显示测试的设置和执行,测试用例测试验证解析器对象是否可以从流中正确提取消息。

struct readFixMessageFixture
{readFixMessageFixture(){fixMsg1 = "8=FIX.4.2\0019=12\00135=A\001108=30\00110=31\001";fixMsg2 = "8=FIX.4.2\0019=17\00135=4\00136=88\001123=Y\00110=34\001";fixMsg3 = "8=FIX.4.2\0019=19\00135=A\001108=30\0019710=8\00110=31\001";object.addToStream( fixMsg1 + fixMsg2 + fixMsg3 );}std::string fixMsg1;std::string fixMsg2;std::string fixMsg3;Parser object;
};TEST_FIXTURE(readFixMessageFixture, readFixMessage)
{std::string readFixMsg1;CHECK( object.readFixMessage( readFixMsg1 ) );CHECK_EQUAL( fixMsg1, readFixMsg1 );std::string readFixMsg2;CHECK( object.readFixMessage( readFixMsg2 ) );CHECK_EQUAL( fixMsg2, readFixMsg2 );std::string readFixMsg3;CHECK( object.readFixMessage( readFixMsg3 ) );CHECK_EQUAL( fixMsg3, readFixMsg3 );
}

2、验证测试

QuickFIX由一个脚本化的测试运行器,其附带一系列自动化验收测试。QuickFIX附带的基本测试基于FIX协议组织提供的《FIX会话层测试用例和预期行为》。QuickFIX的测试会验证QuickFIX是否符合FIX协议规范。QuickFIX测试的自动化特性保证了QuickFIX的未来版本不会破坏任何当前功能。

也许更重要的是如何使用测试驱动QuickFIX的开发。在编写任何一行支持FIX协议的代码前,必须先编写测试用例。

这种测试优先的方法为开发人员建立了一个目标,开发人员将客观地验证其是否正确地实现了FIX标准。

下列测试脚本的示例测试在接收到小于预期MsgSeqNum的NewSeqNo值时FIX引擎的行为。

iCONNECTI8=FIX.4.235=A34=149=TW52=>TIME>56=ISLD98=0108=30
E8=FIX.4.29=5735=A34=149=ISLD52=00000000-00:00:0056=TW98=0108=3010=0# sequence reset without gap fill flag (default to N)
I8=FIX.4.235=434=049=TW52=>TIME>56=ISLD36=1
E8=FIX.4.29=11235=334=249=ISLD52=00000000-00:00:0056=TW45=058=Value is incorrect (out of range) for this tag372=4373=510=0I8=FIX.4.235=134=249=TW52=>TIME>56=ISLD112=HELLO
E8=FIX.4.29=5535=034=349=ISLD52=00000000-00:00:0056=TW112=HELLO10=0# sequence reset without gap fill flag (default to N)
I8=FIX.4.235=434=049=TW52=>TIME>56=ISLD36=1123=N
E8=FIX.4.29=11235=334=449=ISLD52=00000000-00:00:0056=TW45=058=Value is incorrect (out of range) for this tag372=4373=510=0I8=FIX.4.235=134=349=TW52=>TIME>56=ISLD112=HELLO
E8=FIX.4.29=5535=034=549=ISLD52=00000000-00:00:0056=TW112=HELLO10=0iDISCONNECT

示例脚本中有两种命令类型,action命令和messages命令,action命令以小写字母开头,messages命令以大写字母开头。

(1)action命令

i<ACTION> :初始化action
e<ACTION> :预期action

支持的Action如下:

iCONNECT :初始化到FIX Acceptor的连接

eCONNECT:等待来自FIX Initiator的连接

iDISCONNECT:断开到FIX Acceptor的连接
eDISCONNECT:等待来自FIX Initiator的连接断开

(2)messages命令

I<MESSAGE>:初始化(发送)一条消息
E<MESSAGE>:等待一条消息

使用I命令时,不必增加Length(9)、CheckSum(10)字段,命令会自动增加相应的值到合适位置。开发者只有故意使这些字段不正确时才需要增加字段。

I命令也为字段提供了TIME宏,通过设置字段等于<TIME>,当前系统时间会被替换,也可以使用TIME的偏移,如52=<TIME-120>或52=<TIME+15>。

E命令验证是否收到正确消息。E命令会比较每个字段的值确保其是否正确。有些字段在运行时前无法确定地验证,例如SendingTime和CheckSum字段。这些字段可以添加到fields.fmt文件,文件中可以定义正则表达式至少验证字段的格式是否正确。

10=\d{3},,checksum必须是三个数字。
52=\d{8}-\d{2}:\d{2}:\d{2},发送时间必须是DDDDDDDD-DD:DD:DD格式。

I命令和E命令可以包含FILE宏,用于将文件内容传递给字段,如:

58=<FILE:test.txt>

交易系统开发(十二)——QuickFIX官方文档相关推荐

  1. [WebApp开发]基础教程-Google官方文档-第四篇

    文档内容 在Android Browser中使用控制台API 在WebView中使用控制台API 参考 调试 如果你是在为Android开发web应用,那么,你可以使用控制台(console)的Jav ...

  2. [VUE系列二]vue官方文档总结和整理

    一.选项/数据 1. data 类型:Object | Function 组件的定义只接受Function 因为组件可能被用来创建多个实例.如果data仍然是一个纯粹的对象,则所有的实例将共享引用同一 ...

  3. javaStruct - 通讯协议的解封包第三方库--(二)官方文档一

    原文链接 https://code.google.com/archive/p/javastruct/ 翻译用工具 翻译使用的是个人免费版Transmate 译文 javastruct 一个将java对 ...

  4. javaStruct - 通讯协议的解封包第三方库--(二)官方文档二

    原文链接 https://code.google.com/archive/p/javastruct/ 翻译用工具 翻译使用的是个人免费版Transmate 译文 简介 当与嵌入式设备和其他使用C语言样 ...

  5. javaStruct - 通讯协议的解封包第三方库--(二)官方文档三

    原文链接 https://code.google.com/archive/p/javastruct/wikis/example_photoshop_acb_file_reader_writer.wik ...

  6. 对于微信二维码相关官方文档的一些注解(微信登录和绑定微信、关注公众号)

    转载自:https://www.jianshu.com/p/d533c69be034 由于微信官方文档对此的描述虽然还可以,但是还是有一些让人疑惑的地方,所以笔者做了一些注解,希望对大家有所帮助 为什 ...

  7. 微信小程序开发之官方文档学习(一)

    小程序宿主环境 渲染层和逻辑层 小程序的运行环境分成渲染层和逻辑层:WXML 模板和 WXSS 样式工作在渲染层,JS 脚本工作在逻辑层.渲染层和数据相关:逻辑层负责产生.处理数据,通过 Page 实 ...

  8. HarmonyOS(一) 快速开始学习鸿蒙开发,官方文档学习路线解析

    系列文章目录 HarmonyOS(一):快速开始学习鸿蒙开发,官方文档学习路线解析 HarmonyOS(二):应用开发环境搭建准备 HarmonyOS(三):创建你的第一个HelloWorld应用 文 ...

  9. 【pytest官方文档】解读- 开发可pip安装的第三方插件

    在上一篇的 hooks 函数分享中,开发了一个本地插件示例,其实已经算是在编写插件了.今天继续跟着官方文档学习更多知识点. 一个插件包含一个或多个钩子函数,pytest 正是通过调用各种钩子组成的插件 ...

  10. OpenGL ES着色器语言之变量和数据类型(二)(官方文档第四章)

    OpenGL ES着色器语言之变量和数据类型(二)(官方文档第四章) 4.5精度和精度修饰符 4.5.1范围和精度 用于存储和展示浮点数.整数变量的范围和精度依赖于数值的源(varying,unifo ...

最新文章

  1. virtualbox虚拟机XP连接本地WinXP
  2. Linux_查看CPU信息、机器型号等硬件信息
  3. Kubernetes CRD开发工具Operator-SDK简介
  4. 编写你的第一个垃圾收集器
  5. boost::hana::all用法的测试程序
  6. 控制反转 java_控制反转( Ioc)快速入门
  7. 必背单词_初中英语:2000个必背单词(附音标)汇总,打印背完,3年考高分
  8. 对Session、Cookie的完全理解
  9. 标准c语言有几个关键字,C语言有多少个关键字
  10. css的img移上去边框效果及CSS透明度
  11. 基于RPC原理的Dubbo
  12. Python项目开发基础 -- with open (filename) as 读写文件
  13. 柯尔莫哥洛夫最后的问题
  14. Redis发布订阅[西橙先生]
  15. C语言将图片转化成Base64码
  16. Word如何让不同页显示不同的页码,页眉
  17. 大学十年__献给计算机专业的所有学子
  18. 白炽灯护眼还是LED护眼?盘点专业护眼的LED护眼灯
  19. 计算机打字题数字知识,电脑打字出现的是数字怎么办
  20. IDM下载慢 没有权限下载如何解决?

热门文章

  1. 测试内存条是否兼容软件,18款内存条兼容性测试
  2. 在阿里云建网站体验123
  3. 领域驱动架构(DDD)建模中的模型到底是什么?
  4. 梦想照进现实|CSDN 实体奖牌 第三期
  5. could not resolve xxx.jar
  6. Oracle 如何定义自动增量autocreament的主键ID?
  7. 编译原理NFA确定化
  8. 英语时态,看到过的最好讲解没有之一
  9. vue 手写签名_真正的艺术签名!让你的名字充满明星范!
  10. windows快捷键一览表