詳解FIX協議的原理、訊息格式及配置開發

宜信技術學院發表於2019-07-11

一、定義

FIX協議是由國際FIX協會組織提供的一個開放式協議,目的是推動國際貿易電子化的程式,在各類參與者之間,包括投資經理、經紀人,買方、賣方建立起實時的電子化通訊協議。FIX協議的目標是把各類證券金融業務需求流程格式化,使之成為一個個可用計算機語言描述的功能流程,並在每個業務功能介面上統一交換格式,方便各個功能模組的連線。

二、協議工作原理

2.1 通訊模型及基本概念

通訊模型

  • Initiator :發起者,建立通訊連路,通過傳送初始Logon訊息發起會話的參與方。

  • Acceptor :接收方 FIX會話的接收方。負責執行第一層次的認證和通過傳輸Logon訊息的確認正式宣告連線請求被接受。

  • 原則:先發起者為Initiator ,接受者為Acceptor 。

  • 標準模式以閘道器為Acceptor,客戶端為Initiator做為常用模式。

Fix connection

FIX連線 由3部分組成:logon登入,message exchange訊息傳輸,logout登出。

  • logon登入 

  • logout登出 

Fix session

FIX會話由一個或多個FIX Connection FIX連線組成。一個FIX會話可以有多次登入。

序列號

  • 所有的FIX訊息都由一個唯一的序列號進行標示。序列號在每一個FIX會話開始時被初始化為1,並在整個會話期間遞增。監控序列號可以使會話參與者識別和處理丟失的訊息,當在一個FIX會話中重新連線時能夠快速進行應用程式同步。

  • 每個會話將建立一組互不依賴的接受和傳送序列。會話參與者將維護一個賦予傳送訊息的序列和一個監控接受訊息的訊息塊間隙序列號。

心跳

  • 在訊息互動期間,FIX應用程式將週期性產生Heartbeat心跳訊息。該心跳訊息可以監控通訊鏈路狀態及識別接收序列號間隙。傳送Heartbeat的週期間隔由會話發起者使用在Logon訊息中HeartBtInt域進行定義。

  • Heartbeat心跳訊息的時間間隔應當在每一個訊息傳送後復位,即傳送一個訊息後,在間隔給定的時間內無其它訊息傳送則傳送一個Heartbeat心跳訊息。HeartBtInt的值應當被會話雙方認同,由會話發起方定義並由會話接收者通過Logon訊息進行確認。同一個HeartBtInt被會話雙方——登入的發起者和登入的接受者共同使用。

資料完整校驗

  • 訊息資料內容的完整性可以參用兩種方式來驗證:訊息長度和效驗碼檢查。

  • 程式通過計算BodyLength域到CheckSum標記(“10=”)分界符的字元數,域BodyLength標示的訊息長度進行比較來完成完整性效驗。

  • ChekSum完整性檢查,通過計算從域“8=” 中“8”開始,包括緊跟在CheckSum標記域的分界符每個字元的2進位制和同CheckSum進行比較得到。

  • 一個FIX訊息校驗和通過計算到ChechSum域(但不包括)的訊息的每個位元組和得到。然後,校驗和被轉換為模256的數字用於傳送和比較。校驗和在所有加密操作之後被計算。

校驗程式碼:

樣例:8=FIX.4.29=7335=A34=149=CLIENT52=20181119-10:42:48.76856=SERVER98=0108=30141=Y10=208
1、訊息長度:9=73
35=A34=149=CLIENT52=20181119-10:42:48.76856=SERVER98=0108=30141=Y(這段長度)
2、效驗碼檢查
char *GenerateCheckSum( char *buf, long bufLen ) {
static char tmpBuf[ 4 ]; long idx;
unsigned int cks;
for( idx = 0L, cks = 0; idx < bufLen; cks += (unsigned int)buf[ idx++ ] ); sprintf( tmpBuf, “%03d”, (unsigned int)( cks % 256 ) );
return( tmpBuf );
}

 

