交易系統開發(十二)——QuickFIX官方文檔

交易系統開發(十二)——QuickFIX官方文檔

http://www.quickfixengine.org/quickfix/doc/html/?quickfix/doc/htmlhtml

1、QuickFIX編譯構建

一、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的搜索路徑內。python

二、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 installmysql

2、QuickFIX數據庫支持

一、MySQL

運行src/sql/mysql目錄中的建立腳本,須要傳遞建立數據庫的受權用戶,必須安裝MySQL數據庫。正則表達式

create.sh mysql
create.bat mysql

二、MSSQL

必須安裝MSSQL,運行src/sql/mssql目錄的建立腳本,必須傳遞建立MSSQL數據庫的用戶。
create.bat sasql

三、PostgreSQL

必須安裝PostgreSQL,運行src/sql/postgresql腳本,必須傳遞建立PostgreSQL數據庫的用戶。數據庫

create.sh postgres
create.bat postgres

3、測試

一、測試

QuickFIX開發由功能測試和單元測試組件驅動。windows

二、Windows

(1)單元測試
test目錄下執行:
runut release [port]
端口用於測試socket功能。
(2)驗證測試
在test目錄執行:
runat release [port]
runat_threaded release [port]
端口用於Socket Server監聽鏈接。安全

三、Linux

Linux、Solaris、FreeBSD、Mac OS X中單元測試和功能測試相同。
(1)單元測試
test目錄執行:
runut.sh [port]
端口用於測試Socket功能,若是QuickFIX要支持數據庫,須要更新數據庫設置cfg/ut.cfg。
(2)驗證測試
在test目錄執行:ruby

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

端口用於監聽Socket服務器的鏈接。服務器

4、工程設置

一、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目錄。

二、Linux

(1)使用-fexceptions選項開啓異常。
(2)推薦使用-finline-functions選項優化。
(3)QuickFIX必須使用-lquickfix選項進行連接。若是QuickFIX使用pthreads、libxml,則使用-lpthread選項、-lxml2選項、-lz選項連接。
(4)Solaris系統必須使用-lnsl選項和-lsocket選項連接。

5、應用建立

一、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異常。

二、示例

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。

6、QuickFix配置

一、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

二、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

三、Session配置

BeginString:Session使用的Fix版本,可選值爲FIXT.1.一、FIX.4.四、FIX.4.三、FIX.4.二、FIX.4.一、FIX.4.0。
SenderCompID:關聯Fix Session的本地ID,包含字母和數字大小寫敏感的字符串。
TargetCompID:Fix Session的對等方ID,包含字母和數字大小寫敏感的字符串。
SessionQualifier:Session標識,用於區分其它Session,包含字母和數字大小寫敏感的字符串。
DefaultApplVerID:只支持FIXT 1.1及後續版本,早期版本會忽略,用於指定Session的默認應用版本ID,可選值能夠是ApplVerID枚舉值或BeginString值,包括FIX.5.0SP二、FIX.5.0SP一、FIX.5.0、FIX.4.四、FIX.4.三、FIX.4.二、FIX.4.一、FIX.4.0、九、八、七、六、五、四、三、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。

四、認證配置

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秒。

五、Initiator配置

ReconnectInterval:重鏈接時間間隔,只用於Initiator,正數,默認只爲30。
HeartBtInt:心跳時間間隔,只用於Initiator,正數。
LogonTimeout:登陸超時時間,正數,默認爲10。
LogoutTimeout:登出超時時間,正數,默認爲2。
SocketConnectPort:Session鏈接端口,只用於SocketInitiator。
SocketConnectHost:Session鏈接主機,只用於SocketInitiator,能夠是IP地址或域名。
SocketConnectPort&lt;n&gt;:對於熱切換Session的備份鏈接端口。
SocketConnectHost&lt;n&gt;:對於熱切換Session的備份鏈接主機。
SocketNodelay:指定使用TCP_NODELAY建立Socket,目前只能定義在[DEFAULT]分區,可選值爲Y、N,默認值爲N。
SocketSendBufferSize:指定使用SO_SNDBUF建立Socket,目前只能定義在[DEFAULT]分區,正數,默認值爲0。
SocketReceiveBufferSize:指定使用SO_RCVBUF建立Socket,目前只能定義在[DEFAULT]分區,正數,默認值爲0。

六、Acceptor

SocketAcceptPort:監聽端口,只用於SocketAcceptor,目前只能定義在[DEFAULT]分區。
SocketReuseAddress:指定SO_REUSADDR使用建立Socket,只用於SocketAcceptor,可選值爲Y、N,默認值爲Y。
SocketNodelay:指定使用TCP_NODELAY建立Socket,目前只能定義在[DEFAULT]分區,可選值爲Y、N,默認值爲N。

七、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);

八、日誌配置

FileLogPath:日誌存儲路徑,必須有寫權限。
FileLogBackupPath:日誌備份路徑,必須有寫權限。
ScreenLogShowIncoming:打印Incoming消息到標準輸出,可選值爲Y、N,默認值爲Y。
ScreenLogShowOutgoing:打印Outgoing消息到標準輸出,可選值爲Y、N,默認值爲Y。
ScreenLogShowEvents:打印Events消息到標準輸出,可選值爲Y、N,默認值爲Y。
日誌也能夠存儲到數據庫中,支持MySQL、PostgreSQL、ODBC數據庫。

