FIX三天日记-quick fix简介

一、介绍

fix协议在上篇已经学习了,不再介绍。

QuickFIX是一款C++实现的开源FIX引擎,同时提供Python等多种语言实现,具体看quickfix git地址

官网已经介绍如何编译quickfix、配置文件字段含义等等,我假设你可以看懂,用的时候查阅即可,我就不复制过来了,本文是教你快速认识此框架并且用起来。

二、主要类介绍

想了解如何用某个组件,先了解他的成员都有哪些。

2.1 Application

若是须要使用QuickFIX开发FIX应用,则须要实现FIX::Application接口,并重载不一样FIX协议版本的MessageCracker::OnMessage接口,如FIX42::MessageCracker。

class Application
{
public:virtual ~Application() {};/// Notification of a session begin createdvirtual void onCreate( const SessionID& ) = 0;/// Notification of a session successfully logging onvirtual void onLogon( const SessionID& ) = 0;/// Notification of a session logging off or disconnectingvirtual void onLogout( const SessionID& ) = 0;/// Notification of admin message being sent to targetvirtual void toAdmin( Message&, const SessionID& ) = 0;/// Notification of app message being sent to targetvirtual void toApp( Message&, const SessionID& )EXCEPT ( DoNotSend ) = 0;/// Notification of admin message being received from targetvirtual void fromAdmin( const Message&, const SessionID& )EXCEPT ( FieldNotFound, IncorrectDataFormat, IncorrectTagValue, RejectLogon ) = 0;/// Notification of app message being received from targetvirtual void fromApp( const Message&, const SessionID& )EXCEPT ( FieldNotFound, IncorrectDataFormat, IncorrectTagValue, UnsupportedMessageType ) = 0;
};

 onCreate:当Fix Session创建时调用。
onLogon:当Fix Session登陆成功时调用。
onLogout:当Fix Session退出时调用。
fromAdmin:当收到一个Admin类型消息时调用。
fromApp:当收到一个不属于Admin 类型消息时调用。
toAdmin:当发送一个admin类型消息调用。
toApp:当发送一个非admin(业务类型)消息调用。

admin一般是服务提供方,app是客户端

对于支持交易业务的FIX Initiator应用,通常重写4个基本消息,OnMessage(NewOrderSingle)、OnMessage(CancelRequest)、 OnMessage(ExecutionReport)、 OnMessage(CancelReject),用于做委托、撤单、执行回包和对撤单拒绝等4项业务。

更全面的介绍是这样的,入门选手可以先不了解这么详细:

  • onCreate:当quickfix创建新会话时调用。会话一旦创建,将应用程序的整个生命周期内保持存在。不管对方是否连接到会话,会话都存在。一旦创建了会话,就可以开始向它发送消息。如果没有人登录,则消息将在与对方建立连接时发送。
  • onLogon:当建立连接并完成FIX登录过程(双方交换有效的登录消息)时调用该函数。
  • onLogout:当某个 FIX 会话不再在线时进行通知,这可能发生在正常的注销交换过程中,或者由于强制终止或网络连接丢失。
  • toAdmin:使您可以了解从FIX引擎发送到交易方的管理消息。这通常对应用程序没有用处,但它可以让你进行与管理消息相关的日志记录。注意:FIX::Message 不是常量,这允许你在发送管理消息之前向其添加字段。
  • toApp:正在发送给对手方的应用程序消息时进行的回调。如果在这个函数中抛出一个 DoNotSend 异常,应用程序将不会发送消息。这可用于取消重新发送一些不必要的消息,比如与当前市场不再相关的订单。注意:FIX::Message 不是常量,这允许你在发送管理消息之前向其添加字段。
    • 抛出 DoNotSend 异常并将消息的 PossDupFlag 设置为 true:a sequence reset will be sent in place of the message.(序列号将被重置为这个要发送的消息的序列号?)
    • 抛出 DoNotSend 异常并将消息的 PossDupFlag 设置为 false:不会发送消息。
  • fromAdmin:当管理消息从交易方发送到 FIX 引擎时通知您。这对于对登录消息(如验证密码)进行额外的验证非常有用。在函数中抛出 RejectLogon 异常将断开对方的连接。
  • fromApp:接收应用程序级请求。如果您的应用程序是一个卖方OMS,您将从函数中获得的新订单请求;如果你是买方,你会在这里拿到你的执行报告。
    • 如果抛出 FieldNotFound 异常,对方将收到一个 Reject消息,表示缺少一个条件要求的字段。当试图检索丢失的字段时,Message 类将抛出此异常,因此很少需要显式地抛出。
    • 如果抛出 UnsupportedMessageType 异常,对方将收到一个 Reject消息,通知他们您的应用程序无法处理这些类型的消息。
    • 如果字段包含您不支持的值,也会抛出 IncorrectTagValue

