本系列力求手把手教你怎样利用 QuickFix Java 搭建自己的 FIX 协议收法平台,以及其中的注意事项。
所有源码的地址(免费):
https://github.com/zongzhec/QuickFixPractise
这次我们讨论怎样搭建Initiator端。
4. Initiator 端的搭建
Initiator,也可以称作为 Client,就是分散在各个地方的交易机。业务员在上面操作以后,客户端会向服务器发送请求。请求多种多样,基本常用的有:行情请求(35=V),新建订单(35=D),撤销订单(35=F)。
搭建一个客户端非常的简单,只需要两个类:一个类负责初始化、启动和关停服务(起个名字叫FixInitiator);另一个类负责服务,即收发消息(起个名字叫FixInitiatorApplication)。
结构框架如下,详细的在这里:https://github.com/zongzhec/QuickFixPractise/tree/master/FixInitiator
4.1. Property设置
Property在上一节已经做了大致讲解,在这里先贴上源码:
#quickfix-server.properties
[default]
# 这些字段记得改成你的设置
FileStorePath=fileStore
SocketConnectHost=10.176.125.79
SocketConnectPort=10003
TargetCompID=QUICKFIX_ACCEPTOR# 以下字段可以不改
ConnectionType=initiator
HeartBtInt=30
ReconnectInterval=10
FileLogPath=log
UseDataDictionary=N
DataDictionary=src/main/resources/FIX44.modified.xml
ContinueInitializationOnError=Y
BeginString=FIX.4.4
StartTime=00:00:00
EndTime=23:00:00
ResetOnLogon=Y
ResetSeqNumFlag=Y
MaxMessagesInResendRequest=1[session]
SenderCompID=QUICKFIX_INITIATOR1[session]
SenderCompID=QUICKFIX_INITIATOR2
对于Initiator 来说,一定要写ConnectionType=initiator,否则在启动的时候会报错,因为对应的session setting 没有找到。
4.2. FixInitiator
这个类也是Initiator程序的主入口。除了标准化的startServer 和 stopServer,关键部分就是对SocketInitiator 的声明和初始化,初始化需要上面提到的property文件,如果没有的话会报错。
另外需要注意的是在quickfix 包 和quickfix.fix44 里有一些名字相同的包,不要导错了。有时候他们是extend 关系,编译不会报错,但是运行会突然报错。
package foo.zongzhe.quickfix.initiator;import quickfix.*;public class FixInitiator {private static SocketInitiator initiator;private static SessionSettings settings;private static FixInitiatorApplication application;public static SocketInitiator getInitiator() {return initiator;}public FixInitiator() {try {settings = new SessionSettings("src/main/resources/quickfix.properties");} catch (ConfigError configError) {System.out.println("Warning: config error!" + configError);}application = new FixInitiatorApplication();MessageStoreFactory storeFactory = new FileStoreFactory(settings);LogFactory logFactory = new FileLogFactory(settings);MessageFactory messageFactory = new DefaultMessageFactory(); // 不是quickfix.fix44.MessageFactorytry {initiator = new SocketInitiator(application, storeFactory, settings, logFactory, messageFactory);} catch (ConfigError configError) {System.out.println("Warning: config error! " + configError);}}private void startServer() {try {initiator.start();} catch (ConfigError configError) {configError.printStackTrace();}}private void stopServer() {initiator.stop();}public static void main(String[] args) {FixInitiator fixInitiator = new FixInitiator();fixInitiator.startServer();// 启动一个Session,记得参考你的quickfix.properties设定SessionID sessionID = new SessionID("FIX.4.4", "QUICKFIX_INITIATOR1", "QUICKFIX_ACCEPTOR");// 开始发点消息try {application.sendMarketDataRequest(sessionID);Thread.sleep(5000);application.sendNewOrderRequest(sessionID);Thread.sleep(5000);} catch (SessionNotFound | InterruptedException exception) {exception.printStackTrace();}}}
4.3. FixInitiatorApplication
这里就是用来收发消息、解析消息的具体服务类。一方面要extends MessageCracker, 用来解析消息。另一方面要 implements Application,用来收法消息。
一旦implements Application,就要实现固定的几个功能,里面可以什么也不写,打印输出,有个概念即可。
在extends MessageCracker后,可以重写onMessage方法,针对不同的消息做不同的本地化处理。
详见以下源码:
package foo.zongzhe.quickfix.initiator;import quickfix.*;
import quickfix.field.*;
import quickfix.fix44.ExecutionReport;
import quickfix.fix44.MarketDataRequest;
import quickfix.fix44.MessageCracker;
import quickfix.fix44.NewOrderSingle;import java.time.LocalDateTime;public class FixInitiatorApplication extends MessageCracker implements Application {// 以下是Application的固定七件套@Overridepublic void onCreate(SessionID sessionId) {System.out.println("onCreate is called");}@Overridepublic void onLogon(SessionID sessionId) {System.out.println("onLogon is called");}@Overridepublic void onLogout(SessionID sessionId) {System.out.println("onLogout is called");}@Overridepublic void toAdmin(Message message, SessionID sessionId) {System.out.println("toAdmin is called");}@Overridepublic void fromAdmin(Message message, SessionID sessionId) throws FieldNotFound, IncorrectDataFormat, IncorrectTagValue, RejectLogon {System.out.println("fromAdmin is called");}@Overridepublic void toApp(Message message, SessionID sessionId) throws DoNotSend {System.out.println("toApp is called: " + message);}@Overridepublic void fromApp(Message message, SessionID sessionId) throws FieldNotFound, IncorrectDataFormat, IncorrectTagValue, UnsupportedMessageType {System.out.println("fromApp is called");}// 以下是你可以自定义的消息接收器,来自MessageCracker@Overridepublic void onMessage(ExecutionReport message, SessionID sessionID) throws FieldNotFound, UnsupportedMessageType, IncorrectTagValue {System.out.println("Received ExecutionReport: " + message + ", sessionID: " + sessionID);// 收都收了,解析一下System.out.println(String.format("clOrderID: %s, symbol: %s, side: %s",message.getClOrdID().getValue(),message.getSymbol().getValue(),message.getSide().getValue()));}// 以下是发消息的功能/*** 订阅行情消息** @param sessionID* @throws SessionNotFound*/public void sendMarketDataRequest(SessionID sessionID) throws SessionNotFound {// 具体set哪些字段,参考你的FIX44.modified.xmlMarketDataRequest req = new MarketDataRequest();req.set(new MDReqID("mockedMDReqID"));req.set(new SubscriptionRequestType('1'));// 重复组的设置MarketDataRequest.NoRelatedSym symGroup1 = new MarketDataRequest.NoRelatedSym();symGroup1.set(new Symbol("mockedSymbol1"));req.addGroup(symGroup1);MarketDataRequest.NoRelatedSym symGroup2 = new MarketDataRequest.NoRelatedSym();symGroup2.set(new Symbol("mockedSymbol2"));req.addGroup(symGroup2);System.out.println("Sending MarketDataRequest");Session.sendToTarget(req, sessionID);}/*** 下单* @param sessionID* @throws SessionNotFound*/public void sendNewOrderRequest(SessionID sessionID) throws SessionNotFound {NewOrderSingle order = new NewOrderSingle();LocalDateTime date = LocalDateTime.now();order.set(new ClOrdID("mockedClOrdID"));order.set(new Account("mockedAccount"));order.set(new HandlInst('1'));order.set(new OrderQty(45.00));order.set(new Price(25.88));order.set(new Symbol("mockedSymbol"));order.set(new Side(Side.BUY)); // 对于枚举型对象也可以这么设置order.set(new OrdType(OrdType.LIMIT));Session.sendToTarget(order, sessionID);}
}
4.4. 运行及结果
运行以后,如果看到“onCreate is called”的字样,就说明已经通了,准备开始给服务器发消息了。
而“toApp is called” 标明你已经向服务器发送了消息,正在等回应。(注意上节文章说的要修改的配置,比如IP地址!)
下节讲怎么搭建服务器端,下课。