九、SSL配置

SSLProtocol:用於控制應用程序在創建其環境時應使用的SSL協議,可選值爲SSLv二、SSLv三、TLSv一、TLSv1_一、TLSv1_二、all,默認爲all -SSLv2。

7、消息驗證

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>

驗證器不會有條件地拒絕必填字段,由於其規則沒有明肯定義。使用非預設的有條件必填字段將致使消息被拒絕。

8、接收消息

開發者感興趣的大部分消息在應用程序重載的fromApp函數內收到,全部消息都有消息頭和消息尾,消息頭和消息尾分別使用 getHeader和getTrailer函數進行訪問。

一、類型安全消息和字段

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 created
    virtual void onCreate( const SessionID& )
    {

    }
    /// Notification of a session successfully logging on
    virtual void onLogon( const SessionID& )
    {

    }
    /// Notification of a session logging off or disconnecting
    virtual void onLogout( const SessionID& )
    {

    }
    /// Notification of admin message being sent to target
    virtual void toAdmin( Message&, const SessionID& )
    {

    }
    /// Notification of app message being sent to target
    void 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 target
    virtual void fromAdmin( const Message&, const SessionID& )
    throw ( FIX::FieldNotFound, FIX::IncorrectDataFormat, FIX::IncorrectTagValue, FIX::RejectLogon )
    {

    }
    /// Notification of app message being received from target
    virtual 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 OrderCancelRequest
        FIX::Price price;
        message.get(price);
    }
};

經過繼承MessageCracker,可使用crack函數並能夠重載不一樣的FIX協議的消息函數,未重載的函數默認拋出UnsupportedMessageType異常。

二、類型安全字段

使用getField函數從消息中獲取字段。

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

  // another field...
  FIX::ClOrdID clOrdID;
  message.getField(clOrdID);
}

三、非類型安全

使用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 ID
  std::string value;
  value = message.getField(44);

  // retrieve value into a field base with integer field ID
  FIX::FieldBase field(44, "");
  message.getField(field);

  // retreive value with an enumeration, a little better
  message.getField(FIX::FIELD::Price);
}

9、發送消息

一、發送消息

使用靜態函數方法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&);

二、類型安全消息和字段

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"));
}

三、類型安全字段

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);
}

四、非類型安全

可使用setField方法傳遞消息原語。

void sendOrderCancelRequest()
{
  FIX::Message message;
  // BeginString
  message.getHeader().setField(8, "FIX.4.2");
  // SenderCompID
  message.getHeader().setField(49, "TW");
  // TargetCompID, with enumeration
  message.getHeader().setField(FIX::FIELD::TargetCompID, "TARGET");
  // MsgType
  message.getHeader().setField(35, 'F');
  // OrigClOrdID
  message.setField(41, "123");
  // ClOrdID
  message.setField(11, "321");
  // Symbol
  message.setField(55, "LNUX");
  // Side, with value enumeration
  message.setField(54, FIX::Side_BUY);
  // Text
  message.setField(58, "Cancel My Order!");

  FIX::Session::sendToTarget(message);
}

10、循環組

QuickFIX可以發送包含循環組甚至遞歸循環組的消息。全部循環組都以一個字段開頭,用於指示一個集合中有多少個循環組,能夠經過引用在父消息或父組中以該字段命名的類來建立組。

一、使用循環組建立消息

建立消息時,聲明循環組的數量的必填字段設置爲零。當添加組時,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);

二、使用循環組讀取消息

要從消息中拉出組,須要提供要拉出的組對象。應該首先檢查實體字段的數量以得到組的總數。

// 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);

11、用戶自定義字段

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 )

12、測試

一、單元測試

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 );
}

二、驗證測試

QuickFIX由一個腳本化的測試運行器,其附帶一系列自動化驗收測試。QuickFIX附帶的基本測試基於FIX協議組織提供的《FIX會話層測試用例和預期行爲》。QuickFIX的測試會驗證QuickFIX是否符合FIX協議規範。QuickFIX測試的自動化特性保證了QuickFIX的將來版本不會破壞任何當前功能。
也許更重要的是如何使用測試驅動QuickFIX的開發。在編寫任何一行支持FIX協議的代碼前,必須先編寫測試用例。
這種測試優先的方法爲開發人員創建了一個目標,開發人員將客觀地驗證其是否正確地實現了FIX標準。
下列測試腳本的示例測試在接收到小於預期MsgSeqNum的NewSeqNo值時FIX引擎的行爲。

iCONNECT
I8=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=0

I8=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=0

I8=FIX.4.235=134=349=TW52=>TIME>56=ISLD112=HELLO
E8=FIX.4.29=5535=034=549=ISLD52=00000000-00:00:0056=TW112=HELLO10=0
iDISCONNECT

示例腳本中有兩種命令類型,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=&lt;FILE:test.txt&gt;

相關文章
相關標籤/搜索