如果应用程序在多个会话之间共享资源,则必须同步这些资源;可以用SynchronizedApplication 类来自动同步应用程序中的所有函数调用,各种 MessageCracker 类可用于将通用消息结构解析为特定的 FIX 消息。 

2.2 SynchronizedApplication

Application 的区别:通过 Mutex 在回调的时候保证这一次只有一个线程访问应用程序的代码,这样对性能有影响。

/**
* This is a special implementation of the Application interface that takes
* in another Application interface and synchronizes all of its callbacks. This
* will guarantee that only one thread will access the applications code at a time.
*
* This class is a great convenience for writing applications where you
* don't want to worry about synchronization. There is of course a tradeoff
* in that you may be synchronizing more than you need to. There is also a very
* minor performance penalty due to the extra virtual table lookup.
*/
class SynchronizedApplication : public Application
{
public:SynchronizedApplication( Application& app ) : m_app( app ) {}void onCreate( const SessionID& sessionID ){ Locker l( m_mutex ); app().onCreate( sessionID ); }void onLogon( const SessionID& sessionID ){ Locker l( m_mutex ); app().onLogon( sessionID ); }void onLogout( const SessionID& sessionID ){ Locker l( m_mutex ); app().onLogout( sessionID ); }void toAdmin( Message& message, const SessionID& sessionID ){ Locker l( m_mutex ); app().toAdmin( message, sessionID ); }void toApp( Message& message, const SessionID& sessionID )throw( DoNotSend ){ Locker l( m_mutex ); app().toApp( message, sessionID ); }void fromAdmin( const Message& message, const SessionID& sessionID )throw( FieldNotFound, IncorrectDataFormat, IncorrectTagValue, RejectLogon ){ Locker l( m_mutex ); app().fromAdmin( message, sessionID ); }void fromApp( const Message& message, const SessionID& sessionID )throw( FieldNotFound, IncorrectDataFormat, IncorrectTagValue, UnsupportedMessageType ){ Locker l( m_mutex ); app().fromApp( message, sessionID ); }Mutex m_mutex;Application& app() { return m_app; }Application& m_app;
};

2.3. FIX::NullApplication实现

FIX::NullApplication 主要用于不想要实现所有回调接口的场景中,可以继承该类。

class NullApplication : public Application
{void onCreate( const SessionID& ) {}void onLogon( const SessionID& ) {}void onLogout( const SessionID& ) {}void toAdmin( Message&, const SessionID& ) {}void toApp( Message&, const SessionID& ) EXCEPT ( DoNotSend ) {}void fromAdmin( const Message&, const SessionID& )EXCEPT ( FieldNotFound, IncorrectDataFormat, IncorrectTagValue, RejectLogon ) {}void fromApp( const Message&, const SessionID& )EXCEPT ( FieldNotFound, IncorrectDataFormat, IncorrectTagValue, UnsupportedMessageType ) {}
};

 

2.4 SessionSettings

FIX配置使用FIX::SessionSettings读取FIX Session配置文件并传递给QuickFIX框架。

一个FIX应用能够管理多个FIX Session,每一个Session能够采用相同的FIX协议版本,也能够采用不一样的版本。即便采用的是相同的FIX协议版本,不一样FIX Session间也能够有FIX协议细节的差别,经过绑定FIX Session与FIX协议字典的方式来实现,即在Session配置文件中[Session]配置项中使用DataDictionary选项指定相应FIX字典文件来实现。

FIX::SessionSettings settings("sessionConfig.ini");