訊息確認

  • FIX協議不支援單個訊息的確認。採用的是監控訊息時隙的方法來進行訊息恢復和驗證。

  • 普通的資料傳送(無單個訊息確認)通過訊息序列間隙進行錯誤識別。每個訊息由一個唯一的序列號進行標示。接收端應用程式負責監控接收訊息序列號以識別訊息間隙併產生重傳請求。

  • 每個FIX參與方必須為FIX會話維護兩個序列號,一個是接收序列號,一個是傳送序列號,兩者都在建立FIX會話開始時初始化為1。每個訊息被賦予一個唯一的序列號值,並在訊息傳送後遞增。此外,每個收到的訊息都有一個唯一的序列號,接收序列號計數器在收到每個訊息後將會被遞增。

  • 當接收序列號與所希望得到的的正確序列號不必配時,必須採取糾錯處理。

加密

  • 加密演算法由連線雙方共同協商。

  • 一個訊息的任何一個域可以被加密並放在SecureData域中。然而,一些顯示的標誌域必須採用明文進行傳輸。為確保完整性,明文域可以在SecureData域中重複。

  • 當使用加密時,建議但不是必須,所有的訊息體都進行加密。如果一個訊息中的重複組資料中的部分資料要加密,這個重複組必須全部進行加密。

  • 預先協商好的加密演算法在Logon訊息中進行宣告。

自定義域

  • FIX為給使用者提供最大的靈活性,FIX協議允許使用者自定義域。這些域在認同的參與者之間實現、應用,並且應注意避免衝突。

  • Tag數在5000 到9999保留用於使用者自定義域。這些tag值用於企業聯盟的資訊交換。可以通過FIX網站進行註冊。

  • 10000以上保留用於單一企業內部使用。不用註冊。

三、訊息格式

3.1 資料型別

整數int,浮點數float,單個字元char,布林Boolean,字串String,資料data 

3.2 域

常見域

Tag(標記)FieldName(域名)備註
8 BeginString 起始串,FIX協議版本
9 BodyLength 訊息長度
35 MsgType 訊息型別:例如F=Order Cancel Request,取消訂單
11 ClOrdID 客戶端訂單ID
37 OrderID 服務端訂單ID
41 OrigClOrdID 原始客戶端訂單ID
54 Side 買賣型別。例如:1 = Buy,2 = Sell
55 Symbol 股票程式碼。例如:YRD
10 CheckSum 校驗碼

域語法

  • 開始部分應是訊息頭,隨後是正文,最後是訊息尾;

  • 訊息頭的前 3 個域的次序不能改變:起始串(Tag =8)、訊息體長度(Tag =9)、訊息型別(Tag =35);

  • 訊息尾的最後一個域應是校驗和域(Tag=10);

  • 重複組中,域出現的順序應遵循該重複組在訊息或元件中定義時的次序;

  • 在一條訊息中,除重複組域外任何其他域不能重複出現。

安全與加密

  • 由於訊息有可能在公網或不安全的網路上傳輸交換,因此需要對相關的敏感資料加密處理。

  • 具體加密的方法由連線雙方達成的協議而定。

  • 訊息內除某些需要公開識別的域以明文傳輸外其他任何域都可以加密放置密文資料域 (SecureData)內。當然,這些被加密的域也可以同時保留明文的表示方式。

  • 當決定使用加密方案時,可以對訊息正文內所有的域加密。如果訊息的重複組內有部分需要加密的,那麼要求對整個重複組加密。

  • 本協議還提供的一些域用以支援數字簽名、金鑰交換和正文加密等安全技術。

3.3 訊息

訊息頭

每一個會話或應用訊息有一個訊息頭,該訊息頭指明訊息型別、訊息體長度、傳送目的地、訊息序號、傳送起始點和傳送時間。

