An example client/server that can be used as a starting point for development using the Fix8Pro C++ Framework.
- Introduction
- Releases
- To download
- Before you build
- To build
- To setup your run environment
- CLI options
- To run
- Securities
- Order Mode
- Market Data Mode
- About random numbers, distributions and generators used
- Misc Functions
- Runtime Interaction
- Sample output
You have two main options when you develop a FIX application using Fix8Pro. For super high performance close to the metal, choose Fix8Pro C++ Framework (this example). For high performance and rapid development using a range of modern languages, choose UFE.
The UFE package comes with freely available high performance UFEed© client adaptors. You can find full source code and instructions for all our UFEed adaptors (Python, Java, C# and C++) on our github repo.
This example demonstrates how to create a client and a server that can accept or initiate FIX sessions. A client or server can run in two modes - as an execution gateway processing orders and sending execution reports; and as a market data server, accepting market data subscriptions, sending full refreshes and incremental updates. These modes are discussed in detail below.
This example uses the standard FIX44 dictionary, uses high quality pseudo-random number generation classes provided by the C++ Standard Library and offers a model for creating trading simulations.
Message displayed with the built-in Fix8Pro printer
There are a number of releases available. Each release builds on the previous in terms of complexity and functionality. These are summarised here:
- Simple client/server with
NewOrderSingle
andExecutionReport
(order mode only). Browse the source - Client/server with market data generation, simple aggregated order book (order mode and market data mode). Browse the source
- Client/server with market data history (custom FIX messages) (order mode and market data mode). Browse the source
You can read about all the available releases here
.
There are two branches: master
(stable version)
and dev
(latest cutting edge).
Optionally specify the dev
branch when you clone:
git clone https://github.com/fix8mt/fix8pro_example.git [-b dev]
cd fix8pro_example
You will need the following to build this example:
- A supported
C++17
compiler and build environment - A Fix8Pro license from
Fix8MT
(or an evaluation license) - An installed Fix8Pro binary package, minimum version 22.03
For example assuming you have installed Fix8Pro to /opt/fix8pro
and your license file is also in /opt/fix8pro
:
mkdir build
cd build
cmake -DFIX8PRO_LICENSE_FILE=/opt/fix8pro/mylic.xml -DFIX8PRO_ROOT=/opt/fix8pro -DCMAKE_INSTALL_PREFIX=./ -DCMAKE_BUILD_TYPE=Release ..
make install
- Add your Fix8Pro binary and library installation directories to your
$PATH
and$LD_LIBRARY_PATH
. For example, if you installed Fix8Pro to/opt/fix8pro
:
export PATH=$PATH:/opt/fix8pro/bin
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/fix8pro/lib
- Set your
$FIX8PRO_LICENSE_FILE
environment variable. For example, if your license file is in/opt/fix8pro
:
export FIX8PRO_LICENSE_FILE=/opt/fix8pro/mylic.xml
These options are defined in Application::options_setup(). Some of the options are added by Fix8ProApplication.
% ./simpleclisrv -h
Fix8Pro sample client/server
Usage:
simpleclisrv [OPTION...]
-b, --brownopts arg set the Brownian options (drift,volume,lpf)
parameters (default: 0.01,20.0,0.025)
-c, --config arg xml config (default: simple_client.xml or
simple_server.xml)
-d, --depth arg use with market data mode, set maximum depth to
request on subscription (default: 10)
-f, --refdata arg specify alternate security reference data
-g, --giveupreset arg number of reliable reconnects to try before
resetting seqnums (default: 10)
-k, --capture arg capture all screen output to specified file
-l, --log arg global log filename (default:
./run/client_%{DATE}_global.log or ./run/server_%{DATE}_global.log)
-m, --marketdata run in marketdata mode (default order mode)
-n, --numsec arg maximum number of securities to use (default no
limit) (default: 0)
-q, --quiet do not print fix output
-r, --reliable start in reliable mode (default: true)
-s, --server run in server mode (default client mode)
-t, --states show session and reliable session thread state
changes (default: true)
-u, --summary run in summary display mode
-C, --clientsession arg name of client session profile in xml config to
use (default: CLI)
-G, --generate generate NewOrderSingle(client) or market
data(server) messages (default: true)
-H, --showheartbeats show inbound heartbeats (default: true)
-I, --interval arg generation interval (msecs); if -ve choose random
interval between 0 and -(n) (default: 5000)
-K, --tickcapture arg capture all trade ticks to specified file
-L, --libpath arg library path to load Fix8 schema object, default
path or LD_LIBRARY_PATH
-P, --password arg FIX password used in logon (cleartext) (default:
password)
-R, --receive arg set next expected receive sequence number
(default: 0)
-S, --send arg set next expected send sequence number (default:
0)
-T, --threadname arg prefix thread names with given string
-U, --username arg FIX username used in logon (default: testuser)
-V, --serversession arg name of server session profile in xml config to
use (default: SRV)
-D, --debug debug mode
info options:
-h, --help Help screen
-v, --version Version
-0, --showcmdline Show cmdline details
-5, --environment Show FIX8PRO environment variable help; show env vars
(debug mode)
-6, --dependencies Show shared library dependencies
-7, --binaryreport Show ABI, lib and binary info
-8, --appinfo Show application banner and info
history options:
-1, --history Print command history; look in current directory or in
$FIX8PRO_CMD_HIST_DIR; use $FIX8PRO_CMD_HIST_SIZE to set
max history or 0 to turn off
-2, --invoke arg Invoke given command by index (use -ve from last)
-3, --remove Remove all history
-4, --interactive Interactve mode, select/edit using bash-like commands,
(? <enter> for help)
examples:
cli/srv pair:
simpleclisrv -c config/simple_server.xml -s
simpleclisrv -c config/simple_client.xml
cli/srv pair with supplied hash pw, random generation interval (~1s), base thread named, run server in summary mode:
simpleclisrv -sc ../config/simple_server.xml -P 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8 -u -T clisrv
simpleclisrv -c ../config/simple_client.xml -I -1000 -P 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8 -u -T clisrv
cli/srv pair running in market data mode, load refdata from file, random generation interval (~1s), client depth 30 levels:
simpleclisrv -sc ../config/simple_server.xml -u -T clisrv -f ../config/sample_ref_data.csv
simpleclisrv -c ../config/simple_client.xml -I -1000 -u -T clisrv -d 30
This example has been designed to run as two instances - a client and a server. For simplicity we'll run the test from the ./build
directory.
By default, the application runs in 'order mode'.
In one terminal we'll run our server:
./simpleclisrv -c ../config/simple_server.xml -s
In our other terminal we'll run our client:
./simpleclisrv -c ../config/simple_client.xml
- When connected, the client will send a
NewOrderSingle
every 5 seconds. The server will simulate an order accept and trade, sending back an acknowledgment followed by a random number of fills (ExecutionReport
s). - From the client, press
l<enter>
to logout and shutdown,q<enter>
to shutdown andx<enter>
to just exit
./simpleclisrv -c ../config/simple_server.xml -s -m
In our other terminal run the client:
./simpleclisrv -c ../config/simple_client.xml -m
- When connected, the client will send a
SecurityListRequest
. The server will respond with aSecurityList
message containing repeating groups of all available securities. The client will then randomly select a sub-set of those securities and sendMarketDataRequest
messages subscribing to those securities. The server will then respond withMarketDataIncrementalRefresh
andMarketDataSnapshotFullRefresh
messages which will show incremental changes to a simulated order book, top of book and full market depth. - From the client, press
l<enter>
to logout and shutdown,q<enter>
to shutdown andx<enter>
to just exit
The default set of secutities used in both order and market data modes are hard coded in the application (the static Instrument
table is show below). The values for each symbol are a reference price and the maximum quantity that can be ordered:
const Instruments SimpleSession::_staticdata
{
{ "AAPL:NASDAQ", { 163.17, 50 } }, { "MSFT:NASDAQ", { 289.86, 50 } },
{ "GOOG:NASDAQ", { 2642.44, 100 } }, { "AMZN:NASDAQ", { 2912.82, 100 } },
{ "TSLA:NASDAQ", { 838.29, 120 } }, { "MMM:NYSE", { 149.5, 120 } },
{ "FB:NASDAQ", { 200.06, 120 } }, { "NVDA:NASDAQ", { 229.36, 120 } },
{ "UNH:NYSE", { 498.65, 120 } }, { "JNJ:NYSE", { 169.48, 120 } },
{ "V:NYSE", { 200.29, 120 } }, { "JPM:NYSE", { 134.40, 300 } },
{ "WMT:NYSE", { 142.82, 300 } }, { "PG:NYSE", { 155.14, 300 } },
{ "XOM:NYSE", { 84.09, 300 } }, { "HD:NYSE", { 324.26, 300 } },
{ "BAC:NYSE", { 40.95, 200 } }, { "MC:NYSE", { 330.76, 200 } },
{ "CVX:NYSE", { 158.65, 200 } }, { "PFE:NYSE", { 48.65, 200 } },
};
You can supply your own list of securities in CSV format (see -f
option). A sample 100 US stocks is provided in the file config/sample_ref_data.csv
, a sample is shown here:
# security, refprice, max order qty
AAPL:NASDAQ, 163.17, 50
MSFT:NASDAQ, 289.86, 50
GOOG:NASDAQ, 2642.44, 100
AMZN:NASDAQ, 2912.82, 100
TSLA:NASDAQ, 838.29, 120
MMM:NYSE, 149.5, 120
FB:NASDAQ, 200.06, 120
NVDA:NASDAQ, 229.36, 120
UNH:NYSE, 498.65, 120
JNJ:NYSE, 169.48, 120
V:NYSE, 200.29, 120
JPM:NYSE, 134.40, 300
WMT:NYSE, 142.82, 300
PG:NYSE, 155.14, 300
XOM:NYSE, 84.09, 300
HD:NYSE, 324.26, 300
BAC:NYSE, 40.95, 200
MC:NYSE, 330.76, 200
CVX:NYSE, 158.65, 200
PFE:NYSE, 48.65, 200
ABBV:NASDAQ, 161.89, 100
LLY:NYSE, 291.42, 200
This is the default mode for the client and server. After the client logs in and establishes a normal session, it will begin to send NewOrderSingle
order messages from a randomly selected symbol.
The client generates orders using the following method:
- A security is randomly selected
- A randomly selected quantity is chosen, from 1 - maximum
- A randomly selected price is chosen, using the cauchy distribution (see notes below)
OrdType
is set to limit- Randomly selected
HandlInst
,TimeInForce
andSide
are chosen
The server receives the order and responds using the following method:
- Orders are randomly accepted or rejected by an average of 4 to 1
- Fields are automatically copied from the inbound
NewOrderSingle
to the outboundExecutionReport
where legal - For rejected orders, a randomly selected
OrdRejReason
is chosen and an order reject is sent - For accepted orders, an order acknowledgement is sent
- Orders will randomly fill or rest by an average of 3 to 1
- For filled orders, the orders will completely fill immediately by an average of 1 in 4
- The remaining quantities are randomly filled by successive
ExecutionReport
messages until exhausted - Each fill is also randomly assigned 1 to 12
ContraBroker
s with a randomly chosen sub-quantity andContraTrader
ID in repeating groups
This mode simulates a market data service. After the client logs in and establishes a normal session, it will request a list of securities, subscribe to some and then receive market data updates continually from the server. The market data generated in this mode is not the same data generated in order mode.
This process is described below:
The client subscribes as follows:
- At startup or if resubscribe is triggered, the client sends a
SecurityListRequest
; - A
SecurityList
is received, containing repeating groups of all available securities - From a randomly selected sub-set of these securities,
MarketDataRequest
messages are sent;SubscriptionRequestType::SnapshotAndUpdates
is chosen; the default number of depth levels is set to 10. You can override this on the client command line with the-d
option
The server operates as follows:
- When a
SecurityListRequest
is received the server responds with aSecurityList
containing repeating groups of all available securities - The server then generates prices continually for all subscribed securities using the same algorithm as Order Mode
- A
MarketDataSnapshotFullRefresh
is sent about 1 in 10 messages - When
MarketDataSnapshotFullRefresh
is sent, about 1 in 2 are top of book (TOB) refreshes containing Best Bid/Volume, Best Offer/Volume, Open, High, Low, Close, Last/Volume, Total Volume, VWAP and Imbalance - The other
MarketDataSnapshotFullRefresh
messages will be the full aggregated orderbook, containing all the depth levels up to the maximum requested (default 10) - A
MarketDataIncrementalRefresh
is sent about 9 in 10 messages. Of these, about 1 in 4 will be cancels, the rest new orders - When an order is generated a matching algorithm attempts to match out the order; remaining volume is then inserted into the orderbook
- Order cancels are applied directly to the orderbook
- After matching or cancelling orders, a difference algorithm generates a series of book deltas
- Each security has its own double sided aggregated orderbook. Bids are reverse sorted by price, asks are sorted by price. Individual (unaggregated) orders are not maintained.
- Each price level holds the total accumulated order quantity and the total number of orders
- Matching occurs only when a new order is generated; orders are matched out from the top of the book, exhausting all available quantity at or below (above) the best bid or offer; matched quantities are subtracted from the available quantity; price levels are removed when no quantity or order count remains
- Any remaining quantity not matched is inserted into the orderbook
- Only limit GTC orders are supported
- A 'daily' record is maintained for each security which contains Open, High, Low, Close, Last/Volume, Total Volume and Total Price Volume (TPV)
- VWAP and Imbalance are calculated when a
MarketDataSnapshotFullRefresh
TOB is sent using the TPV and best bid/offer quantities
We are using the following pseudo-random number generation functions provided by the standard library:
std::mersenne_twister_engine
engine (mt19937_64) seeded using thestd::random_device
(with optionally supplied implementation-defined token)std::uniform_int_distribution
is used to select random integers from a rangestd::uniform_real_distribution
is used to select random floating point numbers from a rangestd::bernoulli_distribution
is used to select a random boolean value with a specified probability
Prices are generated using random numbers from the uniform real distribution. These are then put through a Brownian filter, described here. The parameters to this filter can be set from the command line.
Tick chart for the symbol PEP:NASDAQ from prices generated by this application, demonstrating the Brownian filter
The following parameters can be set on the command line:
Parameter | Description | Range | Default |
---|---|---|---|
drift | random amount to drift | 0.0 - 1.0 | 0.01 |
volume | amount of 'swing' | 0.0 - 50.0 | 20.0 |
lpf | low pass filter level | 0.0 - 1.0 | 0.025 |
Larger drift yields larger trends away from the reference price. Larger volume has a similar effect. Lower lpf creats more variability. You will need to experiemt to find the combination of the three to suit your purposes.
A simple command menu is provided where you can control certain aspects of the application.
l - logout
s - toggle summary
q - disconnect (no logout)
x - just exit
g - toggle generate (market data mode)
Q - toggle quiet
S - toggle states
? - help
l - logout and quit
q - quit (no logout)
x - just exit
g - toggle generate (order mode)
G - resubscribe (market data mode)
s - toggle summary
Q - toggle quiet
S - toggle states
? - help
Sends a Logout
message, waits for a Logout
reply. In server mode, the server will listen for a new connection; in client mode the application exits
Toggles between summary mode and full message display mode. In summary mode a single line is displayed for each message or repeating group element
Closes the session without sending a logout
Exits the application without logout or closing the session
From the client in market data mode, unsubscribes and resubscribes to market data, randomly selecting a new set of securities
In market data mode, toggles the generation of market data from the server; in order mode, toggles the generation of orders from the client
Toggles output; when client mode is on, no market data or order messages are displayed
When enabled, the application will verbosely display session and reliable session states as they change
Passing a filename with the --capture
switch will cause the client (or server) to send a copy of all screen output to specified file. Note that screen escape sequences (such as colours, etc) will be filtered out.
Passing a filename with the --tickcapture
will cause the server to save each tick to the specified file. Use in combination with --numsec 1
to capture a single security's tick data.
Passing a name with the --threadname
will cause the client or server to append the name to the OS thread name. This will allow you to identify the application threads and filter them by name for viewing (see example below).
Shows a reliable client attempting to reconnect, finally performing an automatic sequence reset and successfully connecting. Notice the state changes.