应用如何实现的管理多session?看Initiator/Acceptor源码可以明白

  class{
'''typedef std::set < SessionID > SessionIDs;typedef std::map < SessionID, int > SessionState;typedef std::map < SessionID, Session* > Sessions;Sessions m_sessions;SessionIDs m_sessionIDs;SessionState m_sessionState;
...
}

 三个map记录id、状态、具体对象。

2.5 FileStoreFactory

QuickFIX提供了存储消息到文件的类FIX::FileStoreFactory。消息文件存储的路径在Session配置中指定。具体配置下文提到。

FIX::FileStoreFactory storeFactory(settings);

2.4 MessageStoreFactory

QuickFIX提供了给Fix Session持久化类型(如文件存储、数据存储,存储内容包括状态、建立时间、消息及其本身维护的发送序列号和接收序列号等)。
如果开发者要自定义持久化方式,也可以自己实现MessageStoreFactory,而且自定义一种MessageStore的ide。

/*** This interface must be implemented to create a MessageStore.*/
class MessageStoreFactory
{
public:virtual ~MessageStoreFactory() {}virtual MessageStore* create( const SessionID& ) = 0;virtual void destroy( MessageStore* ) = 0;
};

2.5 FIX::FileLogFactory

QuickFIX提供了存储全部日志事件到文件的类FIX::FileLogFactory。

2.6 FIX::Message

QuickFIX中定义了不一样FIX协议版本消息的基类FIX::Message,用于定义FIX消息的通用结构,不一样的FIX消息版本的消息定义在不一样的FIX命名空间内定义,如FIX42::Message。FIX::MessageCracker则继承了全部不一样FIX协议版本的MessageCracker类,接收消息后生成具体FIX协议Message对象实现对消息进行处理。

2.7 FIX::Acceptor


FIX::Acceptor用于创建和管理本Acceptor的Session。

具体Session的TCP连接管理、数据读写由具体的SocketAcceptor、SSLSocketAcceptor、ThreadedSocketAcceptor、ThreadedSSLSocketAcceptor实现。

FIX::Acceptor* acceptor = new FIX::SocketAcceptor(application, storeFactory, settings, logFactory);


2.8 FIX::Initiator


FIX::Initiator用于创建和管理本Initiator支持的Session。

具体Session的TCP连接管理、数据读写由具体的SocketInitiator、SSLSocketInitiator、ThreadedSocketInitiator、ThreadedSSLSocketInitiator实现。

FIX::Initiator * initiator = new FIX::SocketInitiator(application,  storeFactory,  settings, logFactory);

三、创建应用

我们根据第二章的内容创建一个应用(主体见2.1)

.h

#ifndef EXECUTOR_APPLICATION_H
#define EXECUTOR_APPLICATION_H#include "quickfix/Application.h"
#include "quickfix/MessageCracker.h"
#include "quickfix/Values.h"
#include "quickfix/Utility.h"
#include "quickfix/Mutex.h"#include "quickfix/fix40/NewOrderSingle.h"
#include "quickfix/fix41/NewOrderSingle.h"
#include "quickfix/fix42/NewOrderSingle.h"
#include "quickfix/fix43/NewOrderSingle.h"
#include "quickfix/fix44/NewOrderSingle.h"
#include "quickfix/fix50/NewOrderSingle.h"class Application
: public FIX::Application, public FIX::MessageCracker
{
public:Application() : m_orderID(0), m_execID(0) {}// Application overloadsvoid onCreate( const FIX::SessionID& );void onLogon( const FIX::SessionID& sessionID );void onLogout( const FIX::SessionID& sessionID );void toAdmin( FIX::Message&, const FIX::SessionID& );void toApp( FIX::Message&, const FIX::SessionID& )EXCEPT( FIX::DoNotSend );void fromAdmin( const FIX::Message&, const FIX::SessionID& )EXCEPT( FIX::FieldNotFound, FIX::IncorrectDataFormat, FIX::IncorrectTagValue, FIX::RejectLogon );void fromApp( const FIX::Message& message, const FIX::SessionID& sessionID )EXCEPT( FIX::FieldNotFound, FIX::IncorrectDataFormat, FIX::IncorrectTagValue, FIX::UnsupportedMessageType );// MessageCracker overloadsvoid onMessage( const FIX40::NewOrderSingle&, const FIX::SessionID& );void onMessage( const FIX41::NewOrderSingle&, const FIX::SessionID& );void onMessage( const FIX42::NewOrderSingle&, const FIX::SessionID& );void onMessage( const FIX43::NewOrderSingle&, const FIX::SessionID& );void onMessage( const FIX44::NewOrderSingle&, const FIX::SessionID& );void onMessage( const FIX50::NewOrderSingle&, const FIX::SessionID& );std::string genOrderID() {std::stringstream stream;stream << ++m_orderID;return stream.str();}std::string genExecID() {std::stringstream stream;stream << ++m_execID;return stream.str();}
private:int m_orderID, m_execID;
};#endif

.cpp

#include "quickfix/config.h"#include "Application.h"
#include "quickfix/Session.h"#include "quickfix/fix40/ExecutionReport.h"
#include "quickfix/fix41/ExecutionReport.h"
#include "quickfix/fix42/ExecutionReport.h"
#include "quickfix/fix43/ExecutionReport.h"
#include "quickfix/fix44/ExecutionReport.h"
#include "quickfix/fix50/ExecutionReport.h"void Application::onCreate( const FIX::SessionID& sessionID ) {}
void Application::onLogon( const FIX::SessionID& sessionID ) {}
void Application::onLogout( const FIX::SessionID& sessionID ) {}
void Application::toAdmin( FIX::Message& message,const FIX::SessionID& sessionID ) {}
void Application::toApp( FIX::Message& message,const FIX::SessionID& sessionID )
EXCEPT( FIX::DoNotSend ) {}void Application::fromAdmin( const FIX::Message& message,const FIX::SessionID& sessionID )
EXCEPT( FIX::FieldNotFound, FIX::IncorrectDataFormat, FIX::IncorrectTagValue, FIX::RejectLogon ) {}void Application::fromApp( const FIX::Message& message,const FIX::SessionID& sessionID )
EXCEPT( FIX::FieldNotFound, FIX::IncorrectDataFormat, FIX::IncorrectTagValue, FIX::UnsupportedMessageType )
{ crack( message, sessionID ); }void Application::onMessage( const FIX40::NewOrderSingle& message,const FIX::SessionID& sessionID )
{FIX::Symbol symbol;FIX::Side side;FIX::OrdType ordType;FIX::OrderQty orderQty;FIX::Price price;FIX::ClOrdID clOrdID;FIX::Account account;message.get( ordType );if ( ordType != FIX::OrdType_LIMIT )throw FIX::IncorrectTagValue( ordType.getField() );message.get( symbol );message.get( side );message.get( orderQty );message.get( price );message.get( clOrdID );FIX40::ExecutionReport executionReport = FIX40::ExecutionReport( FIX::OrderID( genOrderID() ),FIX::ExecID( genExecID() ),FIX::ExecTransType( FIX::ExecTransType_NEW ),FIX::OrdStatus( FIX::OrdStatus_FILLED ),symbol,side,orderQty,FIX::LastShares( orderQty ),FIX::LastPx( price ),FIX::CumQty( orderQty ),FIX::AvgPx( price ) );executionReport.set( clOrdID );if( message.isSet(account) )executionReport.setField( message.get(account) );try{FIX::Session::sendToTarget( executionReport, sessionID );}catch ( FIX::SessionNotFound& ) {}
}

代码很简单,唯一可能值得注意的两点:

1、一般情况下,对于FIX应用开发,除了实现FIX::Application接口,还需要重新实现FIX::MessageCracker从具体FIX协议版本实现继承来的onMessage方法,所以我们一般这么写:

class FIXApplication: public FIX::Application, public FIX::MessageCracker

2、crack接口,他根据message类型匹配到你实现的具体onMessage接口上。

  void crack( const Message& message,const SessionID& sessionID ){const FIX::BeginString& beginString = FIELD_GET_REF( message.getHeader(), BeginString );crack( message, sessionID, beginString );}void crack( const Message& message,const SessionID& sessionID,const BeginString& beginString ){if ( beginString == BeginString_FIX40 )((FIX40::MessageCracker&)(*this)).crack((const FIX40::Message&) message, sessionID);else if ( beginString == BeginString_FIX41 )((FIX41::MessageCracker&)(*this)).crack((const FIX41::Message&) message, sessionID);else if ( beginString == BeginString_FIX42 )((FIX42::MessageCracker&)(*this)).crack((const FIX42::Message&) message, sessionID);else if ( beginString == BeginString_FIX43 )((FIX43::MessageCracker&)(*this)).crack((const FIX43::Message&) message, sessionID);else if ( beginString == BeginString_FIX44 )((FIX44::MessageCracker&)(*this)).crack((const FIX44::Message&) message, sessionID);else if ( beginString == BeginString_FIXT11 ){if( message.isAdmin() ){((FIXT11::MessageCracker&)(*this)).crack((const FIXT11::Message&) message, sessionID);}else{
'''}}}