Tag域名必需說明
8 BeginString Y 起始串,取值:FIX.4.2(不可加密,訊息的第一個域)
9 BodyLength Y 訊息體長度(不可加密,訊息的第二個域)
35 MsgType Y 訊息型別(不可加密,訊息的第三個域)
49 SenderCompID Y 傳送方程式碼(不可加密,傳送方識別符號)
59 TargetCompID Y 接收方程式碼(不可加密,接收方識別符號)
115 OnBehalfOfCompID N 最初傳送方識別符號(可加密),用於經第三方傳送。
128 DeliverToCompID N 最終接收方識別符號(可加密),用於經第三方傳送。
90 SecureDataLen N 密文資料長度
91 SecureData N 密文資料(緊跟密文資料長度域)
34 MsgSeqNum Y 訊息序號(可加密),如果交易雙方不採用 FIX 會話 機制,可將該 tag 置為一個固定的值,例如 0。
50 SenderSubID N 傳送方子識別符號(可加密)
142 SenderLocationID N 傳送方方位識別符號(可加密)
57 TargetSubID N 接收方子識別符號(可加密)
143 TargetLocationID N 接收方方位識別符號(可加密)
116 OnBehalfOfSubID N 最初傳送方子識別符號(可加密)
144 OnBehalfOfLocationID N 最初傳送方方位識別符號(可加密)
129 DeliverToSubID N 最終接收方子識別符號(可加密)
145 DeliverToLocationID N 最終接收方方位識別符號(可加密)
43 PossDupFlag N 可能重複標誌,重複傳送時,作此標記。(可加密)
97 PossResend N 可能重發標誌。(可加密)
52 SendingTime Y 傳送時間(可加密)
122 OrigSendingTime N 原始傳送時間(可加密)
347 MessageEncoding N 訊息中 Encoded 域的字元編碼型別(非 ASCII 碼)
369 LastMsgSeqNumProcesse d N 最後處理訊息序號(可加密)
370 OnBehalfOfSendingTime N 最初傳送時間(用 UTC 表示時間)

訊息尾

每一個訊息(會話或應用訊息)有一個訊息尾,並以此終止。訊息尾可用於分隔多個訊息,包含有 3 位數的校驗和值。

Tag域名必需說明
93 SignatureLength N 數字簽名長度(不可加密)
89 Signature N 數字簽名(不可加密)
10 CheckSum Y 校驗和,訊息的最末域。(不可加密)

新訂單訊息(MsgType=D)

對於在訊息頭中設定了 PossResend 標誌的訂單訊息,應當使用交易客戶方訂單編號(ClOrdID)核 實是否已收到該訂單,具體實現時還應檢查訂單引數(買賣方向、證券程式碼、數量等)進行核實。如果 之前收到該訂單,應以執行報告訊息迴應訂單狀態。如果之前未收到,則以執行報告訊息迴應訂單確認。

Tag域名必需說明
  標準訊息頭 Y MsgType=D
11 ClOrdID Y 交易客戶方訂單編號,在訂單有效交易日內必需
109 ClientID Y 客戶資金帳號
1 Account Y 客戶交易編碼
110 MinQty N 最小成交量。
55 Symbol Y 期貨合約程式碼
167 SecurityType N FUT = 期貨
200 MaturityMonthYear N 用於指定期貨到期的年和月
205 MaturityDay N 用於期貨的到期日期,並被與到期年月(MaturityMonthYear)聯合使用
207 SecurityExchange Y 用於指定交易所
77 OpenClose Y 指明開倉,平倉
8009 HedgeFlag Y 投機套保標誌
8010 TouchCondition N 觸發條件
54 Side Y 買賣方向
38 OrderQty N 委託手數
60 TransactTime Y 訂單發起時間
40 OrdType Y 訂單型別
44 Price N 價格(限價訂單時有效)
423 PriceType N 價格型別
99 StopPx N 停止價
15 Currency N 幣種
59 TimeInForce N 新訂單生效時間,預設為當日有效
168 EffectiveTime N 用於指定定單有效的時間
432 ExpireDate N 有條件地用於在生效時間(TimeInForce)=在某 日前有效(GTD),而沒有指定截止時間 (ExpireTime)的情況之下
126 ExpireTime N 有條件地用於生效時間(TimeInForce) = 在某 日前有效(GTD)和到期日沒有被指定的情況之 下
8096 MacNetInfo N 委託方的機器網路資訊
  標準訊息尾 Y  

執行報告訊息(MsgType=8)

  • 訂單確認

  • 訂單狀態變化確認(如撤單確認)

  • 傳送訂單的成交回報

  • 訂單拒絕

