Trading System Development (11)-Introduction to QuickFIX
1. Introduction to QuickFIX
1. Introduction to QuickFIX
QuickFIX is an open source FIX engine implemented in C++, and it also provides Python and Ruby language implementation.
QuickFIX official website:
http://www.quickfixengine.org
Github address:
https://github.com/quickfix/quickfix
2. QuickFIX compilation
Compile option configuration:
configure
compile:
make
check:
make check
install:
sudo make install
Two, FIX application mode
1. Introduction to FIX application mode
FIX application has two modes: initiator and acceptor.
The initiator is the initiator of the TCP connection, and the acceptor is the listener of the TCP connection. Standard FIX applications (such as CTS FIX gateway) can support both initiator and acceptor modes at the same time, which can initiate a connection or accept a connection request.
When developing FIX application, you need to determine the FIX application mode first, and then select the corresponding QuickFIX class object.
The initiator mode FIX application can use SocketInitiator, SSLSocketInitiator, ThreadedSocketInitiator, ThreadedSSLSocketInitiator to create a FIX Initiator application.
FIX applications in acceptor mode can use SocketAcceptor, SSLSocketAcceptor, ThreadedSocketAcceptor, ThreadedSSLSocketAcceptor to create FIX Acceptor applications.
2, FIX communication
FIX application business is carried out by establishing a FIX Session and exchanging messages between the initiator and the acceptor. FIX business messages have a series of predefined types and formats. At present, there are 5 or 60 standard message types, covering all aspects of investment management such as trading, market quotation, and settlement. FIX application development is basically the programming of FIX messages.
Fix Session can be composed of one or more Fix connections, that is, Fix Session can log in multiple times. Fix Session usually uses several important identifications to distinguish, such as Fix version number string (such as: FIX.4.2), sender ID, receiver ID, each time Fix Session sends a message, the message header BeginString, SenderCompID, TargetCompID will be Assignment. When the message reaches the peer through the FIX connection, the peer can find the local Fix Session to process the message according to the identifier in the message header.
Both parties of FIX communication, including Initiator and Acceptor, will each maintain two incremental sequence numbers (Send message sequence number-add 1 for each message sent, and receive message sequence number-add 1 for each message received).
(1) In communication, the Initiator first initiates a TCP connection request, and the Acceptor receives the network connection request and establishes a TCP connection.
(2) The Initiator initiates a login message request.
(3) After receiving the login request, the Acceptor will go through a series of message checks, and after passing the test, it will return the login confirmation.
(4) After the Initiator receives the login confirmation, after a series of message checks, after passing the test, the FIX Session of the two parties is successfully connected.
(5) Initiator and Acceptor exchange messages.
(6) Either of the Initiator and Acceptor issues an exit message.
3. FIX Session configuration
The initiator or acceptor in QuickFix maintains multiple Fix Sessions. QuickFix uses the combination of BeginString (Fix version number), SenderCompID, and TargetCompID to identify a Session, and the Session identifier is used to distinguish other different Sessions.
The Session configuration file contains two subsections [DEFAULT] and [SESSION]. The [SESSION] section means that a Session is defined in QuickFix, and [DEFAULT] means all the configuration items used by the Session by default. If the configuration required by QuickFix is not provided, QuickFix A ConfigError exception will be thrown, indicating that the configuration is missing or the format is incorrect.
If the [DEFAULT] and [SESSION] partitions both contain the same configuration items, the configuration items of the [SESSION] partition will overwrite the corresponding configuration items of the [DEFAULT] partition.
The QuickFix configuration file sessions.ini is as follows:
[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
Three, QuickFIX core class
1、FIX::Application
FIX applications need to implement the FIX::Application interface:
class Application
{
public:
virtual ~Application() {};
/// Notification of a session begin created
virtual void onCreate( const SessionID& ) = 0;
/// Notification of a session successfully logging on
virtual void onLogon( const SessionID& ) = 0;
/// Notification of a session logging off or disconnecting
virtual void onLogout( const SessionID& ) = 0;
/// Notification of admin message being sent to target
virtual void toAdmin( Message&, const SessionID& ) = 0;
/// Notification of app message being sent to target
virtual void toApp( Message&, const SessionID& )
EXCEPT ( DoNotSend ) = 0;
/// Notification of admin message being received from target
virtual void fromAdmin( const Message&, const SessionID& )
EXCEPT ( FieldNotFound, IncorrectDataFormat, IncorrectTagValue, RejectLogon ) = 0;
/// Notification of app message being received from target
virtual void fromApp( const Message&, const SessionID& )
EXCEPT ( FieldNotFound, IncorrectDataFormat, IncorrectTagValue, UnsupportedMessageType ) = 0;
};
onCreate: Called when a Fix Session is established.
onLogon: Called when a Fix Session login is successful.
onLogout: called when a Fix Session exits.
fromAdmin: When a message is received, after a series of checks, it is called when it belongs to the Admin type.
fromApp: Called when a message is received, after a series of checks, and after it is qualified, it is not of the Admin type.
toAdmin: Called when sending an admin type message.
toApp: Called when sending a non-admin (service type) message.
If you need to use QuickFIX to develop FIX applications, you need to implement the FIX::Application interface and reload the MessageCracker::OnMessage interface of different FIX protocol versions, such as FIX42::MessageCracker.
FIX business is processed asynchronously, and the basic object of business processing is the message. OnMessage is a message receiving callback function, there are multiple overloaded versions, developers only need to overload one FIX version.
For FIX Initiator applications that support transaction services, there are usually 4 basic messages to be rewritten, namely OnMessage(NewOrderSingle), OnMessage(CancelRequest), OnMessage(ExecutionReport), OnMessage(CancelReject), which are used for commissioning, canceling orders, and executing returns. (Including the refusal of entrustment) and the refusal of cancellation of orders and other 4 businesses.
#include "quickfix/Application.h"
#include "quickfix/MessageCracker.h"
class FIXApplication: public FIX::Application, public FIX::MessageCracker
{
public:
/**************************************************
* reimplementation from Application
* ***********************************************/
/// Notification of a session begin created
virtual void onCreate( const SessionID& )
{
}
/// Notification of a session successfully logging on
virtual void onLogon( const SessionID& )
{
}
/// Notification of a session logging off or disconnecting
virtual void onLogout( const SessionID& )
{
}
/// Notification of admin message being sent to target
virtual void toAdmin( Message&, const SessionID& )
{
}
/// Notification of app message being sent to target
void fromApp( const FIX::Message& message, const FIX::SessionID& sessionID )
throw( FIX::FieldNotFound&, FIX::IncorrectDataFormat&, FIX::IncorrectTagValue&,
FIX::UnsupportedMessageType& )
{
crack(message, sessionID);
}
/// Notification of admin message being received from target
virtual void fromAdmin( const Message&, const SessionID& )
throw ( FIX::FieldNotFound, FIX::IncorrectDataFormat, FIX::IncorrectTagValue,
FIX::RejectLogon )
{
}
/// Notification of app message being received from target
virtual void fromApp( const Message&, const SessionID& )
throw ( FIX::FieldNotFound, FIX::IncorrectDataFormat, FIX::IncorrectTagValue,
FIX::UnsupportedMessageType )
{
}
/**************************************************
* reimplementation from FIX42::MessageCracker
* ***********************************************/
virtual void onMessage( const FIX42::NewOrderSingle& message, const FIX::SessionID& )
{
}
virtual void onMessage( const FIX42::OrderCancelRequest& message, const FIX::SessionID& )
{
}
virtual void onMessage( ExecutionReport&, const FIX::SessionID& )
{
}
virtual void onMessage( OrderCancelReject&, const FIX::SessionID& )
{
}
};
2、FIX::SessionSettings
The FIX application configuration uses FIX::SessionSettings to read the FIX Session configuration file and pass it to the QuickFIX framework. A FIX application can manage multiple FIX Sessions, and each Session can use the same FIX protocol version or different versions. Even if the same FIX protocol version is used, there can be differences in the FIX protocol details between different FIX Sessions. This can be achieved by binding the FIX Session and the FIX protocol dictionary, which is used in the [Session] configuration item in the Session configuration file. The DataDictionary option specifies the corresponding FIX dictionary file to achieve.FIX::SessionSettings settings("sessionConfig.ini");
3、FIX::FileStoreFactory
QuickFIX provides the class FIX::FileLogFactory that stores messages to files. The storage path of the message file is specified in the Session configuration.FIX::FileStoreFactory storeFactory(settings);
4、FIX::MessageStoreFactory
QuickFIX provides the type of persistence for Fix Session (such as file storage, data storage, storage content including status, creation time, message and its own maintained sending sequence number and receiving sequence number, etc.).
If the developer wants to customize the persistence method, he can define the MessageStoreFactory implementation and customize a MessageStore.
5、FIX::FileLogFactory
QuickFIX provides the class FIX::FileLogFactory that stores all log events to a file.
6、FIX::ScreenLogFactory
QuickFIX provides a class ScreenLogFactory that displays all message events to standard output.FIX::ScreenLogFactory logFactory(settings);
7、FIX::Message
The base class FIX::Message of different FIX protocol version messages is defined in QuickFIX, which is used to define the general structure of FIX messages. The message definitions of different FIX message versions are defined in different FIX namespaces, such as FIX42::Message. FIX::MessageCracker inherits the MessageCracker class of all different FIX protocol versions, and generates a specific FIX protocol Message object to process the message after receiving the message.
8、FIX::Session
Whether it is a FIX Initiator application or a FIX Acceptor application, the Fix Session time range will be checked when the Fix Session is initialized, that is, after the Fix Session is created by the FIX::SessionFactory. If the Fix Session start is not a valid session on the day, the sending sequence number and the receiving sequence number of the Fix Session will be reset. (FIX stipulates that a Fix Session generally does not exceed 24 hours).
FIX::Session part of the interface is defined as follows:
class Session
{
public:
static bool sendToTarget( Message& message,
const std::string& qualifier = "" )
EXCEPT ( SessionNotFound );
static bool sendToTarget( Message& message,
const SessionID& sessionID )
EXCEPT ( SessionNotFound );
static bool sendToTarget( Message&,
const SenderCompID& senderCompID,
const TargetCompID& targetCompID,
const std::string& qualifier = "" )
EXCEPT ( SessionNotFound );
static bool sendToTarget( Message& message,
const std::string& senderCompID,
const std::string& targetCompID,
const std::string& qualifier = "" )
EXCEPT ( SessionNotFound );
bool send( Message& );
void next();
void next( const UtcTimeStamp& timeStamp );
void next( const std::string&, const UtcTimeStamp& timeStamp, bool queued = false );
void next( const Message&, const UtcTimeStamp& timeStamp, bool queued = false );
};
The next() method is a method that runs on a regular basis. It is mainly used to detect whether you need to send a heartbeat message, whether you need to send a TEST message, whether you need to disconnect, whether you need to generate a LOGON (Initiator).
The next (const Message&) method is used to process the FIX message received by the Session.
9、FIX::Acceptor
FIX::Acceptor is used to read information from the Session configuration file to create and manage the FIX Session supported by this Acceptor. The TCP connection management and data reading and writing of the specific FIX Session are implemented by the specific SocketAcceptor, SSLSocketAcceptor, ThreadedSocketAcceptor, ThreadedSSLSocketAcceptor.FIX::Acceptor* acceptor = new FIX::SocketAcceptor(application, storeFactory, settings, logFactory);
10、FIX::Initiator
FIX::Initiator is used to read information from the Session configuration file to create and manage the FIX Session supported by this Initiator. The TCP connection management and data reading and writing of the specific FIX Session are implemented by the specific SocketInitiator, SSLSocketInitiator, ThreadedSocketInitiator, and ThreadedSSLSocketInitiator.FIX::Initiator * initiator = new FIX::SocketInitiator(application, storeFactory, settings, logFactory);
Four, FIX Acceptor application development
1. Introduction to FIX Acceptor application
The FIX Acceptor application is usually used in the FIX gateway and is deployed on the seller side.
2. FIX Acceptor application creation
(1) Create FIX Session configuration object FIX::SessionSettings settings(sessionFile);
(2) Create FIX application: Application application;
Create log factory: LogFactory logFactory(settings);
Create message storage file factory: FIX::FileStoreFactory storeFactory(settings);
Create Acceptor server: FIX::Acceptor* acceptor = new SocketAcceptor(application, storeFactory, settings, logFactory);
Start SocketAcceptor:acceptor->start();
3. FIX Acceptor application example
The Executor sample demonstrates the FIX Acceptor application that receives order requests and returns transaction receipts.
Application.h file:
#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 overloads
void 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 overloads
void 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
Application.cpp file:
#include "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& ) {}
}
void Application::onMessage( const FIX41::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 );
FIX41::ExecutionReport executionReport = FIX41::ExecutionReport
( FIX::OrderID( genOrderID() ),
FIX::ExecID( genExecID() ),
FIX::ExecTransType( FIX::ExecTransType_NEW ),
FIX::ExecType( FIX::ExecType_FILL ),
FIX::OrdStatus( FIX::OrdStatus_FILLED ),
symbol,
side,
orderQty,
FIX::LastShares( orderQty ),
FIX::LastPx( price ),
FIX::LeavesQty( 0 ),
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& ) {}
}
void Application::onMessage( const FIX42::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 );
FIX42::ExecutionReport executionReport = FIX42::ExecutionReport
( FIX::OrderID( genOrderID() ),
FIX::ExecID( genExecID() ),
FIX::ExecTransType( FIX::ExecTransType_NEW ),
FIX::ExecType( FIX::ExecType_FILL ),
FIX::OrdStatus( FIX::OrdStatus_FILLED ),
symbol,
side,
FIX::LeavesQty( 0 ),
FIX::CumQty( orderQty ),
FIX::AvgPx( price ) );
executionReport.set( clOrdID );
executionReport.set( orderQty );
executionReport.set( FIX::LastShares( orderQty ) );
executionReport.set( FIX::LastPx( price ) );
if( message.isSet(account) )
executionReport.setField( message.get(account) );
try
{
FIX::Session::sendToTarget( executionReport, sessionID );
}
catch ( FIX::SessionNotFound& ) {}
}
void Application::onMessage( const FIX43::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 );
FIX43::ExecutionReport executionReport = FIX43::ExecutionReport
( FIX::OrderID( genOrderID() ),
FIX::ExecID( genExecID() ),
FIX::ExecType( FIX::ExecType_FILL ),
FIX::OrdStatus( FIX::OrdStatus_FILLED ),
side,
FIX::LeavesQty( 0 ),
FIX::CumQty( orderQty ),
FIX::AvgPx( price ) );
executionReport.set( clOrdID );
executionReport.set( symbol );
executionReport.set( orderQty );
executionReport.set( FIX::LastQty( orderQty ) );
executionReport.set( FIX::LastPx( price ) );
if( message.isSet(account) )
executionReport.setField( message.get(account) );
try
{
FIX::Session::sendToTarget( executionReport, sessionID );
}
catch ( FIX::SessionNotFound& ) {}
}
void Application::onMessage( const FIX44::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 );
FIX44::ExecutionReport executionReport = FIX44::ExecutionReport
( FIX::OrderID( genOrderID() ),
FIX::ExecID( genExecID() ),
FIX::ExecType( FIX::ExecType_TRADE ),
FIX::OrdStatus( FIX::OrdStatus_FILLED ),
side,
FIX::LeavesQty( 0 ),
FIX::CumQty( orderQty ),
FIX::AvgPx( price ) );
executionReport.set( clOrdID );
executionReport.set( symbol );
executionReport.set( orderQty );
executionReport.set( FIX::LastQty( orderQty ) );
executionReport.set( FIX::LastPx( price ) );
if( message.isSet(account) )
executionReport.setField( message.get(account) );
try
{
FIX::Session::sendToTarget( executionReport, sessionID );
}
catch ( FIX::SessionNotFound& ) {}
}
void Application::onMessage( const FIX50::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 );
FIX50::ExecutionReport executionReport = FIX50::ExecutionReport
( FIX::OrderID( genOrderID() ),
FIX::ExecID( genExecID() ),
FIX::ExecType( FIX::ExecType_TRADE ),
FIX::OrdStatus( FIX::OrdStatus_FILLED ),
side,
FIX::LeavesQty( 0 ),
FIX::CumQty( orderQty ) );
executionReport.set( clOrdID );
executionReport.set( symbol );
executionReport.set( orderQty );
executionReport.set( FIX::LastQty( orderQty ) );
executionReport.set( FIX::LastPx( price ) );
executionReport.set( FIX::AvgPx( price ) );
if( message.isSet(account) )
executionReport.setField( message.get(account) );
try
{
FIX::Session::sendToTarget( executionReport, sessionID );
}
catch ( FIX::SessionNotFound& ) {}
}
main.cpp file:
#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>
void wait()
{
std::cout << "Type Ctrl-C to quit" << std::endl;
while(true)
{
FIX::process_sleep(1);
}
}
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();
wait();
acceptor->stop();
delete acceptor;
return 0;
}
catch ( std::exception & e )
{
std::cout << e.what() << std::endl;
delete acceptor;
return 1;
}
}
Five, FIX Initiator application development
1. Introduction to FIX Initiator application
The FIX Initiator application is usually used on the trading client side and deployed on the buyer's side.
2. FIX Initiator application creation
(1) Create FIX Session configuration object FIX::SessionSettings settings(sessionFile);
(2) Create FIX application: Application application;
create log factory: LogFactory logFactory(settings);
create message storage file factory: FIX::FileStoreFactory storeFactory(settings);
create Acceptor server: FIX::Initiator* client= new SocketInitiator(application, storeFactory,settings, logFactory);
start SocketInitiator:client->start();
3. FIX Initiator application example
Tradeclient trading client
Application.h file:
#ifndef TRADECLIENT_APPLICATION_H
#define TRADECLIENT_APPLICATION_H
#include "quickfix/Application.h"
#include "quickfix/MessageCracker.h"
#include "quickfix/Values.h"
#include "quickfix/Mutex.h"
#include "quickfix/fix40/NewOrderSingle.h"
#include "quickfix/fix40/ExecutionReport.h"
#include "quickfix/fix40/OrderCancelRequest.h"
#include "quickfix/fix40/OrderCancelReject.h"
#include "quickfix/fix40/OrderCancelReplaceRequest.h"
#include "quickfix/fix41/NewOrderSingle.h"
#include "quickfix/fix41/ExecutionReport.h"
#include "quickfix/fix41/OrderCancelRequest.h"
#include "quickfix/fix41/OrderCancelReject.h"
#include "quickfix/fix41/OrderCancelReplaceRequest.h"
#include "quickfix/fix42/NewOrderSingle.h"
#include "quickfix/fix42/ExecutionReport.h"
#include "quickfix/fix42/OrderCancelRequest.h"
#include "quickfix/fix42/OrderCancelReject.h"
#include "quickfix/fix42/OrderCancelReplaceRequest.h"
#include "quickfix/fix43/NewOrderSingle.h"
#include "quickfix/fix43/ExecutionReport.h"
#include "quickfix/fix43/OrderCancelRequest.h"
#include "quickfix/fix43/OrderCancelReject.h"
#include "quickfix/fix43/OrderCancelReplaceRequest.h"
#include "quickfix/fix43/MarketDataRequest.h"
#include "quickfix/fix44/NewOrderSingle.h"
#include "quickfix/fix44/ExecutionReport.h"
#include "quickfix/fix44/OrderCancelRequest.h"
#include "quickfix/fix44/OrderCancelReject.h"
#include "quickfix/fix44/OrderCancelReplaceRequest.h"
#include "quickfix/fix44/MarketDataRequest.h"
#include "quickfix/fix50/NewOrderSingle.h"
#include "quickfix/fix50/ExecutionReport.h"
#include "quickfix/fix50/OrderCancelRequest.h"
#include "quickfix/fix50/OrderCancelReject.h"
#include "quickfix/fix50/OrderCancelReplaceRequest.h"
#include "quickfix/fix50/MarketDataRequest.h"
#include <queue>
class Application :
public FIX::Application,
public FIX::MessageCracker
{
public:
void run();
private:
void 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 );
void onMessage( const FIX42::ExecutionReport&, const FIX::SessionID& );
void onMessage( const FIX42::OrderCancelReject&, const FIX::SessionID& );
void queryEnterOrder();
void queryCancelOrder();
void queryReplaceOrder();
FIX42::NewOrderSingle queryNewOrderSingle42();
FIX42::OrderCancelRequest queryOrderCancelRequest42();
FIX42::OrderCancelReplaceRequest queryCancelReplaceRequest42();
void queryHeader( FIX::Header& header );
char queryAction();
int queryVersion();
bool queryConfirm( const std::string& query );
FIX::SenderCompID querySenderCompID();
FIX::TargetCompID queryTargetCompID();
FIX::TargetSubID queryTargetSubID();
FIX::ClOrdID queryClOrdID();
FIX::OrigClOrdID queryOrigClOrdID();
FIX::Symbol querySymbol();
FIX::Side querySide();
FIX::OrderQty queryOrderQty();
FIX::OrdType queryOrdType();
FIX::Price queryPrice();
FIX::StopPx queryStopPx();
FIX::TimeInForce queryTimeInForce();
};
#endif
Application.cpp file:
#include "config.h"
#include "Application.h"
#include "quickfix/Session.h"
#include <iostream>
void Application::onLogon( const FIX::SessionID& sessionID )
{
std::cout << std::endl << "Logon - " << sessionID << std::endl;
}
void Application::onLogout( const FIX::SessionID& sessionID )
{
std::cout << std::endl << "Logout - " << sessionID << std::endl;
}
void Application::fromApp( const FIX::Message& message, const FIX::SessionID& sessionID )
EXCEPT( FIX::FieldNotFound, FIX::IncorrectDataFormat, FIX::IncorrectTagValue, FIX::UnsupportedMessageType )
{
crack( message, sessionID );
std::cout << std::endl << "IN: " << message << std::endl;
}
void Application::toApp( FIX::Message& message, const FIX::SessionID& sessionID )
EXCEPT( FIX::DoNotSend )
{
try
{
FIX::PossDupFlag possDupFlag;
message.getHeader().getField( possDupFlag );
if ( possDupFlag ) throw FIX::DoNotSend();
}
catch ( FIX::FieldNotFound& ) {}
std::cout << std::endl
<< "OUT: " << message << std::endl;
}
void Application::onMessage
( const FIX42::ExecutionReport&, const FIX::SessionID& ) {}
void Application::onMessage
( const FIX42::OrderCancelReject&, const FIX::SessionID& ) {}
void Application::run()
{
while ( true )
{
try
{
char action = queryAction();
if ( action == '1' )
queryEnterOrder();
else if ( action == '2' )
queryCancelOrder();
else if ( action == '3' )
queryReplaceOrder();
else if ( action == '5' )
break;
}
catch ( std::exception & e )
{
std::cout << "Message Not Sent: " << e.what();
}
}
}
void Application::queryEnterOrder()
{
int version = queryVersion();
std::cout << "\nNewOrderSingle\n";
FIX::Message order;
switch ( version ) {
case 42:
order = queryNewOrderSingle42();
break;
default:
std::cerr << "No test for version " << version << std::endl;
break;
}
if ( queryConfirm( "Send order" ) )
FIX::Session::sendToTarget( order );
}
void Application::queryCancelOrder()
{
int version = queryVersion();
std::cout << "\nOrderCancelRequest\n";
FIX::Message cancel;
switch ( version ) {
case 42:
cancel = queryOrderCancelRequest42();
break;
default:
std::cerr << "No test for version " << version << std::endl;
break;
}
if ( queryConfirm( "Send cancel" ) )
FIX::Session::sendToTarget( cancel );
}
void Application::queryReplaceOrder()
{
int version = queryVersion();
std::cout << "\nCancelReplaceRequest\n";
FIX::Message replace;
switch ( version ) {
case 42:
replace = queryCancelReplaceRequest42();
break;
default:
std::cerr << "No test for version " << version << std::endl;
break;
}
if ( queryConfirm( "Send replace" ) )
FIX::Session::sendToTarget( replace );
}
FIX42::NewOrderSingle Application::queryNewOrderSingle42()
{
FIX::OrdType ordType;
FIX42::NewOrderSingle newOrderSingle(
queryClOrdID(), FIX::HandlInst( '1' ), querySymbol(), querySide(),
FIX::TransactTime(), ordType = queryOrdType() );
newOrderSingle.set( queryOrderQty() );
newOrderSingle.set( queryTimeInForce() );
if ( ordType == FIX::OrdType_LIMIT || ordType == FIX::OrdType_STOP_LIMIT )
newOrderSingle.set( queryPrice() );
if ( ordType == FIX::OrdType_STOP || ordType == FIX::OrdType_STOP_LIMIT )
newOrderSingle.set( queryStopPx() );
queryHeader( newOrderSingle.getHeader() );
return newOrderSingle;
}
FIX42::OrderCancelRequest Application::queryOrderCancelRequest42()
{
FIX42::OrderCancelRequest orderCancelRequest( queryOrigClOrdID(),
queryClOrdID(), querySymbol(), querySide(), FIX::TransactTime() );
orderCancelRequest.set( queryOrderQty() );
queryHeader( orderCancelRequest.getHeader() );
return orderCancelRequest;
}
FIX42::OrderCancelReplaceRequest Application::queryCancelReplaceRequest42()
{
FIX42::OrderCancelReplaceRequest cancelReplaceRequest(
queryOrigClOrdID(), queryClOrdID(), FIX::HandlInst( '1' ),
querySymbol(), querySide(), FIX::TransactTime(), queryOrdType() );
if ( queryConfirm( "New price" ) )
cancelReplaceRequest.set( queryPrice() );
if ( queryConfirm( "New quantity" ) )
cancelReplaceRequest.set( queryOrderQty() );
queryHeader( cancelReplaceRequest.getHeader() );
return cancelReplaceRequest;
}
void Application::queryHeader( FIX::Header& header )
{
header.setField( querySenderCompID() );
header.setField( queryTargetCompID() );
if ( queryConfirm( "Use a TargetSubID" ) )
header.setField( queryTargetSubID() );
}
char Application::queryAction()
{
char value;
std::cout << std::endl
<< "1) Enter Order" << std::endl
<< "2) Cancel Order" << std::endl
<< "3) Replace Order" << std::endl
<< "4) Market data test" << std::endl
<< "5) Quit" << std::endl
<< "Action: ";
std::cin >> value;
switch ( value )
{
case '1': case '2': case '3': case '4': case '5': break;
default: throw std::exception();
}
return value;
}
int Application::queryVersion()
{
char value;
std::cout << std::endl
<< "1) FIX.4.0" << std::endl
<< "2) FIX.4.1" << std::endl
<< "3) FIX.4.2" << std::endl
<< "4) FIX.4.3" << std::endl
<< "5) FIX.4.4" << std::endl
<< "6) FIXT.1.1 (FIX.5.0)" << std::endl
<< "BeginString: ";
std::cin >> value;
switch ( value )
{
case '1': return 40;
case '2': return 41;
case '3': return 42;
case '4': return 43;
case '5': return 44;
case '6': return 50;
default: throw std::exception();
}
}
bool Application::queryConfirm( const std::string& query )
{
std::string value;
std::cout << std::endl << query << "?: ";
std::cin >> value;
return toupper( *value.c_str() ) == 'Y';
}
FIX::SenderCompID Application::querySenderCompID()
{
std::string value;
std::cout << std::endl << "SenderCompID: ";
std::cin >> value;
return FIX::SenderCompID( value );
}
FIX::TargetCompID Application::queryTargetCompID()
{
std::string value;
std::cout << std::endl << "TargetCompID: ";
std::cin >> value;
return FIX::TargetCompID( value );
}
FIX::TargetSubID Application::queryTargetSubID()
{
std::string value;
std::cout << std::endl << "TargetSubID: ";
std::cin >> value;
return FIX::TargetSubID( value );
}
FIX::ClOrdID Application::queryClOrdID()
{
std::string value;
std::cout << std::endl << "ClOrdID: ";
std::cin >> value;
return FIX::ClOrdID( value );
}
FIX::OrigClOrdID Application::queryOrigClOrdID()
{
std::string value;
std::cout << std::endl << "OrigClOrdID: ";
std::cin >> value;
return FIX::OrigClOrdID( value );
}
FIX::Symbol Application::querySymbol()
{
std::string value;
std::cout << std::endl << "Symbol: ";
std::cin >> value;
return FIX::Symbol( value );
}
FIX::Side Application::querySide()
{
char value;
std::cout << std::endl
<< "1) Buy" << std::endl
<< "2) Sell" << std::endl
<< "3) Sell Short" << std::endl
<< "4) Sell Short Exempt" << std::endl
<< "5) Cross" << std::endl
<< "6) Cross Short" << std::endl
<< "7) Cross Short Exempt" << std::endl
<< "Side: ";
std::cin >> value;
switch ( value )
{
case '1': return FIX::Side( FIX::Side_BUY );
case '2': return FIX::Side( FIX::Side_SELL );
case '3': return FIX::Side( FIX::Side_SELL_SHORT );
case '4': return FIX::Side( FIX::Side_SELL_SHORT_EXEMPT );
case '5': return FIX::Side( FIX::Side_CROSS );
case '6': return FIX::Side( FIX::Side_CROSS_SHORT );
case '7': return FIX::Side( 'A' );
default: throw std::exception();
}
}
FIX::OrderQty Application::queryOrderQty()
{
long value;
std::cout << std::endl << "OrderQty: ";
std::cin >> value;
return FIX::OrderQty( value );
}
FIX::OrdType Application::queryOrdType()
{
char value;
std::cout << std::endl
<< "1) Market" << std::endl
<< "2) Limit" << std::endl
<< "3) Stop" << std::endl
<< "4) Stop Limit" << std::endl
<< "OrdType: ";
std::cin >> value;
switch ( value )
{
case '1': return FIX::OrdType( FIX::OrdType_MARKET );
case '2': return FIX::OrdType( FIX::OrdType_LIMIT );
case '3': return FIX::OrdType( FIX::OrdType_STOP );
case '4': return FIX::OrdType( FIX::OrdType_STOP_LIMIT );
default: throw std::exception();
}
}
FIX::Price Application::queryPrice()
{
double value;
std::cout << std::endl << "Price: ";
std::cin >> value;
return FIX::Price( value );
}
FIX::StopPx Application::queryStopPx()
{
double value;
std::cout << std::endl << "StopPx: ";
std::cin >> value;
return FIX::StopPx( value );
}
FIX::TimeInForce Application::queryTimeInForce()
{
char value;
std::cout << std::endl
<< "1) Day" << std::endl
<< "2) IOC" << std::endl
<< "3) OPG" << std::endl
<< "4) GTC" << std::endl
<< "5) GTX" << std::endl
<< "TimeInForce: ";
std::cin >> value;
switch ( value )
{
case '1': return FIX::TimeInForce( FIX::TimeInForce_DAY );
case '2': return FIX::TimeInForce( FIX::TimeInForce_IMMEDIATE_OR_CANCEL );
case '3': return FIX::TimeInForce( FIX::TimeInForce_AT_THE_OPENING );
case '4': return FIX::TimeInForce( FIX::TimeInForce_GOOD_TILL_CANCEL );
case '5': return FIX::TimeInForce( FIX::TimeInForce_GOOD_TILL_CROSSING );
default: throw std::exception();
}
}
main.cpp file:
#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;
}
}