四、发送方

Acceptor,也可以称作为 Server,就是服务端。客户端从各地发来交易或者行情请求后,由这些服务器端接收,并进一步送给决策端进行验证、决策,并由此服务器返回相应的结果。大概的回复可能是这样的:

  • 行情请求(35=V)——行情报价回复(35=W);
  • 新建订单(35=D)——订单回复(35=8)
  • 撤销订单(35=F)——订单回复(35=8)或者拒绝(35=9)

搭建一个服务端非常的简单,只需要两个类:一个类负责初始化、启动和关停服务(见2.7 FIX::Acceptor);另一个类负责服务,即收发消息(见2.1 Application)。

在第三章我们已经实现了收发消息的类,接下来我们实现Acceptor,万里长征就走完了一大半。

(1)创建FIX Session配置对象FIX::SessionSettings settings(sessionFile);
(2)创建FIX应用:Application application;
(3)创建日志工厂:LogFactory logFactory(settings);
(4)创建消息存储文件工厂:FIX::FileStoreFactory storeFactory(settings);
(5)创建Acceptor服务端:acceptor = new FIX::SocketAcceptor ( application, storeFactory, 
                                         settings, logFactory );
(6)启动:acceptor->start();

#include "config.h"
#include "quickfix/FileStore.h"
#include "quickfix/SocketAcceptor.h"
#include "quickfix/Log.h"
#include "quickfix/SessionSettings.h"
#include "Application.h"
#include <string>
#include <iostream>
#include <fstream>int main( int argc, char** argv )
{if ( argc < 2 ){std::cout << "usage: " << argv[ 0 ]<< " FILE." << std::endl;return 0;}std::string file = argv[ 1 ];FIX::Acceptor * acceptor = 0;try{FIX::SessionSettings settings( file );Application application;FIX::FileStoreFactory storeFactory( settings );FIX::ScreenLogFactory logFactory( settings );acceptor = new FIX::SocketAcceptor ( application, storeFactory, settings, logFactory );acceptor->start();// dosomething wait();acceptor->stop();delete acceptor;return 0;}catch ( std::exception & e ){std::cout << e.what() << std::endl;delete acceptor;return 1;}
}

五、接收方

Initiator,也可以称作为 Client,就是分散在各个地方的交易机(客户端)。业务员在上面操作以后,客户端会向服务器发送请求。请求多种多样,基本常用的有:行情请求(35=V),新建订单(35=D),撤销订单(35=F)。

搭建Initiator和服务端基本一样,不过多解释。(当然,买方卖方的application逻辑肯定不一样,这里只是给你一个代码框架,希望你注意)

#include "config.h"
#include "quickfix/FileStore.h"
#include "quickfix/SocketInitiator.h"
#include "quickfix/SessionSettings.h"
#include "quickfix/Log.h"
#include "Application.h"
#include <string>
#include <iostream>
#include <fstream>int main( int argc, char** argv )
{if ( argc < 2 ){std::cout << "usage: " << argv[ 0 ]<< " FILE." << std::endl;return 0;}std::string file = argv[ 1 ];FIX::Initiator * initiator = 0;try{FIX::SessionSettings settings( file );Application application;FIX::FileStoreFactory storeFactory( settings );FIX::ScreenLogFactory logFactory( settings );initiator = new FIX::SocketInitiator( application, storeFactory, settings, logFactory );initiator->start();application.run();initiator->stop();delete initiator;return 0;}catch ( std::exception & e ){std::cout << e.what();delete initiator;return 1;}
}

六、进一步的操作

通过上文,我们已经了解了quickfix大概的代码框架是什么样子的,接下来认识一些进一步的操作,以便我们可以把这个小demo真的做成一个功能。

 6.1 配置文件

配置文件,简化来讲,有两个:一个是程序调用的properties,另一个是传输时候的字典,相当于“密码本”。
 

先看一下文件内容

6.1.1 quickfix.properties

#quickfix-server.properties
[default]
# 这些字段接的改成你的设置
FileStorePath=fileStore
SocketConnectHost=XXX.XXX.XXX.XXX
SocketConnectPort=XXXXX
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

能看到,配置被分为3大类:开头的[default],然后是两个[session]。

session的意思是,你作为发送方时,不一定发给一个人。或者你作为接收方时,不一定只接收一个人的信息。每个session下面配了不同的SenderCompID,意思就是可能会有很多Sender给我发消息,因此我要根据不同的SenderComID来区别不同的发送方。这个名字可以随便起,只要保证收发双方一致。

其他公用的部分可以放在[default]里面,其中:

  • FileStorePath是存储消息和事件的文件夹,确保一定要有写的权限;
  • SocketConnectHost和SocketConnectPort 是你Accepter的IP地址和端口;
  • TargetCompID 是你发的消息由谁来接收(和SenderCompID对应);
  • ConnectionType是你扮演的角色,有initiator和acceptor两种;
  • HeartBtInt是心跳间隔。

更多配置直接查阅官网。

讲到这里,我害怕你还是不会写两边的配置,所以我复制一份

[default]
# 这些字段记得改成你的设置
FileStorePath=fileStore
SocketConnectHost=***
SocketConnectPort=***
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
#quickfix-server.properties
[default]
# 这些字段记得改成你的设置
FileStorePath=fileStore
SocketAcceptAddress=***
SocketAcceptPort=***
SenderCompID=QUICKFIX_ACCEPTOR# 以下字段可以不改
ConnectionType=acceptor
HeartBtInt=30
#ReconnectInterval=10
FileLogPath=log
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
SocketReuseAddress=Y
UseDataDictionary=Y[session]
TargetCompID=QUICKFIX_INITIATOR1[session]
TargetCompID=QUICKFIX_INITIATOR2

6.1.2 FIX44.modified.xml

在实际业务中,我们会对字典进行一定修改,比如让某些字段从“必须有”到“可以有”,增减某些消息里面的某些字段,甚至定义自己的新消息。在这个时候,我们就可以在原有FIX44.xml的基础上维护我们自己的“密码本”,取个名字:FIX44.modified.xml。

每一个消息都在message的标签内:

  • name="MarketDataRequestReject" 表示名字;
  • msgtype="Y" 表示他的类别,在传输的时候会表现为“35=Y”;
  • msgcat="app" 表示他是App层的传输。另外一个是Admin;
  • field name="MDReqID" required="N" 表示这个消息有个字段叫MDReqID,并且在传输的时候不是必须的。如果一个字段定义为required="Y"但是实际没有传,那么这条消息就会被自动拒收,并返回“35=3”的消息高速对方;
  • group name="NoAltMDSource" 表示里面有个重复组,即一个消息中可能多次出现同一个字段(比如我在查询行情的时候可能查询多个产品的行情),那么这可以“重复”的字段就必须定义在重复组里。

更多信息查阅官网

6.2 官网需要查看的东西

我还希望你可以查看官网的第三章和第四章,这可以帮助你学习quickfix的各种细节用法,包括如何收发消息、重复组的使用、自定义字段使用、他们自带的一套测试框架等等

 七、常见的错误解答&q&a

第一条消息由Initiator 发出,但是在Acceptor端没有看到接收的痕迹。

可能是Initiator端在发送第一条消息的时候过快,以至于抢在了登录之前。

SessionNotFound

一般都是你配置错误导致找不到session,我们前期讲过,一个Session由3个基本要素构成:消息头、发送方、接收方。任何一方有错误都会导致上述的SessionNotFound问题。

比如最常见的错误是:Properties 文件中IP地址和端口不对或被占用?

toApp 接口中,抛出 DoNoSend 异常会对序列号造成影响吗?

Application::toApp() 接口的回调发生在序列号递增(Session::persist())之前,所以不会对序列号造成影响。

SendingTime Accuracy Problem

因为收发双方的时间不对应,根据Fix协议,默认双方时差在30s以上时就会爆出这种时钟不同步的问题。

解决办法1:配置你的机器,使时间一致。

解决办法2:通过设置properties文件,配置CheckLatency=N,这样就不会去做时钟同步检查。


Logout is Called

在建立链接的时候,立即收到Logout is Called的消息,除此之外没有其他提示。

如果不是网络问题,那就是对方拒绝了跟你的通信,但出于安全考虑,又不想暴露原因,就会抛出这么一个消息。

可能的原因有:

  1. 没有使用SSL链接方式;
  2. Logon时候对方需求在消息中包含用户名和密码,但你没有写;
  3. SenderCompID没配好,是对方不能识别或者没有记录在案的ID。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/443924.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

兔老大的系统设计(一)健康度系统

本系列用大白话&#xff0c;手把手带你实现上百个BAT公司内部真实的常用中型系统。评论抽奖送书 与培训班/营销号/忽悠人的低水平作者&#xff0c;不同的是&#xff1a; 保证听懂&#xff08;小白也可以&#xff0c;这是我的一贯风格&#xff0c;字典式小白式的输出&#xff0…

兔老大的系统设计(二)定时系统(延时队列)

之前文章&#xff1a; 兔老大的系统设计&#xff08;一&#xff09;健康度系统 一、背景 延迟队列的应用场景非常广泛&#xff0c;如客户主动操作&#xff1a; 股票定投顾客预约场景会员定时续费/缴费CSDN定时发布或系统内部操作&#xff1a; 订单成功后&#xff0c;在30分…

新鲜美团测试岗面经(带答案)

1、测试环境搭建过程 2、 验证环境部署是否成功时 跑测试用例 接口是什么样的&#xff1f;&#xff08;服务对外提供的调用接口&#xff09; 3、 数据库 表 有个字段 name字段 nameliuguoge 唯一标识id3 修改nameguogeliu update user set nameguogeliu where id3; 4、写…

如何实现Oracle数据库之间的数据同步?

我们都知道&#xff0c;在Oracle数据库的管理与开发工作中&#xff0c;总会存在着一些表数据和基础资料数据&#xff0c;这时需要有效的将这些数据库进行同步合并&#xff0c;有没有什么简单的方法可以实现Oracle数据库之间的数据同步呢&#xff1f;在此诚恺科技重庆服务器频道…

Linux简单命令收录(who,echo,date)【上】

shell严格区分输入命令的大小写&#xff0c;如who、Who和WHO是不同的&#xff0c;其中只有全小写——who是正确的Linux命令。 命令与选项和参数之间要用空格或制表符隔开。连续空格会被shell解释称单个空格。 选项&#xff1a;对命令的特殊定义&#xff0c;以“-”开始&#…

Linux简单命令收录(cal,passwd,clear)【下】

1、cal NAME cal - display a calendar 显示日历 SYNOPSIS cal [options] [[[day] month] year] cal [options] [timestamp|monthname] 用法&#xff1a; cal [选项] [[[日] 月] 年] cal [选项] <时间戳|月份名> OPTIONS -1…

操作系统中避免死锁的银行家算法【表面C++实际C语言】一学就废的菜鸡代码

文章目录银行家算法实验原理数据结构初始化输出资源分配量安全性算法银行家算法完整代码测试数据测试结果第一题第二题银行家算法 银行家算法是一种最有代表性的避免死锁的算法。在避免死锁方法中允许进程动态地申请资源&#xff0c;但系统在进行资源分配之前&#xff0c;应先…

GitHub 使用指南

目录切换分支删除已有文件只删除远程仓库中的文件&#xff0c;不删除本地仓库中的文件同时删除远程仓库和本地仓库中的文件提交文件git查看本地分支连接的是哪个远程分支切换分支 查看本地和远程所有分支 git branch -a当前本地分支为绿色&#xff0c;当前所在分支前带有“*”号…

一学就废的并查集它来了

文章目录题目描述输入输出样例输入样例输出提示算法思想代码实现寻找根节点汇总连接情况完整代码关于flag的初值题目描述 某省调查城镇交通状况&#xff0c;得到现有城镇道路统计表&#xff0c;表中列出了每条道路直接连通的城镇。省政府“畅通工程”的目标是使全省任何两个城…

百战c++(2)

delete 和 delete []的真正区别 delete 对应 new delete[]对应new[]对于简单类型包括简单类型数组&#xff0c;delete 与delete[]没有区别。对于自定义类型数组&#xff0c;delete 只会删除一个元素&#xff0c;delete 则会删除所有元素。 指针和数组的区别 野指针是什么 野指…

Linux实验一:常用的Linux命令

文章目录一、实验目的二、实验要求三、实验内容1、系统的使用2、命令的使用3、文件操作4、系统询问与权限口令5、其它常用命令四、实验操作1、基本命令的使用2、文件和目录操作3、创建用户帐户一、实验目的 1、熟悉Linux的桌面环境&#xff1b; 2、了解Linux所安装的软件包 3、…

Linux实验二:vi编辑器的使用

文章目录一、实验目的二、实验要求三、实验内容1、创建文件2、编辑文件一、实验目的 1、练习并掌握Linux提供的vi编辑器来编译C程序 2、学会利用gcc、gdb编译、调试C程序 3、本次实验的目的是让同学们了解如何使用vi编辑器进行创建和编辑文件 二、实验要求 1、文件编辑器vi…

百战c++(os1)

Linux中的锁 互斥锁&#xff1a;mutex&#xff0c;用于保证在任何时刻&#xff0c;都只能有一个线程访问该对象。当获取锁操作失败时&#xff0c;线程会进入睡眠&#xff0c;等待锁释放时被唤醒 读写锁&#xff1a;rwlock&#xff0c;分为读锁和写锁。处于读操作时&#xff0…

Linux实验三:Shell编程

文章目录一、实验目的二、实验要求三、实验内容1、通配符的使用2、重定向3、管道4、shell变量5、建立下面的脚本&#xff0c;运行并分析输出结果&#xff0c;并给出代码注释。6、编写脚本一、实验目的 1.为文件扩展名使用通配符 2.标准输入、标准输出和标准错误的重定向 3.使…

Linux实验四:编译和调试工具的使用

文章目录一、实验目的&#xff1a;二、实验要求三、实验内容四、实验操作1、用gcc编译程序&#xff0c;写出编译过程&#xff0c;并给出运行结果。2、调试程序&#xff0c;要求用gdb进行调试并给出修改方案。3、make的使用一、实验目的&#xff1a; 1、练习并掌握Linux提供的v…

Linux实验五:Linux环境下的C语言编程

文章目录一、实验目的&#xff1a;二、实验要求三、实验内容1、编写一段C语言程序使其完成&#xff1a;父进程创建两个子进程&#xff0c;每个进程都在屏幕上显示自己的进程ID号。2、上机调试下面的程序&#xff0c;观察运行结果&#xff0c;分析原因。3、利用两个管道进行双向…

ndarray对象的建立

文章目录ndarray&#xff08;别名array&#xff09;常用属性创建NumPy数组使用array()函数使用zeros()函数使用ones()函数使用empty()函数使用arange()函数注意ndarray&#xff08;别名array&#xff09; 常用属性 import numpy as np # Numpy工具包data np.arange(12).res…

Numpy数组的广播机制

文章目录前言数组广播广播机制的使用条件前言 Numpy数组不需要循环遍历&#xff0c;即可对每个元素执行批量的算术运算操作&#xff08;矢量化运算&#xff09;。当两个数组大小&#xff08;Numpy.shape&#xff09;不同时&#xff0c;进行算术运算会出现广播机制。 数组广播…

数组的转置和轴对称

文章目录T属性transpose()方法swapaxes()方法T属性 import numpy as np # Numpy工具包data np.arange(12).reshape(3, 4) # 创建一个3行4列的数组 print(data)# 数组的转置和轴对称 data1 data.T print(data1)print(data) [[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11]] print(dat…

管道实现父子进程的信息传递(一)【fork函数、pipe函数、write/read操作、wait函数】

文章目录题目描述代码实现关于pipe函数关于读写操作关于读写端口关于wait函数功能&#xff1a;注意&#xff1a;关于fork函数题目描述 编写一个程序&#xff0c;利用管道实现父子进程的通信&#xff0c;父进程向子进程发送信息&#xff0c;由子进程输出显示。 代码实现 #inclu…