Tag域名必需說明
  標準訊息頭 Y MsgType=8
37 OrderID Y 期貨公司委託號,同個交易日必需保證唯一
11 ClOrdID N 交易客戶方訂單編號。如果是強平回報,則該值 取值為以”NONE”開頭的當天交易日唯一的字元 串標識
42 OrigClOrdID N 原始交易客戶方訂單編號,指示被撤消訂單的 ClOrdID
17 ExecID Y 期貨公司的執行編號,在訂單有效交易日內應保證唯一
150 ExecType Y 執行型別
39 OrdStatus Y 訂單狀態
103 OrdRejReason N 訂單拒絕時需要
109 ClientID Y 客戶資金帳號
1 Account Y 客戶交易編碼
55 Symbol Y 期貨合約程式碼
167 SecurityType N FUT=期貨
200 MaturityMonthYear N 到期年月
205 MaturityDay N 到期日期
207 SecurityExchange Y 用於指定交易所
77 OpenClose N 指明開倉,平倉
54 Side Y 買賣方向
38 OrderQty Y 委託手數
40 OrdType N 訂單型別
44 Price N 訂單價格
99 StopPx N 停止價
59 TimeInForce N 新訂單生效時間,預設為當日有效
15 Currency N 幣種
32 LastShares N 上一成交數(最近一筆成交數量)
31 LastPx N 上一成交價(最近一筆成交價格)
30 LastMkt N 上一成交市場
151 LeavesQty Y 訂單剩餘數量
14 CumQty Y 成交總數
6 AvgPx Y 成交平均價
60 TransactTime N 執行報告時間
381 GrossTradeAmt N 成交總金額
110 MinQty N 最小成交量
8500 OrderEntryTime N 訂單申報時間
8093 DeclarationID N 報單號
8094 TradeID N 撮合編號
  標準訊息尾 Y  

訂單狀態請求訊息(MsgType=H)

訂單狀態請求用於向交易服務方請求某訂單的狀態,交易服務方通過執行報告訊息返回訂單狀態。

Tag域名必需說明
  標準訊息頭 Y MsgType=H
37 OrderID Y 期貨公司委託號,同個交易日必需保證唯一
11 ClOrdID Y 交易客戶方訂單編號
109 ClientID Y 客戶資金帳號
1 Account Y 客戶交易編碼
55 Symbol Y 期貨合約程式碼
207 SecurityExchange Y 用於指定交易所
167 SecurityType N FUT=期貨
200 MaturityMonthYear N 用於指定期貨到期的年和月
205 MaturityDay N 用於期貨的到期日期,並被與到期年月(MaturityMonthYear)聯合使用
54 Side Y 買賣方向
  標準訊息尾 Y  

撤單訊息(MsgType=F)

撤單訊息用以撤消訂單的全部訂單剩餘數量。

撤單訊息也被賦予一個 ClOrdID,可視作另外一個訂單。如果被拒絕,撤單拒絕訊息的 ClOrdID 放 置撤單訊息的 ClOrdID,而原始訂單的 ClOrdID 則放入 OrigClOrdID 域。ClOrdID 要保證唯一。

Tag域名必需說明
  標準訊息頭 Y MsgType=F
41 OrigClOrdID Y 原始交易客戶方訂單編號,指示被撤消訂單的ClOrdID
37 OrderID Y 期貨公司委託號,同個交易日必需保證唯一
11 ClOrdID Y 交易客戶方訂單編號
109 ClientID Y 客戶資金帳號
1 Account Y 客戶交易編碼
55 Symbol Y 期貨合約程式碼
167 SecurityType N 證券程式碼源
200 MaturityMonthYear N FUT=期貨
205 MaturityDay N 期貨到期年月
207 SecurityExchange Y 期貨到期日期
54 Side Y 買賣方向
60 TransactTime Y 訂單發起時間
40 OrdType Y 訂單型別
38 OrderQty Y 委託手數
8093 DeclarationID N 報單號
58 Text N  
  標準訊息尾 Y  

撤單拒絕訊息(MsgType=9)

