本系列力求手把手教你怎样利用 QuickFix Java 搭建自己的 FIX 协议收法平台,以及其中的注意事项。
所有源码的地址(免费):
这次我们讨论怎样搭建Initiator端。
4. Initiator 端的搭建
Initiator,也可以称作为 Client,就是分散在各个地方的交易机。业务员在上面操作以后,客户端会向服务器发送请求。请求多种多样,基本常用的有:行情请求(35=V),新建订单(35=D),撤销订单(35=F)。
搭建一个客户端非常的简单,只需要两个类:一个类负责初始化、启动和关停服务(起个名字叫FixInitiator);另一个类负责服务,即收发消息(起个名字叫FixInitiatorApplication)。
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.MessageFactory try {
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的固定七件套 @Override
public void onCreate(SessionID sessionId) {
System.out.println("onCreate is called");
}
@Override
public void onLogon(SessionID sessionId) {
System.out.println("onLogon is called");
}
@Override
public void onLogout(SessionID sessionId) {
System.out.println("onLogout is called");
}
@Override
public void toAdmin(Message message, SessionID sessionId) {
System.out.println("toAdmin is called");
}
@Override
public void fromAdmin(Message message, SessionID sessionId) throws FieldNotFound, IncorrectDataFormat, IncorrectTagValue, RejectLogon {
System.out.println("fromAdmin is called");
}
@Override
public void toApp(Message message, SessionID sessionId) throws DoNotSend {
System.out.println("toApp is called: " + message);
}
@Override
public void fromApp(Message message, SessionID sessionId) throws FieldNotFound, IncorrectDataFormat, IncorrectTagValue, UnsupportedMessageType {
System.out.println("fromApp is called");
}
// 以下是你可以自定义的消息接收器,来自MessageCracker @Override
public 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.xml MarketDataRequest 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地址!)
下节讲怎么搭建服务器端,下课。