QuickFix Java 讲解(三)客户端的搭建与解析

本系列力求手把手教你怎样利用 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.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地址!

 

下节讲怎么搭建服务器端,下课。

 

 

 

 

 

 

 

  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值