本訊息用於撤單訊息的拒絕。

交易服務方接收到撤單發現無法執行(已成交訂單不可更改等),將傳送撤單拒絕。

拒絕撤單時,撤單拒絕訊息應用 ClOrdID 指示撤單的 ClOrdID,用 OrigClOrdID 指示之前最後接受的訂單(除非拒絕原因是“未知訂單”)。

Tag域名必需說明
  標準訊息頭 Y MsgType=9
37 OrderID Y 期貨公司委託號,同個交易日必需保證唯一
11 ClOrdID Y 交易客戶方訂單編號
41 OrigClOrdID Y 原始交易客戶方訂單編號,指示被撤消訂單的ClOrdID
39 OrdStatus Y 訂單狀態
109 ClientID Y 客戶資金帳號
1 Account Y 客戶交易編碼
60 TransactTime N 訂單發起時間
434 CxlRejResponseTo N 撤單拒絕迴應型別
102 CxlRejReason N 撤單拒絕原因
58 Text N  
  標準訊息尾 Y  

四、FIX配置

4.1 會話配置(SESSION)

配置描述有效值預設
BeginString 會話使用的FIX版本號(傳送和接收訊息起始字串) FIXT.1.1、FIX.4.4、FIX.4.3、FIX.4.2、FIX.4.1、FIX.4.0  
SenderCompID 會話當中定義本方的ID 區分大小寫的字串  
SenderSubID 會話相關的本方的子ID號 (可選) 區分大小寫的字串  
SenderLocationID 會話相關的本方的locationID號 (可選) 區分大小寫的字串  
TargetCompID 本會話當中的對方ID 區分大小寫的字串  
TargetSubID 本會話當中的對方SubID (可選) 區分大小寫的字串  
TargetLocationID 本會話當中的對方locationID (可選) 區分大小寫的字串  
SessionQualifier 附加的限定詞,用於消除歧義,保證會話的唯一性 區分大小寫的字串  
DefaultApplVerID 僅FIXT1.1(或以上版本)需要。忽略早期版本的傳輸。指定會話的預設應用程式的版本ID。ApplVerID的列舉值(請看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  
ConnectionType 定義會話當中本方的角色:acceptor或者initiator initiator、acceptor  
StartTime 交易日的會話有效開始時間,這時FIX會話被啟用 UTC時間,格式: HH:MM:SS  
EndTime 交易日的會話失效時間,FIX會話將被停止 UTC時間,格式: HH:MM:SS  
StartDay 對於為期一週的會話配置,一週會話開始的第一天。與STARTTIME結合使用。 使用一週中某天的英語任何縮寫都是有效的(比如,mo, mon, mond, monda,Monday都是有效的)  
EndDay 對於為期一週的會話配置,一週會話結束的最後一天。與EndTime結合使用。 使用一週中某天的英語任何縮寫都是有效的(比如,mo, mon, mond, monda,Monday都是有效的)  
MillisecondsInTimeStamp 時間戳是否加入毫秒。FIX.4.2和更高版本可用。 Y、N Y
ResetOnLogon 接收登入請求時,序列號是否要復位。只用於Acceptor Y、N N
ResetOnLogout 正常登出登入時,序列號是否要復位 Y、N N
ResetOnDisconnect 連線異常斷開後是否要將序列號重置為1 Y、N N
RefreshOnLogon 確定是否應當從持久層登入時恢復會話狀態。在建立熱故障切換會話時有用。 Y、N N
EnableLastMsgSeqNumProcessed 是否在header中新增最後一條訊息的序列號(可選tag369)。 Y、N N
MaxMessagesInResendRequest 設定一次重發請求的訊息的最大訊息數。 任何大於0的整數。使用0為無窮大(預設)。 0
SendLogoutBeforeDisconnectFromTimeout 指定是否因超時斷開連線之前傳送logout訊息 Y、N N
IgnorePossDupResendRequests 當PossDupFlag(tag 43)設定為true時,是否忽略一次重發請求 Y、N N

4.2 驗證配置

配置描述有效值預設
UseDataDictionary 告訴會話是否使用資料字典,或不希望使用資料字典。 如果你要使用repeating group,你必須使用DataDictionary。 Y、N Y
DataDictionary 該配置只用於比FIXT.1.1還老的版本。詳細參考FIXT.1.1的TransportDataDictionary和AppDataDictionary的配置。 FIX44.xml、FIX43.xml、FIX42.xml、FIX41.xml、FIX40.xml  
TransportDataDictionary XML定義檔案用於驗證傳入的管理訊息。如果沒有提供DataDictionary,只會做基本訊息的驗證。該配置只用於FIXT.1.1(或更高版本)的會話。 FIXT1.1.xml  
AppDataDictionary 用於驗證應用層訊息的XML定義檔案。僅對FIXT.1.1(或更高版本)的會話有效。更多資訊請參考(FIX.4.0到 FIX.4.4)的DataDictionary。該配置可以為每個會話指定一個自定義應用的資料字典。該配置僅用於FIXT.1.1或更新的傳輸協議。使用FIXT傳輸時,該配置可以作為指定多個應用的資料字典的字首。例如: 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 有效的XML資料字典檔案。QuickFIX/N 配備預設的協議字典資料:FIX50SP2.xml、FIX50SP1.xml、FIX50.xml、FIX44.xml、FIX43.xml、FIX42.xml、FIX41.xml、FIX40.xml  
ValidateFieldsOutOfOrder 如果設定為N,欄位放置區域錯誤(例如,body欄位在header區域內,或在header欄位在body區域內)將不會被拒絕。用於連線欄位要求不嚴格的系統。 Y、N Y
ValidateFieldsHaveValues 如果設定為N,沒有值的欄位將不會被拒絕。用於連線到系統不當傳送空標籤。 Y、N Y
ValidateUserDefinedFields 如果設定為N,使用者自定義的欄位將不會被拒絕,即使沒有在資料字典中定義,或沒出現在訊息中。 Y、N Y

4.3 Initiator

配置描述有效值預設
ReconnectInterval 嘗試重新連線的時間間隔(秒)。僅用於 initiator。 正整數 30
HeartBtInt 心跳間隔(秒)。僅用於initiator。 正整數 -
LogonTimeout 登入超時時間間隔(秒) 正整數 10
LogoutTimeout 登出登入超時時間間隔(秒) 正整數 2
SocketConnectPort Socket服務埠,用於建立會話。僅用於 initiator 正整數 -
SocketConnectHost 連線主機.僅用於 initiator x.x.x.x格式IP地址或域名 -
SocketConnectPort 一組備用Socket埠,用於連線會話的故障轉移,n是正整數。SocketConnectPort1,SocketConnectPort2 ... 必須是連續的,並有一個與之相匹配的陣列SocketConnectHost 正整數 -
SocketConnectHost 一組備用Socket服務主機,用於連線會話的故障轉移,n是正整數。SocketConnectHost1, SocketConnectHost2... 必須是連續的,並有一個與之相匹配的陣列SocketConnectPort x.x.x.x格式IP地址或域名 -
SocketNodelay 連線是否禁用Nagle演算法。在[DEFAULT]配置節點定義。 Y、N Y
ReconnectInterval 嘗試重新連線的時間間隔(秒)。僅用於 initiator。 正整數 30

4.4 Acceptor

配置描述有效值預設
SocketAcceptPort 監聽接入連線Socket埠。僅用於acceptor 正整數,有效的、開放的套接字埠 -
SocketAcceptHost 監聽接入連線的Socket服務的主機。如果不提供,acceptor將監聽所有網路埠(0.0.0.0) 有效的x.x.x.x格式IP地址 0.0.0.0
SocketNodelay 連線是否禁用Nagle演算法。在[DEFAULT]配置節點定義。 Y、N Y

4.5 Storage

配置描述有效值預設
PersistMessages 如果設定為N,被不會儲存訊息。這樣將迫使quickfix總是傳送GapFills,而不是重新傳送訊息。如果你知道你永遠不需要重新傳送訊息,使用此配置。有用的市場資料流。 Y、N Y

4.6 File Storage

配置描述有效值預設
FileStorePath 儲存序列號和訊息的檔案目錄。 有效的檔案儲存目錄,必須有寫入許可權。 -

4.7 Logging

配置描述有效值預設
FileLogPath 儲存日誌的目錄。 有效的檔案儲存目錄,必須有寫入許可權。 -

五、FIX開發

5.1 FIX引擎

  • 官網:FIX引擎(http://www.quickfixengine.org/)

github:QFJ GitHub Repository(https://github.com/quickfix-j/quickfixj)

5.2 DEMO

Acceptor 配置檔案

# 定義會話的預設配置(default節點)
[DEFAULT]
FileStorePath=store
FileLogPath=log
ConnectionType=acceptor
ReconnectInterval=60
SenderCompID=SERVER
ResetOnDisconnect=Y
ResetOnLogout=Y
ResetOnLogon=Y

[SESSION]
BeginString=FIX.4.2
TargetCompID=CLIENT
StartTime=00:00:00
EndTime=23:59:59
HeartBtInt=30
SocketAcceptHost=127.0.0.1
SocketAcceptPort=6666
DataDictionary=FIX42.xml

 

Initiator 配置檔案

[DEFAULT]
ConnectionType=initiator
ReconnectInterval=60
FileLogPath=log
FileStorePath=store
StartTime=00:00:00
EndTime=23:59:59
HeartBtInt=30
ResetOnDisconnect=Y
ResetOnLogout=Y
ResetOnLogon=Y

[SESSION]
BeginString=FIX.4.2
SenderCompID=CLIENT
TargetCompID=SERVER
SocketConnectPort=6666
SocketConnectHost=127.0.0.1
DataDictionary=FIX42.xml

 

FixServer

package com.app.fix;

import quickfix.*;

/**
 * 服務啟動主類(執行緒)
 */
public class FixServer {
    private static ThreadedSocketAcceptor acceptor = null;

    /**
     * 指定配置檔案啟動
     *
     * @param propFile
     * @throws ConfigError
     * @throws FieldConvertError
     */
    public FixServer(String propFile) throws ConfigError, FieldConvertError {
        // 設定配置檔案
        SessionSettings settings = new SessionSettings(propFile);

        // 設定一個APPlication
        Application application = new FixServerApplication();

        /**
         *
         * quickfix.MessageStore 有2種實現。 quickfix.JdbcStore,quickfix.FileStore .
         * JdbcStoreFactory 負責建立JdbcStore , FileStoreFactory 負責建立FileStorequickfix
         * 預設用檔案儲存,因為檔案儲存效率高。
         */
        MessageStoreFactory storeFactory = new FileStoreFactory(settings);

        LogFactory logFactory = new FileLogFactory(settings);

        MessageFactory messageFactory = new DefaultMessageFactory();

        acceptor = new ThreadedSocketAcceptor(application, storeFactory, settings, logFactory, messageFactory);

    }

    private void startServer() throws RuntimeError, ConfigError {
        acceptor.start();
    }

    /**
     * 測試本地使用的main方法
     *
     * @param args
     * @throws FieldConvertError
     * @throws ConfigError
     */
    public static void main(String[] args) throws ConfigError, FieldConvertError {
        FixServer fixServer = new FixServer("res/acceptor.config");
        fixServer.startServer();
    }

}

 

FixServerApplication

package com.app.fix;

import quickfix.Application;
import quickfix.DoNotSend;
import quickfix.FieldNotFound;
import quickfix.IncorrectDataFormat;
import quickfix.IncorrectTagValue;
import quickfix.Message;
import quickfix.MessageCracker;
import quickfix.RejectLogon;
import quickfix.Session;
import quickfix.SessionID;
import quickfix.UnsupportedMessageType;
import quickfix.field.MsgType;

/**
 * 
 */
public class FixServerApplication extends MessageCracker implements Application {
    @Override
    protected void onMessage(Message message, SessionID sessionID) {
        try {
            String msgType = message.getHeader().getString(35);
            Session session = Session.lookupSession(sessionID);
            switch (msgType) {
                case MsgType.LOGON: // 登陸
                    session.logon();
                    session.sentLogon();
                    break;
                case MsgType.HEARTBEAT: // 心跳
                    session.generateHeartbeat();
                    break;
            }

        } catch (FieldNotFound e) {
            e.printStackTrace();
        }

    }

    @Override
    public void onCreate(SessionID sessionId) {
        System.out.println(" 伺服器啟動時候呼叫此方法建立");

    }

    @Override
    public void onLogon(SessionID sessionId) {
        System.out.println("客戶端登陸成功時候呼叫此方法");

    }

    @Override
    public void onLogout(SessionID sessionId) {
        System.out.println("客戶端斷開連線時候呼叫此方法");

    }

    @Override
    public void toAdmin(Message message, SessionID sessionId) {
        System.out.println("傳送會話訊息時候呼叫此方法");

    }

    @Override
    public void toApp(Message message, SessionID sessionId) throws DoNotSend {
        System.out.println("傳送業務訊息時候呼叫此方法");

    }

    @Override
    public void fromAdmin(Message message, SessionID sessionId)
            throws FieldNotFound, IncorrectDataFormat, IncorrectTagValue, RejectLogon {
        System.out.println("接收會話型別訊息時呼叫此方法");
        try {
            crack(message, sessionId);
        } catch (UnsupportedMessageType | FieldNotFound | IncorrectTagValue e) {
            e.printStackTrace();
        }

    }

    @Override
    public void fromApp(Message message, SessionID sessionId)
            throws FieldNotFound, IncorrectDataFormat, IncorrectTagValue, UnsupportedMessageType {
        System.out.println("接收業務訊息時呼叫此方法");
        crack(message, sessionId);

    }

}

 

FixClient

package com.app.fix;

import quickfix.*;
import quickfix.field.*;
import quickfix.fix42.NewOrderSingle;

import java.io.FileNotFoundException;
import java.util.Date;

public class FixClient implements Application {

    private static volatile SessionID sessionID;

    @Override
    public void onCreate(SessionID sessionID) {
        System.out.println("OnCreate");
    }

    @Override
    public void onLogon(SessionID sessionID) {
        System.out.println("OnLogon");
        FixClient.sessionID = sessionID;
    }

    @Override
    public void onLogout(SessionID sessionID) {
        System.out.println("OnLogout");
        FixClient.sessionID = null;
    }

    @Override
    public void toAdmin(Message message, SessionID sessionID) {
        System.out.println("ToAdmin");
    }

    @Override
    public void fromAdmin(Message message, SessionID sessionID) throws FieldNotFound, IncorrectDataFormat, IncorrectTagValue, RejectLogon {
        System.out.println("FromAdmin");
    }

    @Override
    public void toApp(Message message, SessionID sessionID) throws DoNotSend {
        System.out.println("ToApp: " + message);
    }

    @Override
    public void fromApp(Message message, SessionID sessionID) throws FieldNotFound, IncorrectDataFormat, IncorrectTagValue, UnsupportedMessageType {
        System.out.println("FromApp");
    }

    public static void main(String[] args) throws ConfigError, FileNotFoundException, InterruptedException, SessionNotFound {
        SessionSettings settings = new SessionSettings("res/initiator.config");

        Application application = new FixClient();
        MessageStoreFactory messageStoreFactory = new FileStoreFactory(settings);
        LogFactory logFactory = new ScreenLogFactory(true, true, true);
        MessageFactory messageFactory = new DefaultMessageFactory();

        Initiator initiator = new SocketInitiator(application, messageStoreFactory, settings, logFactory, messageFactory);
        initiator.start();

        while (sessionID == null) {
            Thread.sleep(1000);
        }

        final String orderId = "342";
        NewOrderSingle newOrder = new NewOrderSingle(new ClOrdID(orderId), new HandlInst('1'), new Symbol("YRD"),
                new Side(Side.BUY), new TransactTime(new Date()), new OrdType(OrdType.MARKET));
        Session.sendToTarget(newOrder, sessionID);
        Thread.sleep(5000);
    }
}

 

作者:姜永念

來源:來源:宜信技術學院

相關文章