Full Code of objectcomputing/liquibook for AI

master 2427613b32f1 cached
88 files
605.2 KB
172.8k tokens
252 symbols
1 requests
Download .txt
Showing preview only (635K chars total). Download the full file or copy to clipboard to get everything.
Repository: objectcomputing/liquibook
Branch: master
Commit: 2427613b32f1
Files: 88
Total size: 605.2 KB

Directory structure:
gitextract_uq787oa9/

├── .gitignore
├── PERFORMANCE.md
├── README.md
├── README_ORDER_ENTRY.md
├── doc/
│   └── settAug2013/
│       ├── liquibook_sett.html
│       ├── settAug2013_files/
│       │   └── paper.css
│       └── styles/
│           └── SETT.css
├── env.sh
├── examples/
│   ├── .gitignore
│   └── mt_order_entry/
│       ├── Market.cpp
│       ├── Market.h
│       ├── Order.cpp
│       ├── Order.h
│       ├── OrderFwd.h
│       ├── TestOneAonBidTwoAsk.script
│       ├── Util.cpp
│       ├── Util.h
│       ├── mt_order_entry.mpc
│       ├── mt_order_entry_main.cpp
│       ├── order_entry.script
│       └── teststoporders.script
├── license.txt
├── liquibook.features
├── liquibook.mwc
├── mpc/
│   ├── liquibook.mpb
│   ├── liquibook_book.mpb
│   ├── liquibook_exe.mpb
│   ├── liquibook_lib.mpb
│   ├── liquibook_simple.mpb
│   └── liquibook_test.mpb
├── mpc.bat
├── noQuickFAST/
│   └── QuickFASTApplication.mpb
├── pt_run.bat
├── src/
│   ├── book/
│   │   ├── bbo_listener.h
│   │   ├── callback.h
│   │   ├── comparable_price.h
│   │   ├── depth.h
│   │   ├── depth_constants.h
│   │   ├── depth_level.h
│   │   ├── depth_listener.h
│   │   ├── depth_order_book.h
│   │   ├── liquibook.mpc
│   │   ├── logger.h
│   │   ├── main.cpp
│   │   ├── order.h
│   │   ├── order_book.h
│   │   ├── order_book_listener.h
│   │   ├── order_listener.h
│   │   ├── order_tracker.h
│   │   ├── trade_listener.h
│   │   ├── types.h
│   │   └── version.h
│   ├── liquibook_export.h
│   └── simple/
│       ├── liquibook_simple.mpc
│       ├── simple_order.cpp
│       ├── simple_order.h
│       └── simple_order_book.h
├── test/
│   ├── .gitignore
│   ├── latency/
│   │   ├── clock_gettime.h
│   │   ├── liquibook_latency.mpc
│   │   └── lt_order_book.cpp
│   ├── perf/
│   │   ├── liquibook_perf.mpc
│   │   └── pt_order_book.cpp
│   └── unit/
│       ├── changed_checker.h
│       ├── depth_check.h
│       ├── liquibook_unit.mpc
│       ├── ut_all_or_none.cpp
│       ├── ut_bbo_order_book.cpp
│       ├── ut_depth.cpp
│       ├── ut_immediate_or_cancel.cpp
│       ├── ut_listeners.cpp
│       ├── ut_main.cpp
│       ├── ut_market_price.cpp
│       ├── ut_order_book.cpp
│       ├── ut_order_book_shared_ptr.cpp
│       ├── ut_stop_orders.cpp
│       └── ut_utils.h
├── ut_run.bat
├── web/
│   ├── css/
│   │   └── liquibook.css
│   ├── easy.html
│   ├── fast.html
│   ├── flexible.html
│   ├── fluid.html
│   ├── get-started.html
│   ├── index.html
│   └── sub-template.html
├── winenv.bat
└── winenv_clear.bat

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# Compiled Object files
*.slo
*.lo
*.o
*.obj
*.iobj

# Compiled Dynamic libraries
*.so
*.dylib
*.dll

# Compiled Static libraries
*.lai
*.la
*.a
*.lib
lib/

# Compiled executables
*.exe
/bin
/Output

Makefile*
.depend*
*.sln
make

# Artifacts
*.swp

#visual studio work files
*vcproj*
*vcxproj*
Debug
Release
*.pdb
*.ipdb
*.ilk
.vs/
*.VC.*

# test output files
*.log

#helper/customized commands
# (use single letter names and don't add to repo)
?.cmd
?.bat

#a place to hide documents that should not go into the repo:
doc/NotGitWorthy/


================================================
FILE: PERFORMANCE.md
================================================
Performance Test Results, Inserts Per Second
============================================
(newest results on top)
 
<table>
  <tr>
    <th>5 Level Depth</th>
    <th>BBO Only</th>
    <th>Order Book Only</th>
    <th>Note</th>
  </tr>
  <tr>
    <td>2,062,158</td>
    <td>2,139,950</td>
    <td>2,494,532</td>
    <td>Now testing on a modern laptop (2.4 GHZ i7).</td>
  </tr>
  <tr>
    <td>1,231,959</td>
    <td>1,273,510</td>
    <td>1,506,066</td>
    <td>Handling all or none order condition.</td>
  </tr>
  <tr>
    <td>1,249,544</td>
    <td>1,305,482</td>
    <td>1,531,998</td>
    <td>Remove callbacks_added method.  Caller can invoke equivalent if necessary.</td>
  </tr>
  <tr>
    <td>1,222,000</td>
    <td>1,279,711</td>
    <td>1,495,714</td>
    <td>Use vector for callback container.</td>
  </tr>
  <tr>
    <td>1,250,616</td>
    <td>1,264,227</td>
    <td>1,463,738</td>
    <td>Union in callback.  For clarity of purpose, not for performance.</td>
  </tr>
  <tr>
    <td>1,267,135</td>
    <td>1,270,188</td>
    <td>1,469,246</td>
    <td>Combine 2 fill callbacks into one.</td>
  </tr>
  <tr>
    <td>1,233,894</td>
    <td>1,237,154</td>
    <td>1,434,354</td>
    <td>Store excess depth levels in depth to speed repopulation.</td>
  </tr>
  <tr>
    <td>58,936</td>
    <td>153,839</td>
    <td>1,500,874</td>
    <td>Removed spuroious insert on accept of completely filled order.</td>
  </tr>
  <tr>
    <td>38,878</td>
    <td>124,756</td>
    <td>1,495,744</td>
    <td>Initial run with all 3 tests.</td>
  </tr>
</table>



================================================
FILE: README.md
================================================
# Liquibook

Open source order matching engine

Liquibook provides the low-level components that make up an order matching engine. 

Order matching is the process of accepting buy and sell orders for a security (or other fungible asset) and matching them to allow 
trading between parties who are otherwise unknown to each other.

An order matching engine is the heart of every financial exchange, 
and may be used in many other circumstances including trading non-financial assets, serving as a test-bed for trading algorithms, etc.

A typical Liquibook-based application might look something like this:
![Market Application](doc/Images/MarketApplication.png)

In addition to the order matching process itself, Liquibook can be configured
to maintain an "depth book" that records the number of open orders and total quantity
represented by those orders at individual price levels.  

#### Example of an depth book
* Symbol XYZ: 
  * Buy Side: 
    * $53.20 per share: 1203 orders; 150,398 shares.
    * $53.19 per share: 87 orders; 63,28 shares
    * $52.00 per share 3 orders; 2,150 shares
  * Sell Side
    * $54.00 per share 507 orders; 120,700 shares
    * etc...            

## Order properties supported by Liquibook.

Liquibook is aware of the following order properties.

* Side: Buy or Sell
* Quantity
* Symbol to represent the asset to be traded
  * Liquibook imposes no restrictions on the symbol.  It is treated as a simple character string.
* Desired price or "Market" to accept the current price defined by the market.
  * Trades will be generated at the specified price or any better price (higher price for sell orders, lower price for buy orders)
* Stop loss price to hold the order until the market price reaches the specified value.
  * This is often referred to as simply a stop price.
* All or None flag to specify that the entire order should be filled or no trades should happen.
* Immediate or Cancel flag to specify that after all trades that can be made against existing orders on the market have been made, the remainder of the order should be canceled.
  * Note combining All or None and Immediate or Cancel produces an order commonly described as Fill or Kill.

The only required properties are side, quantity and price.  Default values are available for the other properties.

The application can define addtional properties on the order object as necessary.  These properties will have no impact on the behavior of Liquibook.
 
## Operations on Orders

In addition to submitting orders, traders may also submit requests to cancel or modify existing orders.  (Modify is also know as cancel/replace)
The requests may succeed or fail depending on previous trades executed against the order.
  
## Notifications returned to the application.

Liquibook will notify the application when significant events occur to allow the application to actually execute the trades identified by Liquibook, and to allow the application to publish market data for use by traders.

The notifications generated include:

* Notifications intended for trader submitting an order:
  * Order accepted 
  * Order rejected
  * Order filled (full or partial)
  * Order replaced
  * Replace request rejected
  * Order canceled
  * Cancel request rejected.
* Notifications intended to be published as Market Data
  * Trade
    * Note this should also trigger the application to do what it needs to do to make the trade happen.
  * Security changed
    * Triggered by any event that effects a security
      * Does not include rejected requests.
  * Notification of changes in the depth book (if enabled)
    * Depth book changed
    * Best Bid or Best Offer (BBO) changed.


## Performance
* Liquibook is written in C++ using modern, high-performance techniques. This repository includes
the source of a test program that can be used to measure Liquibook performance.
  * Benchmark testing with this program shows sustained rates of  
__2.0 million__ to __2.5 million__ inserts per second. 

As always, the results of this type of performance test can vary depending on the hardware and operating system on which you run the test, so use these numbers as a rough order-of-magnitude estimate of the type of performance your application can expect from Liquibook. 

## Works with Your Design
* Allows an application to use smart or regular pointers to orders.
* Compatible with existing order model, 
  * Requires a trivial interface which can be added to or wrapped around an existing Order object.
* Compatible with existing identifiers for securities, accounts, exchanges, orders, fills

## Example
This repository contains two complete example programs.  These programs can be used to evaluate Liquibook to see if it meets your needs. They can also be used as models for your application or even incorporated directly into your application thanks to the liberal license under which Liquibook is distributed.

The examples are:
* Depth feed publisher and subscriber
  * Generates orders that are submitted to Liquibook and publishes the resulting market data.
  * Uses [QuickFAST](https://github.com/objectcomputing/quickfast) to publish the market data

* Manual Order Entry
  * Allows orders and other requests to be read from the console or submitted by a script (text file)
  * Submits these to Liquibook.
  * Displays the notifications received from Liquibook to the console or to a log file.
  * [Detailed instructions are in the README_ORDER_ENTRY.md file.]( README_ORDER_ENTRY.md)

# Building Liquibook
The good news is you don't need to build Liquibook.  The core of Liquibook is a header-only library, so you can simply
add Liquibook/src to your include path then `#include <book/order_book.h>` to your source, and Liquibook will be available
to be used in your application.

However, this repository includes tests and example programs for Liquibook.  These programs need to be compiled and built in order to run them.  The remainder of this section describes how to do this.

## Dependencies
Liquibook has no runtime dependencies.  It will run in any environment that can run C++ programs.

To build the Liquibook test and example programs from source you need to create makefiles (for linux, et al.) or Project and Solution files for Windows Visual Studio.

Liquibook uses MPC to create these platform-dependent files from a common build definition:
* [MPC](https://github.com/objectcomputing/MPC) for cross-platform builds.

  MPC itself is written in perl, so your environment needs a working Perl compiler.  Most linux systems already have this. If you need a Perl compiler on Windows, OCI recommends [Active State Perl V5.x or later](http://www.activestate.com/)

If you wish to build the unit tests for Liquibook, you will also need the boost test library:
* [BOOST](http://www.boost.org/) (optional) for unit testing.

One of the example programs (publish and subscribe to market data) uses QuickFAST to encode and decode market data messages.  If you wish to run this example you need QuickFAST:
* [QuickFAST](https://github.com/objectcomputing/quickfast) (optional) for building the example depth feed publisher/subscriber.

  QuickFAST his its own dependencies which are described on its web page.

## Submodule Note
The Assertive test framework was used in previous versions, but it is no longer needed.  
If you have imported this submodule to support previous versions, you may delete the liquibook/test/unit/assertiv directory.

## Getting ready to build the tests and example programs.

### Boost Test
If you want to run the Liquibook unit tests (highly recommended!) you should install and/or build boost test before trying to build Liquibook.  Boost test is used in the multifile-test mode rather than simple header-only mode so the compiled boost test library must be available.

Please follow the instructions on the [boost web site](http://www.boost.org/) for building/installing the library in your environment.
When you are done you should export the $BOOST_ROOT environment varialble.  

Because of the many boost build options, please check to be sure that the include files and library files are in the expected locations.
MPC expects to find:
*  Include files in $BOOST_ROOT/include/boost
*  Library files in $BOOST_ROOT/lib


If you prefer not to install boost you can edit the liquibook.features file to change the appropriate line to say `boost=0`  This will disable building the unit tests.

### QuickFAST
The publish and subscribe example program uses QuickFAST.  If you want to run this example program, please see the [QuickFAST web site](https://github.com/objectcomputing/quickfast) to download and build this library.

Set the environment variable $QUICKFAST_ROOT to point to the location where you installed and build QuickFAST.

Before running MPC you should also edit the file liquibook.features to set the value QuickFAST=1

If you do not plan to run this example program, set the environment variable QUICKFAST_ROOT to liquibook/noQuickFAST.

## Building Liquibook on Linux

The env.sh script uses the readlink program which is present on most Linux/Unix systems. 
If you don't have readlink, set the $LIQUIBOOK_ROOT environment variable the directory containing liquibook before running env.sh

Open a shell and type:

<pre>
$ cd liquibook
$ . ./env.sh
$ $MPC_ROOT/mwc.pl -type make liquibook.mwc
$ make depend
$ make all
</pre>

### Output from build
* The Liquibook test and example libraries will be in $LIQUIBOOK_ROOT/lib
* The Liquibook example programs will be in $LIQUIBOOK_ROOT/bin
* The Liquibook test programs will be in $LIQUIBOOK_ROOT/bin/test

## Building Liquibook examples and test programs with Visual Studio

Use the following commands to set up the build environment and create Visual Studio project and solution files.
Note if you are using MinGW or other linux-on-Windows techniques, follow the Linux instructions; however, OCI does not normally test this.

<pre>
> cd liquibook
> copy winenv.bat w.bat #optional if you want to keep the original
                        # note that single character batch file names are ignored in 
                        # .getignore so the customized
                        # file will not be checked into the git repository (a good thing.)
> edit w.bat            # edit is your choice of text editor
                        # follow the instructions in the file itself.
> w.bat                 # sets and verifies environment variables
> mpc.bat               # generate the visual studio solution and project files.
</pre>

Then:
* Start Visual Studio from the command line by typing liquibook.sln or 
* Start Visual Studio from the Windows menu and use the menu File|Open|Project or Solution to load liquibook.sln.

## For any platform

Liquibook should work on any platform with a modern C++ compiler (supporting at least C++11.)  

The MPC program used to create the build files and the Boost library used in the tests and some of the examples support a wide variety of platforms.  

See the [MPC documentation](https://github.com/objectcomputing/MPC) for details about using MPC in your enviornment.

See the [Boost website](http://www.boost.org/) for details about using Boost in your environment.


================================================
FILE: README_ORDER_ENTRY.md
================================================
# Manual Order Entry -- Liquibook example program

This program accepts requests typed from the console or read from a script file and applies them to
one or more order books.  It displays the results of the callbacks generated by Liquibook in
response to those requests.

## The Command Line

The mt_order_entry command accepts two command line options.  Both are optional.

Parameter:

* script_file_name  
  The name of a script file.
  * The script file contains a series of requests -- one per line.  See below for the syntax of these requests.
  * If you don't want a script file, but you want to specify a log file, use a single hyphen (-) for the script file name.  The commands will be read from the console.
* log_file_name  
  The name of a file to which output should be written.  
  * Prompts (if any) will still be written to the console.

## Request syntax
Requests are read from the console or from a script file.

An empty line or a line beginning with a hash (#) is considered a comment and will be ignored.
Other lines read are in the format:

   REQUEST [parameters]* [;]

That is: a one word request followed by parameters as necessary and terminated in a semicolon.  

The semicolon may be optional for some commands, but it can always be included if desired.
The stand-alone word "END" may be substituted for a semicolon.

If a request accepts optional parameters, or if not all required parameters are provided, the 
console user will be prompted for additional parameters until a semicolon is entered.
This prompting will happen even if the input is being read from a file, so the requests coming
in from a script file should be complete and should be terminated with a semicolon.
   
####For example:
<pre>
   > BUY 100 GOOG 850 AON;
       ADDING order:  [#2 BUY 100 GOOG $850 AON Last Event:{Submitted BUY 100 GOOG @850}]
   > BUY              (incomplete request)
   Quantity: 100      (prompt for parameters)
   Symbol: GOOG
   Limit Price or MKT: 800
   AON, or IOC, or STOP, or END: ;
       ADDING order:  [#3 BUY 100 GOOG $800 Last Event:{Submitted BUY 100 GOOG @800}]  
</pre>       
## Requests

Most requests can be spelled out or abbreviated to their first character.  
Parameters accepted by each request will be explained later in this document.
Requests are not case sensitive.  Even though they are shown here in ALL CAPS
a BUY request may be entered as BUY, buy, b, or even Buy.
<pre>
* BUY or B      : Enter a new order to buy a security.
* SELL or S     : Enter a new order to sell a security.
* CANCEL or C   : Request that an existing order be canceled.
* MODIFY or M   : Request that an existing order be modified.
* DISPLAY or D  : Display information about existing orders
* FILE or F     : Open or close a script file.
* ?             : Display help for requests
* QUIT          : Exit the program.
</pre>
   
## Symbols

Liquibook itself imposes no limitations on symbol.  It accepts an arbitrary string of characters 
(actually of 8 bit octets so UTF-8 or other encodings are supported with no special effort.)

The manual entry program has a few restrictions on the symbols to ease parsing: 

    * The symbol may not include spaces.
    * The symbol may not begin with a plus sign (+) or exclamation mark(!)
    * The symbol should not be ALL.
    * The symbol should not be a single asterisk (*)

It is important to stress that these restrictions apply only to the example program, not to Liquibook itself.

Disallowing spaces obviously makes parsing the incoming request easier.  
Using "ALL" and "*" would confict with the DISPLAY command parsing.
  
The restriction against leading plus signs and exclamation marks requires more explanation.

Liquibook uses a separate order book for each security.  When a new symbol is encountered (representing a new security)
the manual entry program will create a new order book for the symbol.  It needs to know whether to create
a simple order book, or a depth book.  

* If the new symbol is prefixed by a plus sign(+), then a simple
order book is created. 
* If it is prefixed by an exclamation mark (!) then a depth book is created.  
* If neither of these prefixes is present then manual entry will prompt the console user to determine what
type of book to create.

For example:
<pre>
    > buy 100 ibm 50;
    New Symbol IBM.
    Add [S]imple book, or [D]epth book, or 'N' to cancel request.
    [SDN}: d
    Create new depth order book for IBM
    ADDING order:  [#1 BUY 100 IBM $50 Last Event:{Submitted BUY 100 IBM @50}]
            Accepted: [#1 BUY 100 IBM $50 Open: 100 Last Event:{Accepted }]
            Book Change:  IBM
            Depth Change:  IBM Changed Change Id: 1 Published: 0
            BIDS:
            Price 50 Count: 1 Quantity: 100 Change id#: 1
            BBO Change:  IBM Changed Change Id: 1 Published: 0
    > buy 200 t 78;
    New Symbol T.
    Add [S]imple book, or [D]epth book, or 'N' to cancel request.
    [SDN}: s
    Create new order book for T
    ADDING order:  [#2 BUY 200 T $78 Last Event:{Submitted BUY 200 T @78}]
            Accepted: [#2 BUY 200 T $78 Open: 200 Last Event:{Accepted }]
            Book Change:  T
    > buy 50 r 99;
    New Symbol R.
    Add [S]imple book, or [D]epth book, or 'N' to cancel request.
    [SDN}: n
    Request ignored
    Cannot process command BUY 50 R 99 ;
</pre>

Since console  prompting does not play well with scripting, it is important that symbols used in scripts
have the appropriate prefix the first time they appear.

It is acceptable to have a + or ! prefix on a symbol for which a book already exists.  The prefix will be ignored
and the existing book will be used.

## Prices

Liquibook uses integers for prices.  It does care what currency unit is actually being used as long as all orders for a
particular security use the same units.   

Because Liquibook uses integers the prices must be expressed in terms of the "atomic currency unit"  For example
if prices are in US Dollars, then the atomic currency unit is often a penny, so fifteen dollars would be expressed to
Liquibook as 1500.

In most cases where a price is needed, the word MARKET (or the abbreviation MKT) is accepted indicating that no limit price is specified for this order, and a trade can be generated at the price specified for the counter order, or at the current market price for the security if both sides of the trade specify MARKET price.

## Order Identity

Liquibook uses the memory address of the applications order object as an identifier for the order. [This may be changed in the future because it
imposes some odd restrictions on object lifetime.]  The manual entry program needs a way for the console user (or script)
to identify orders, and the memory address is obviously not available for this purpose.

Thus the order entry program assigns an order id to each order.  Whenever an order is displayed the order ID appears after a hash sign(#).
So for example if a new order is added, and the response is:

      ADDING order:  [#1 BUY 100 IBM $50 Last Event:{Submitted BUY 100 IBM @50}]

This order may be referred to in following commands by using the order id 1. When an order is read from the console or from a script,
an optional # can prefix the order id.  Thus:  "Cancel #1" and "CANCEL 1" refer to the same order.

Order ID's may also be entered with a leading minus sign.  These order IDs are relative to the NEXT order id that will be used.  
Thus "Cancel -1" would cancel the most recent order entered.  "Cancel -2" would cancel the order before that, and so on.

## Details of specific Requests
   
### BUY or B 
Enter a new order to buy a security.

Syntax:  BUY quantity symbol price [AON | IOC | STOP price]* ;

Parameters:
* *quantity* is number of shares (or other tradable units) to buy.
* *symbol* is an arbitrary character string.
  * See the **Symbol** section above for information about this string.
* *price* is the highest price the trader is willing to pay for each tradable unit of the security, or MARKET to accept any trade.
  * See the **Price** section above for details about expressing prices.
* AON  This optional parameter sets the All-Or-None condition for this order.
* IOC  This optional parameter sets the Immediate-Or-Cancel condition for this order.
* STOP price  This optional parameter sets a STOP LOSS price for this order using the same currency units as the price parameter.

The traling semicolon (or the word END) is required for a BUY request.

Zero or more trades may be triggered by entering a BUY order.

### SELL or S
Enter a new order to sell a security.

Syntax:  SELL quantity symbol price [AON | IOC | STOP price]* ;

Parameters:
* *quantity* is number of shares (or other tradable units) to be sold.
* *symbol* is an arbitrary character string.
  * See the **Symbol** section above for information about this string.
* *price* is the lowest price the trader is willing to accept for each tradable unit of the security, or MARKET to accept any trade.
  * See the **Price** section above for details about expressing prices.
* AON  This optional parameter sets the All-Or-None condition for this order.
* IOC  This optional parameter sets the Immediate-Or-Cancel condition for this order.
* STOP price  This optional parameter sets a STOP LOSS price for this order using the same currency units as the price parameter.

The traling semicolon (or the word END) is required for a SELL request.

Zero or more trades may be triggered by entering a SELL order.

### CANCEL or C
Request that an existing order be canceled.

Parameter:
* orderid, or #orderid, or -relative_order_id.
  * See the **Order Identity** section above for details. 
  
No trades will be triggered by entering a CANCEL request.

The traling semicolon (or the word END) is optional for a CANCEL request. The request is considered complete after one required parameter.

The request may be rejected if the order has already been filled.

### MODIFY or M

Request that an existing order be modified.

Parameters:
* orderid, or #orderid, or -relative_order_id.
  * See the **Order Identity** section above for details. 
* PRICE new_price
  * See the **Price** section above for details about expressing prices.
  * Prices may be increased or decreased.
  * PRICE is optional.  If it is not specified the existing price will be unchanged.
* QUANTITY new_initial_quantity
  * Note this is NOT the new quantity to be open (that's a moving target).  Nor is it a delta to quantity (although that's how Liquibook expresses it internally.)
  * QUANTITY is optional.  If it is not specified the existing quantity will be unchanged.

The traling semicolon (or the word END) is required for a MODIFY request.

Zero or more trades may be triggered by entering a MODIFY request.

The request may be rejected if the order has already been filled, or if no changes are requested.

### DISPLAY or D
Display information about existing orders

Parameters:
* * this optional first parameter (a single asterisk(*)) requests a more verbose display.
* order_id or symbol or ALL.
  * One of these must appear.
  * See the **Order Identity** section above for a description of the ways in which order ID can be expressed.
  * A symbol will display all orders related to a specific symbol.
  * The literal word ALL will display information about all known symbols.

### FILE or F
Open or close a script file.

Parameter:
* If this command appears in a script file, no parameter is allowed. This is considered to be a request to close the script file.
* filename    If this command is entered from the console, the filename is the name of a script file from which future commands should com.

Commands will be read from the script file until:
* The "FILE" command appears in the script file,
* End-of-file on the script file.
* The "QUIT" command appears in the script file.

If the script file was originally opened by a FILE command entered from the console, and the script exits because of-
a FILE command or end-of-file, then control returs to the console.
If the script file was opened from the command line (see the **The Command Line** section), or the QUIT command appears in the script file, then the program exits.

### HELP or ?
Display help for requests

Displays a brief form of the information in this document.

### QUIT
Exit the program.




================================================
FILE: doc/settAug2013/liquibook_sett.html
================================================
<!DOCTYPE HTML>
<head>
    <meta charset="utf-8">

    <meta name="description" content="Building a Market Data Feed With Liquibook"/>
    <meta name="keywords" content="SETT, OCI, finance, order book"/>
    <meta name="author" content="Jeff Schmitz"/>
    <title>SETT August 2013 - Building a Market Data Feed With Liquibook</title>

    <link rel="alternate" type="application/rss+xml" title="RSS" href="http://ociweb.com/sett/rss.xml"/>
    <link href="styles/SETT.css" rel="stylesheet" type="text/css"/>

    <!-- stylings for this article only -->
    <style type="text/css">
        table {
            border-collapse: collapse;
            border-width: 3px;
        }

        table, th, td {
            border: 2px solid black;
        }

        td {
            font-size: 10pt;
        }

        th {
            font-size: 9pt;
        }

        tr {
            text-align: left;
        }
    </style>

    <!--Used for syntax highlighting.  -->
    <link href="http://alexgorbatchev.com/pub/sh/current/styles/shCore.css" rel="stylesheet" type="text/css"/>
    <link href="http://alexgorbatchev.com/pub/sh/current/styles/shThemeDefault.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div class="header">
    <div>
        <div class="left">
            <a href="http://twitter.com/ObjectComputing" class="twitter-follow-button">Follow @ObjectComputing</a>
        </div>
        <div class="right quicklinks">
            <a href="http://www.ociweb.com/">Home</a> |
            <a href="http://www.ociweb.com/sett/index.html"> Software Engineering Tech Trends Archive</a> |
            <a href="http://www.ociweb.com/training">OCI Educational Services</a>
        </div>
    </div>
    <div class="headimage_container">
        <div class="headerimage_leftlogo">
            <a href="http://www.ociweb.com/">
                <img alt="" src="./images/OCILogo.png" height="120" width="180"/></a>
        </div>
        <div class="headimage_left"><img alt="" src="images/Left.png"/></div>
        <div class="headimage_right">
            <img alt="" src="images/Right.png"/>
        </div>
    </div>
    <div class="lower_header">
        <div class="left">
            <img alt="" src="images/SETT.png" height="34" width="377"/>
        </div>
        <div class="right social">
            <!-- AddThis Button BEGIN -->
<div class="addthis_toolbox addthis_default_style">
    <a class="addthis_button_tweet"></a>
    <a id="plusone" class="addthis_button_google_plusone"></a>
    <a class="addthis_button_dzone"></a>
    <a class="addthis_button_reddit"></a>
    <a class="addthis_button_digg"></a>
    <a id="addthis" class="addthis_counter addthis_pill_style"></a>
    <a href="http://ociweb.com/sett/rss.xml"><span class="rssRow">RSS</span></a>
</div>
<script type="text/javascript">
    document.getElementById("plusone").setAttribute("g:plusone:size", "medium");
    document.getElementById("addthis").setAttribute("addthis:ui_delay", "500");
</script>
<script type="text/javascript" src="//s7.addthis.com/js/300/addthis_widget.js#pubid=ra-51702ff71314f8ed"></script>
<!-- AddThis Button END -->

        </div>
    </div>
</div>

<h1>Building a Market Data Feed with Liquibook</h1>

<p class="author">
    by<br/>

    Jeff Schmitz, Principal Software Engineer
    <br/>Object Computing, Inc. (OCI)
</p>

<h2>Introduction</h2>

<p>
  The financial industry relies heavily on systems to disseminate and interpret
  market data. Both trading decisions and analytics are based on the current
  state of the market for a particular security.  A typical trading exchange
  involves multiple parties, and in a conceptual view looks like this:
</p>

<div class="figure">Figure 1: Conceptual View of Exchange</div>
<img src="settAug2013_files/Exchange.png"/>

<p>
  Building such an exchange is a complex undertaking, involving order taking,
  matching, serialization and compression to a feed, dissemination,
  deserialization, decompression, and interpretation.  This paper will
  demonstrate how to build such a system using only open-source components,
  including Liquibook for limit order book matching, QuickFAST for compression
  and decompression, and Boost ASIO for distribution.
</p>

<h3>Liquibook Background</h3>

<p>
  Liquibook is a C++ open source limit order book matching engine.  It is a
  component library that you can integrate into your project for implementing
  matching algorithms.  Liquibook includes components for an order book,
  aggregate depth tracking, BBO tracking, and a trade feed.
</p>

<h3>QuickFAST Background</h3>

<p>
  The FAST protocol is a compression and encoding mechanism for streaming data.
  QuickFAST is an implementation of the FAST protocol in C++ for encoding and 
  decoding message streams using the FAST protocol.
</p>

<h3>Boost ASIO Background</h3>

<p>
  Boost ASIO is a cross-platform C++ library for asynchronous network
  programming.
</p>

<h2>Example System</h2>

<p>
  The example project for this paper will take on a more advanced use case:
  simulating an exchange producing a 5-level depth feed - aggregated by price,
  and a trade feed.  This example involves a number of steps:
</p>

<h3>Accept Orders</h3>

<p>
  The example exchange generates orders, simulating the receipt of orders from
  traders.  A true exchange takes orders from traders by means of an API - 
  often via the FIX protocol.  For the purposes of this paper, internally
  generated orders will suffice.
</p>

<h3>Maintain a Limit Order Book for Each Security</h3>

<p>
  The example exchange must be ready to match unfilled orders against inbound
  orders.  This is done by means of a limit order book.  The limit order book
  keeps these orders sorted so that when multiple existing orders can match a
  new inbound order, the matching is performed in a sequence determined by
  established exchange rules.  The example exchange uses Liquibook's 
  DepthOrderBook class to maintain the limit order book.
</p>

<h3>Match Buy and Sell Orders</h3>

<p>
  The example exchange must match new inbound orders against those in the 
  limit order book.  The exchange must detect a match, and fill the appropriate
  orders.  Liquibook's order book classes naturally includes matching
  capability.
</p>

<h3>Update Trading Client Order Status</h3>

<p>
  An exchange must always notify traders of changes in their order status.
  This includes accepting, filling, and rejecting orders, but also accepting
  or rejecting order cancellation and replacement requests.  In the example
  exchange, there are no trading clients (the orders are randomly generated),
  so this step will be skipped.  Liquibook does, however, provide an interface
  and make callbacks for changes in order status, which can easily be turned
  into notifications sent to trading clients.
</p>

<h3>Aggregate the Top 5 Price Levels into Depth</h3>

<p>
  While a full order book feed is useful, some exchanges choose to send 
  aggregated forms of data instead, known as depth.  Rather than
  representing individual orders, depth models price levels as a whole.
  An exchange will also limit the number of levels distributed - top 5 and 
  top 10 are common limits.  
</p>
<p>
  The example exchange goes through the extra step and complexity of building
  5-level depth.  Liquibook provides the DepthOrderBook class, which
  manages the order book and depth, and the DepthListener class for being
  notified of changes to the top few depth levels.
</p>

<h3>Determine Changes to Depth in Response to an Event</h3>

<p>
  It is not enough to maintain the depth.  In order to produce an 
  incremental feed, the example exchange must also be able to determine which
  levels of the depth have changed.  A change can happen to one or more
  levels in the depth, on both the buy and sell sides, in response to a
  single order event.  Liquibook has an interface for interrogating the various
  levels of depth and finding out if it has changed since the last
  published change.  This makes it trivial to build an incremental feed -
  sending only changed levels to feed clients.
</p>

<h3>Compress and Distributing Market Data in a Feed</h3>

<p>
  In order to effectively trade, feed clients have a need to understand order
  flow in the exchange.  Feeds in which clients are notified of all possible
  order detail are known as "quote" feeds or "full order book" feeds, while
  those which aggregate orders by price are called "depth" feeds.  A "BBO"
  feed gives only the top-level quote for a security, and a "trade" feed 
  reflects all trades, or matched orders.  Liquibook has support for building
  all of these feeds.
</p>

<p>
  The example exchange builds both a trade and depth feed, then compresses the
  trade and depth messages using QuickFAST and sends them to clients using
  Boost ASIO.
</p>

<h3>Handle the Feed in a Client Program</h3>

<p>
  Finally, the example includes feed clients that connect to the feed, decode
  the updates to recreate the trade events and reproduce the market depth.
  The clients use QuickFAST for decoding, Boost ASIO to handle network I/O, and
  Liquibook to reproduce the depth.
</p>

<h2>Getting Started</h2>

<p>
  If you intend to build this project, you will need to download and build:
</p>

<ol>
  <li>Boost version 1.53 or later</li>
  <li>Xerces version 3.1.1 or later</li>
  <li>QuickFAST version 1.5.0 or later</li>
  <li>Liquibook version 1.1.0 or later </li>
</ol>

<p>
  The project is found in total within the directory
  <code> examples/depth_feed_publisher</code> of Liquibook.
</p>

<h2>The Example Exchange</h2>

<p>
  The example exchange consists of two applications, a publisher and a
  subscriber.  The figure below illustrates the flow of data in the example
  exchange.
</p>

<a name="figure2"><div class="figure">Figure 2: Example Exchange Data Flow</div></a>
<img src="settAug2013_files/ExampleExchange.png"/>

<h2>Publisher Application</h2>

<p>
  The publisher implements the exchange in the example project.  The publisher
  generates orders and adds them to the proper order book, handles the trade
  and depth events through listeners, encodes these using QuickFAST, and sends
  them using Boost ASIO.
</p>

<h3>The Order Book</h3>
<p>
  As an exchange, the publisher must implement an order book.  Liquibook comes
  with an order book class - two actually: <code>book::OrderBook</code> for
  managing an order book only, and a derived class
  <code>book::DepthOrderBook</code> for adding depth aggregation to that order
  book.  Since the publisher builds a depth feed, it uses
  <code>book::DepthOrderBook</code>.  The relevant parts of the
  <code>book::OrderBook</code> class are:
</p>

<div class="listing">book/order_book.h: Selected parts of OrderBook class declaration</div>
<pre class="code">
namespace liquibook { namespace book {

template &lt;class OrderPtr = Order*&gt;
class OrderBook {
public:
  typedef OrderBook&lt;OrderPtr &gt; MyClass;
  typedef TradeListener&lt;MyClass &gt; TypedTradeListener;

  /// @brief set the trade listener
  void set_trade_listener(TypedTradeListener* listener);

  /// @brief add an order to book
  /// @param order the order to add
  /// @param conditions special conditions on the order
  /// @return true if the add resulted in a fill
  virtual bool add(const OrderPtr& order, OrderConditions conditions = 0);

  /// @brief perform all callbacks in the queue
  virtual void perform_callbacks();
};

} }
</pre>

<p>
  Note that the <code>book::OrderBook</code> class is a template class,
  allowing the user to define not only the order class used in the order book,
  but the style of pointer used.  This can be a regular pointer or a smart
  pointer.
</p>

<p>
  The first method of interest allows a setting of a listener for trades.  This
  listener, shown later, gets notified when a trade occurs.  The example
  exchange needs this notification to build a trade feed, and thus sets the
  trade listener.  The <code>book::OrderBook</code> class also includes
  listeners for all order status updates, for providing updates back to the
  trade clients, and a listener for all changes to the order book, to build a
  full order book feed.
</p>

<p>
  Next is a method to add an order to the order book.  <code>add()</code> 
  accepts an order pointer, and condition flags for special conditions, like
  an immediate or cancel order.  Note that there are also methods (not used in
  the example project) to cancel an order and to replace an order.
</p>

<p>
  Finally, there is a method to perform the callbacks on the order book.
  Liquibook gives the client code the responsibility to execute this method,
  so that it can be done in the calling thread, or a background thread.
  By default, this method calls <code>perform_callback()</code> for each
  callback in the queue.  Naturally, <code>perform_callback()</code> can be
  overridden, but that is not necessary in the publisher.  The default
  implementation of this method issues callbacks for to the trade listener,
  order listener, and order book listener, if present.
</p>

<p>
  The derived <code>book::DepthOrderBook</code> class is simpler:
</p>

<div class="listing">book/depth_order_book.h: DepthOrderBook class declaration</div>
<pre class="code">
namespace liquibook { namespace book {

/// @brief Implementation of order book child class, that incorporates
///        aggregate depth tracking.  Overrides perform_callback() method to 
//         track depth aggregated by price.
template <class OrderPtr = Order*, int SIZE = 5>
class DepthOrderBook : public OrderBook<OrderPtr> {
public:
  typedef Depth<SIZE> DepthTracker;
  typedef BboListener<DepthOrderBook >TypedBboListener;
  typedef DepthListener<DepthOrderBook >TypedDepthListener;
  typedef Callback<OrderPtr> DobCallback;

  /// @brief construct
  DepthOrderBook();

  /// @brief set the BBO listener
  void set_bbo_listener(TypedBboListener* bbo_listener);

  /// @brief set the depth listener
  void set_depth_listener(TypedDepthListener* depth_listener);

  /// @brief handle a single callback
  virtual void perform_callback(DobCallback& cb);

  // @brief access the depth tracker
  DepthTracker& depth();

  // @brief access the depth tracker
  const DepthTracker& depth() const;

private:
  DepthTracker depth_;
  TypedBboListener* bbo_listener_;
  TypedDepthListener* depth_listener_;
};
</pre>

<p>
  <code>book::DepthOrderBook</code> overrides <code>perform_callback()</code>
  to update its depth, which is accessible through the <code>depth()</code>
  accessor methods.  In addition, <code>book::DepthOrderBook</code> adds two
  new listeners - a BBO listener, for tracking only changes to the best bid
  and best offer, and a depth listener, for tracking all depth changes.
</p>

<p>
  To build the exchange, the publisher must create a 
  <code>book::DepthOrderBook</code>, and it set the trade listener and depth
  listener.  While handling these callbacks, the publisher will need to 
  update the feed clients with trade and depth update messages.
</p>

<p>
  Note that in some of the callbacks, a pointer to the order book is provided,
  while a pointer to the order is provided in others.
  This gives the publisher two opportunities to provide additional fields and 
  logic through inheritance.  The publisher takes advantage of this, providing
  access to the symbol of the order book's security:
</p>

<div class="listing">example_order_book.h: ExampleOrderBook class declaration</div>
<pre class="code">
namespace liquibook { namespace examples {

typedef boost::shared_ptr<Order> OrderPtr;

class ExampleOrderBook : public book::DepthOrderBook&lt;OrderPtr&gt; {
public:
  ExampleOrderBook(const std::string& symbol);
  const std::string& symbol() const;

private:
  std::string symbol_;
};

} } // End namespace
</pre>

<p>
  Here the reader will note that the <code>ExampleOrderBook</code>
  class specializes <code>book::DepthOrderBook</code> and binds a shared
  pointer of <code>Order</code> to its parent's template argument.
  The <code>Order</code> class is shown below.  The trivial
  implementation of the <code>ExampleOrderBook</code> is omitted
  from this paper.  The class diagram for order books looks like this:
</p>

<div class="figure">Figure 3: Order Book Class Diagram</div>
<img src="settAug2013_files/OrderBookUML.png"/>

<h3>The Exchange</h3>

<p>
  The <code>ExampleOrderBook</code> class manages the order book and
  depth for a single security.  Since exchanges handle multiple securities, 
  somewhere the publisher needs to create an order book for each security, and
  maintain a mapping from a symbol to its order book.  The publisher does this
  in the <code>Exchange</code> class:
</p>

<div class="listing">exchange.h: Exchange class declaration</div>
<pre class="code">
namespace liquibook { namespace examples {

class Exchange {
public:
  Exchange(ExampleOrderBook::TypedDepthListener* depth_listener,
           ExampleOrderBook::TypedTradeListener* trade_listener);

  // Permanently add an order book to the exchange
  void add_order_book(const std::string&amp; symbol);

  // Handle an incoming order
  void add_order(const std::string&amp; symbol, OrderPtr&amp; order);
private:
  typedef std::map&lt;std::string, ExampleOrderBook&gt; OrderBookMap;
  OrderBookMap order_books_;
  ExampleOrderBook::TypedDepthListener* depth_listener_;
  ExampleOrderBook::TypedTradeListener* trade_listener_;
};

} }
</pre>

<p> 
  The <code>Exchange</code> class has only three methods: a
  constructor, a method to add an order book, and a method to handle an order.
  As expected, it maintains a map of order books.  It also maintains pointers
  to the depth listener and trade listener.  The constructor does exactly that,
  in order to add the listeners to future order books:
</p>

<div class="listing">exchange.cpp: Exchange class implementation</div>
<pre class="code">
namespace liquibook { namespace examples {

Exchange::Exchange(ExampleOrderBook::TypedDepthListener* depth_listener,
                   ExampleOrderBook::TypedTradeListener* trade_listener)
: depth_listener_(depth_listener),
  trade_listener_(trade_listener)
{
}
</pre>

<p>
  The <code>add_order_book()</code> method creates a new order book, sets the
  listeners on the order book, and adds a mapping from the given symbol to the
  order book:
</p>

<div class="listing">exchange.cpp: Exchange class implementation, continued</div>
<pre class="code">
void
Exchange::add_order_book(const std::string&amp; sym)
{
  std::pair<OrderBookMap::iterator, bool> result;
  result = order_books_.insert(std::make_pair(sym, ExampleOrderBook(sym)));
  result.first-&gt;second.set_depth_listener(depth_listener_);
  result.first-&gt;second.set_trade_listener(trade_listener_);
}
</pre>

<p>
  Finally, the <code>add_order()</code> method finds the correct order book,
  adds the order to the order book, and then triggers callbacks by calling
  the <code>perform_callbacks()</code> method:
</p>

<div class="listing">exchange.cpp: Exchange class implementation, continued</div>
<pre class="code">
void
Exchange::add_order(const std::string&amp; symbol, OrderPtr&amp; order)
{
  OrderBookMap::iterator order_book = order_books_.find(symbol);
  if (order_book != order_books_.end()) {
    order_book-&gt;second.add(order);
    order_book-&gt;second.perform_callbacks();
  }
}

} } // End namespace
</pre>

<p>
  The call to <code>perform_callbacks()</code> will trigger any callbacks to
  the provided listeners.  This way, a single thread is manipulating the order
  books.  It is also possible to issue <code>perform_callbacks()</code> calls
  in a background thread, but the publisher does it this way for simplicity.
</p>

<h3>Order Class</h3>

<p>
  Liquibook requires that the application define an order class to represent 
  an order to match.  Liquibook requires that it meet the interface defined
  in the class <code>src/book/order.h</code>:
</p>

<div class="listing">book/order.h: Order class (Liquibook) declaration</div>
<pre class="code">
namespace liquibook { namespace book {

class Order {
public:
  /// @brief is this a limit order?
  bool is_limit() const;

  /// @brief is this order a buy?
  virtual bool is_buy() const = 0;

  /// @brief get the price of this order, or 0 if a market order
  virtual Price price() const = 0;

  /// @brief get the quantity of this order
  virtual Quantity order_qty() const = 0;
};

} }
</pre>

<p>
  The publisher defines <code>Order</code> to implement the 
  <code>book::Order</code> interface:
</p>

<div class="listing">order.h: Order class (publisher) declaration</div>
<pre class="code">
namespace liquibook { namespace examples {

class Order : public book::Order {
public:
  Order(bool buy,
        const double&amp; price,
        book::Quantity qty);

  virtual bool is_buy() const;
  virtual book::Quantity order_qty() const;
  virtual book::Price price() const;

  static const uint8_t precision_;
private:
  bool is_buy_;
  double price_;
  book::Quantity qty_;
};

} }
</pre>

<p>
  Note that as a template class, Liquibook's <code>book::OrderBook</code> does
  not strictly require inheriting from the <code>book::Order</code> class,
  just that the interface be implemented.
</p>
<p>
  The example's <code>Order</code> class implements <code>book::Order</code>,
  and adds a static field called <code>precision_</code> for price conversion.
</p>

<p>
  The <code>Order</code> constructor simply copies the arguments passed to it:
</p>

<div class="listing">order.h: Order class implementation</div>
<pre class="code">
namespace liquibook { namespace examples {

const uint8_t Order::precision_(100);

Order::Order(bool buy, const double&amp; price, book::Quantity qty)
: is_buy_(buy),
  price_(price),
  qty_(qty)
{
}
</pre>

<p>
  Note the presence of the static initializer of <code>precision_</code>.
</p>

<p>
  The <code>is_buy()</code> and <code>order_qty()</code> are simple accessors:
</p>

<div class="listing">order.h: Order class implementation, continued</div>
<pre class="code">
bool
Order::is_buy() const
{
  return is_buy_;
}

book::Quantity
Order::order_qty() const
{
  return qty_;
}
</pre>

<p>
  The final method is used to provide a price to the order book.  For
  performance reasons, the Liquibook requires this to be in integer format,
  so <code>Order</code> converts the price by multiplying by the precision:
</p>

<div class="listing">order.h: Order class implementation, continued</div>
<pre class="code">
book::Price
Order::price() const
{
  return price_ * precision_;
}
</pre>

<p>
  There are two implications - first, that the publisher is limited to penny 
  precision.  This is suitable for purposes of the example, but may need to be
  increased for other use cases.  The second implication is that all securities
  in the publisher have the same precision - since a static variable is used to
  set precision.  Again, in other use cases, this may not be sufficient.
</p>

<h3>QuickFAST Templates</h3>

<p>
  To transmit messages using the FAST protocol, one must first decide on the 
  messages to transmit on the feed.  This exchange will produce a trade feed
  and an incremental depth feed.  A simplified view of a trade consists of a
  symbol, a quantity, and a trade price.  The publisher alters this slightly
  by providing the trade cost (price times quantity), from which price per
  share can be derived.
</p>

<p>
  QuickFAST uses XML files, called templates to describe the messages in the 
  feed protocol.  The example's templates can be found in the file 
  <code>examples/depth_feed_publisher/depth.xml</code>.  The trade message 
  template looks like this:
</p>

<pre class="code">
&lt;template name="Trade" id="1"&gt;
  &lt;uInt16 name="MessageType" id="100"&gt;
    &lt;constant value="22"/&gt;
  &lt;/uInt16&gt;
  &lt;uInt32 name="SequenceNumber" id="200"&gt;
    &lt;increment/&gt;
  &lt;/uInt32&gt;
  &lt;uInt32 name="Timestamp" id="300"&gt;
    &lt;copy/&gt;
  &lt;/uInt32&gt;
  &lt;string name="Symbol" id="400"&gt;
    &lt;copy/&gt;
  &lt;/string&gt;
  &lt;uInt32 name="Quantity" id="604"&gt;
    &lt;copy/&gt;
  &lt;/uInt32&gt;
  &lt;uInt32 name="Cost" id="603"&gt;
    &lt;copy/&gt;
  &lt;/uInt32&gt;
&lt;/template&gt;
</pre>

<p>
  Note that the tag names within the <code>&lt;template&gt;</code> tag
  indicate the type of the field.  The reader may be surprised to see a
  currency field (Cost) represented as an integer (uint32).  This, however is
  consistent with Liquibook's internal storage of prices as as integer.  
  Liquibook's design decision is thus carried forward to the feed protocol,
  requiring the clients to convert these prices back to their decimal format.
</p>

<p>
  In addition to the expected trade fields, the publisher provides a message
  type, to indicate to the subscriber that the message is for a trade, a
  sequence number, so the subscriber can be confident it has received all
  messages, and processed them in the correct order, and a timestamp, so the
  subscriber can be confident the message has been received in a timely manner.
</p>

<p>
  The depth update template is more complicated:
</p>

<pre class="code">
&lt;template name="Depth" id="2"&gt;
  &lt;uInt16 name="MessageType" id="100"&gt;
    &lt;constant value="11"/&gt;
  &lt;/uInt16&gt;
  &lt;uInt32 name="SequenceNumber" id="200"&gt;
    &lt;increment/&gt;
  &lt;/uInt32&gt;
  &lt;uInt32 name="Timestamp" id="300"&gt;
    &lt;copy/&gt;
  &lt;/uInt32&gt;
  &lt;string name="Symbol" id="400"&gt;
    &lt;copy/&gt;
  &lt;/string&gt;
  &lt;sequence name="Bids" id="500"&gt;
    &lt;uInt8 name="LevelNum" id="501"&gt;
      &lt;copy/&gt;
    &lt;/uInt8&gt;
    &lt;uInt32 name="OrderCount" id="502"&gt;
      &lt;copy/&gt;
    &lt;/uInt32&gt;
    &lt;uInt32 name="Price" id="503"&gt;
      &lt;copy/&gt;
    &lt;/uInt32&gt;
    &lt;uInt32 name="AggregateQty" id="504"&gt;
      &lt;copy/&gt;
    &lt;/uInt32&gt;
  &lt;/sequence&gt;
  &lt;sequence name="Asks" id="600"&gt;
    &lt;uInt8 name="LevelNum" id="601"&gt;
      &lt;copy/&gt;
    &lt;/uInt8&gt;
    &lt;uInt32 name="OrderCount" id="602"&gt;
      &lt;copy/&gt;
    &lt;/uInt32&gt;
    &lt;uInt32 name="Price" id="603"&gt;
      &lt;copy/&gt;
    &lt;/uInt32&gt;
    &lt;uInt32 name="AggregateQty" id="604"&gt;
      &lt;copy/&gt;
    &lt;/uInt32&gt;
  &lt;/sequence&gt;
&lt;/template&gt;
</pre>

<p>
  The depth message begins with the same 4 fields as the trade message: 
  message type, sequence number, timestamp, and symbol.  The depth message also
  includes two sequences, or  variable-length lists.  These sequences
  represent the changed bid and ask levels of the depth for the message
  security.  The sequences themselves contain a set of fields, although in
  this case both sequences are of the same type.
</p>

<p>
  Each changed depth level begins with a level number, indicating which level 
  has changed.  If the exchange were to produce all depth levels on every 
  change, this would not be necessary.  Because in an incremental feed the
  sequence element could represent any of the 5 levels, it is required.
</p>

<p>
  Order count indicates the number of orders which were aggregated at this 
  level.  If the order count is 0, it indicates a deleted level.  Price is the
  price common to all the aggregated orders at this level.  As in the trade
  message, it is represented by an integer.  Finally, aggregate quantity shows
  the sum of all the order quantities at this level.
</p>

<h3>Using QuickFAST Template</h3>

<p>
  With the template in place, the publisher (and subscriber) must use
  QuickFAST to put the templates to use for encoding (and decoding).  The 
  publisher (and subscriber) do this by means of the
  <code>TemplateConsumer</code> class:
</p>

<div class="listing">tempate_consumer.h: TemplateConsumer class declaration</div>
<pre class="code">
namespace liquibook { namespace examples {

class TemplateConsumer {
public:
  static QuickFAST::Codecs::TemplateRegistryPtr 
             parse_templates(const std::string&amp; template_filename);

  // Trade field identities
  static const QuickFAST::Messages::FieldIdentity id_qty_;
  static const QuickFAST::Messages::FieldIdentity id_cost_;

  // Common field identities
  static const QuickFAST::Messages::FieldIdentity id_seq_num_;
  static const QuickFAST::Messages::FieldIdentity id_msg_type_;
  static const QuickFAST::Messages::FieldIdentity id_timestamp_;
  static const QuickFAST::Messages::FieldIdentity id_symbol_;

  // Depth field identities
  static const QuickFAST::Messages::FieldIdentity id_bids_length_;
  static const QuickFAST::Messages::FieldIdentity id_bids_;
  static const QuickFAST::Messages::FieldIdentity id_asks_length_;
  static const QuickFAST::Messages::FieldIdentity id_asks_;

  static const QuickFAST::Messages::FieldIdentity id_level_num_;
  static const QuickFAST::Messages::FieldIdentity id_order_count_;
  static const QuickFAST::Messages::FieldIdentity id_price_;
  static const QuickFAST::Messages::FieldIdentity id_size_;
};

} }
</pre>

<p>
  The <code>TemplateConsumer</code> class also has a static
  <code>parse_templates()</code> method for parsing of templates.  It also 
  includes several static members of type
  <code>const QuickFAST::Messages::FieldIdentityCPtr</code>.  These members
  are used by QuickFAST to establish identities of fields within the various
  messages.  The reader will notice that the field identifiers correspond to
  fields in the template definition, with two exceptions:
  <code>id_bids_length_</code>, and <code>id_asks_length_</code>.  This is
  because the FAST protocol specifies that a sequence length is required for
  each sequence, but can be implicitly declared.
</p>

<p>
</p>

<p>
  These members are initialized in a manner consistent with the template 
  definition:
</p>

<div class="listing">tempate_consumer.cpp: TemplateConsumer class implementation</div>
<pre class="code">
namespace liquibook { namespace examples {

using namespace QuickFAST::Messages;

const FieldIdentity TemplateConsumer::id_seq_num_("SequenceNumber");

const FieldIdentity TemplateConsumer::id_msg_type_("MessageType");

const FieldIdentity TemplateConsumer::id_timestamp_("Timestamp");

// more like this...
</pre>

<p>
  The remaining members are initialized in a similar manner, and thus are not
  included in this paper.  Finally, there is the <code>parse_templates()
  method</code> which parses the QuickFAST templates:
</p>

<div class="listing">tempate_consumer.cpp: TemplateConsumer class implementation, continued</div>
<pre class="code">
QuickFAST::Codecs::TemplateRegistryPtr 
TemplateConsumer::parse_templates(const std::string& template_filename)
{
  std::ifstream template_stream(template_filename.c_str());
  QuickFAST::Codecs::XMLTemplateParser parser;
  return parser.parse(template_stream);
}

} } 
</pre>

<p>
  The <code>parse_templates()</code> method opens an input file stream using
  the filename passed to it.  Then it creates a
  <code>QuickFAST::Codecs::XMLTemplateParser</code> and calls the
  <code>parse()</code> method on it, passing it the input file stream.  This 
  parses the template file, and produces the
  <code>QuickFAST::Codecs::TemplateRegistryPtr</code> which preserves the
  parsed form of the template file.
</p>

<h3>Connecting the Publisher and Subscriber</h3>

<p>
  The publisher and subscriber communicate over TCP/IP using boost::ASIO.  The
  communication between publisher and subscriber is one way - the publisher
  only sends messages, and the subscriber only receives them.  The
  communication is also one-to-many, as multiple subscribers connect to a 
  single publisher.  Since QuickFAST encodes a stream of data, uniquely
  compressed for each recipient, the publisher will need to maintain a
  QuickFAST state for each subscriber's encoder.  This is done through a class
  called <code>DepthFeedSession</code>.
</p>

<p>
  The publisher also uses the <code>DepthFeedConnection</code> to listen for connections from subscribers.  The <code>DepthFeedConnection</code> will establish many <code>DepthFeedSession</code> instances.
</p>

<div class="listing">depth_feed_connection.h: DepthFeedSession class declaration</div>
<pre class="code">
namespace liquibook { namespace examples {
  typedef boost::shared_ptr<QuickFAST::WorkingBuffer> WorkingBufferPtr;
  typedef std::deque<WorkingBufferPtr> WorkingBuffers;
  typedef boost::array<unsigned char, 128> Buffer;
  typedef boost::shared_ptr<Buffer> BufferPtr;
  typedef boost::function<bool (BufferPtr, size_t)> MessageHandler;
  typedef boost::function<void ()> ResetHandler;
  typedef boost::function<void (const boost::system::error_code& error,
                                std::size_t bytes_transferred)> SendHandler;
  typedef boost::function<void (const boost::system::error_code& error,
                                std::size_t bytes_transferred)> RecvHandler;

  class DepthFeedConnection;

  // Session between a publisher and one subscriber
  class DepthFeedSession : boost::noncopyable {
  public:
    DepthFeedSession(boost::asio::io_service&amp; ios,
                     DepthFeedConnection* connection,
                     QuickFAST::Codecs::TemplateRegistryPtr&amp; templates);
</pre>

<p>
  In addition to declaring some typedefs and a forward declaration, this
  snippet of code declares <code>DepthFeedSession</code>'s  constructor.  This
  constructor accepts (1) an IO Service object through which it can create a
  TCP socket, (2) the <code>DepthFeedConnection</code> and (3) the parsed 
  QuickFAST templates, so it can encode outgoing messages.
</p>

<p>
  Next are an accessor and setter for the session's connection status, and an
  accessor for the session's TCP socket:
</p>

<div class="listing">depth_feed_connection.h: DepthFeedSession class declaration, continued</div>
<pre class="code">
    // Is this session connected?
    bool connected() const { return connected_; }

    // Mark this session as connected
    void set_connected() { connected_ = true; }

    // Get the socket for this session
    boost::asio::ip::tcp::socket&amp; socket() { return socket_; }
</pre>

<p>
  The real meat of the class is next, where messages are sent to the
  subscriber, in <code>send_trade()</code> for trades, and both
  <code>send_incr_update()</code>and <code>send_full_update()</code> for
  depth updates.
</p>

<div class="listing">depth_feed_connection.h: DepthFeedSession class declaration, continued</div>
<pre class="code">
    // Send a trade messsage to all clients
    void send_trade(QuickFAST::Messages::FieldSet& message);

    // Send an incremental update - if this client has handled this symbol
    //   return true if handled
    bool send_incr_update(const std::string& symbol,
                          QuickFAST::Messages::FieldSet& message);

    // Send a full update - if the client has not yet received for this symbol
    void send_full_update(const std::string& symbol,
                          QuickFAST::Messages::FieldSet& message);
</pre>

<p>
  The reader may wonder why there are two methods for sending depth updates.  
  This is because the publisher produces an incremental feed for depth updates,
  allowing only the changed depth levels to be published to the subscriber, 
  rather than all 5 levels of depth.  This is particularly effective because of
  the nature of this data - most changes to depth are at the top one or two
  levels of the limit order book.
</p>

<p>
  Note that the trade feed is not an incremental feed - each trade stands on
  its own.  This could be different if the trade carried accumulating values,
  such as volume for the day, or average traded price.  In this case, an
  incremental feed may be suitable for trades as well.  
</p>

<p>
  For the depth feed, two update methods are provided, for sending an 
  incremental update, and for sending a full update.  Even though the depth
  feed is an incremental feed, it is necessary to send full updates on
  occasion.  A full update is required, for example, when the subscriber does
  not yet have the depth state for a security, such as when it first connects
  to the publisher.
</p>

<p>
  The <code>DepthFeedSession</code> class includes a number of private members:
</p>

<div class="listing">depth_feed_connection.h: DepthFeedSession class declaration, continued</div>
<pre class="code">
  private:       
    bool connected_;
    uint64_t seq_num_;

    boost::asio::io_service&amp; ios_;
    boost::asio::ip::tcp::socket socket_;
</pre>

<p>
  The first member indicates the connection status of the session.  Next, is a sequence number counter for the session.  This is followed by two low-level ASIO members: a reference to the IO service, and a socket for the session.
</p>

<p>
  This is followed by a pointer to the one <code>DepthFeedConnection</code> of
  the publisher, and the session's QuickFAST encoder:
</p>

<div class="listing">depth_feed_connection.h: DepthFeedSession class declaration, continued</div>
<pre class="code">
    DepthFeedConnection* connection_;
    QuickFAST::Codecs::Encoder encoder_;
</pre>

<p>
  Next, is a <code>set</code> to track which securities have had depth
  information published, in order to track which securities still need a full
  update:
</p>

<div class="listing">depth_feed_connection.h: DepthFeedSession class declaration, continued</div>
<pre class="code">
    typedef std::set&lt;std::string&gt; StringSet;
    StringSet sent_symbols_;
</pre>

<p>
  Next are two static members that store the template IDS for the trade and
  depth messages:
</p>

<div class="listing">depth_feed_connection.h: DepthFeedSession class declaration, continued</div>
<pre class="code">
    static QuickFAST::template_id_t TID_TRADE_MESSAGE;
    static QuickFAST::template_id_t TID_DEPTH_MESSAGE;
</pre>

<p>
  Finally, there are two private methods, to set the sequence number on a 
  message, and to handle a sent message:
</p>

<div class="listing">depth_feed_connection.h: DepthFeedSession class declaration, continued</div>
<pre class="code">
    void set_sequence_num(QuickFAST::Messages::FieldSet&amp; message);

    void on_send(WorkingBufferPtr wb,
                 const boost::system::error_code&amp; error,
                 std::size_t bytes_transferred);
  };
</pre>

<p>
  The <code>DepthFeedSession</code> implementation begins with the declarations
  of its two static members, <code>TID_TRADE_MESSAGE</code> and
  <code>TID_DEPTH_MESSAGE</code>.
</p>

<div class="listing">depth_feed_connection.cpp: DepthFeedSession class implementation</div>
<pre class="code">
using namespace boost::asio::ip;

namespace liquibook { namespace examples {

QuickFAST::template_id_t DepthFeedSession::TID_TRADE_MESSAGE(1);
QuickFAST::template_id_t DepthFeedSession::TID_DEPTH_MESSAGE(2);
</pre>

<p>
  Next is the class constructor, which sets the connected status to false, sets
  the session sequence number to 0, initializes the socket, saves the IO
  service and connection, and provides the templates to the QuickFAST encoder:
</p>

<div class="listing">depth_feed_connection.cpp: DepthFeedSession class implementation, continued</div>
<pre class="code">
DepthFeedSession::DepthFeedSession(
    boost::asio::io_service&amp; ios,
    DepthFeedConnection* connection,
    QuickFAST::Codecs::TemplateRegistryPtr&amp; templates)
: connected_(false),
  seq_num_(0),
  ios_(ios),
  socket_(ios),
  connection_(connection),
  encoder_(templates)
{
}
</pre>

<p>
  The next method sends a trade to the subscriber:
</p>

<div class="listing">depth_feed_connection.cpp: DepthFeedSession class implementation, continued</div>
<pre class="code">
void
DepthFeedSession::send_trade(QuickFAST::Messages::FieldSet&amp; message)
{
  // Add or update sequence number in message
  set_sequence_num(message);

  std::cout &amp;&amp; "sending trade message with " &amp;&amp; message.size() &amp;&amp; " fields" &amp;&amp; std::endl;

  QuickFAST::Codecs::DataDestination dest;
  encoder_.encodeMessage(dest, TID_TRADE_MESSAGE, message);
  WorkingBufferPtr wb = connection_-&gt;reserve_send_buffer();
  dest.toWorkingBuffer(*wb);

  // Perform the send
  SendHandler send_handler = boost::bind(&amp;DepthFeedSession::on_send,
                                         this, wb, _1, _2);
  boost::asio::const_buffers_1 buffer(
      boost::asio::buffer(wb-&gt;begin(), wb-&gt;size()));
  socket_.async_send(buffer, 0, send_handler);
}
</pre>

<p>
  The <code>send_trade()</code> accepts an argument, reference to type 
  <code>QuickFAST::Messages::FieldSet</code>, which contains the method in a
  protocol-neutral format.  The method starts by setting the sequence number
  of the message by passing the field set to  <code>set_sequence_num()</code>.
  The caller is responsible for filling out the other fields of the message, 
  while the session sets the sequence number.
</p>

<p>
  Next, the method declares a data destination variable, which holds an
  encoded message.  This message is then encoded into the destination by the
  QuickFAST encoder, using the template ID for trade messages.
</p>

<p>
  Next a QuickFAST working buffer is reserved for sending the message, and the
  encoded message is serialized into the buffer.  To send the message, the
  method first creates a send handler for the buffer, so that
  <code>DepthFeedSession::on_send</code> is called after the send.  Next, it
  sets up an ASIO buffer to send the working buffer's contents, and calls the
  socket's <code>async_send()</code> method.
</p>

<p>
  The <code>send_incr_update</code> method is similar, with some key
  differences:
</p>

<div class="listing">depth_feed_connection.cpp: DepthFeedSession class implementation, continued</div>
<pre class="code">
bool
DepthFeedSession::send_incr_update(const std::string&amp; symbol,
                                   QuickFAST::Messages::FieldSet&amp; message)
{
  bool sent = false;
  // If the session has been started for this symbol
  if (sent_symbols_.find(symbol) != sent_symbols_.end()) {
    QuickFAST::Codecs::DataDestination dest;
    // Add or update sequence number in message
    set_sequence_num(message);
    encoder_.encodeMessage(dest, TID_DEPTH_MESSAGE, message);
    WorkingBufferPtr wb = connection_-&gt;reserve_send_buffer();
    dest.toWorkingBuffer(*wb);
    SendHandler send_handler = boost::bind(&amp;DepthFeedSession::on_send,
                                           this, wb, _1, _2);
    boost::asio::const_buffers_1 buffer(
        boost::asio::buffer(wb-&gt;begin(), wb-&gt;size()));
    socket_.async_send(buffer, 0, send_handler);
    sent = true;
  }
  return sent;
}
</pre>

<p>
  First, the reader will notice that <code>send_incr_update()</code> returns a
  <code>bool</code> value.  This value indicates whether the message was sent
  or not.  If not, the caller is responsible for calling
  <code>send_full_update()</code> with the message.
</p>

<p>
  The method also checks the <code>sent_symbols_</code> for the symbol of the 
  message (provided as an argument) to ensure the security has been sent to the
  subscriber previously.  Should this be true, execution is similar to that of
  <code>send_trade()</code>, where the sequence number is set, the message is
  encoded to a data destination, a working buffer is reserved, the destination
  serialized to the working buffer, and the working buffer contents is sent.
</p>

</p>
  The <code>send_full_update()</code> method is similar to
  <code>send_incr_update</code>, without the return value.  In this case, the
  method updates the <code>sent_symbols_</code> map to be true.  As a form of
  protection, it does not send the full update if the map already had a value
  for that security.
<p>

<div class="listing">depth_feed_connection.cpp: DepthFeedSession class implementation, continued</div>
<pre class="code">
void
DepthFeedSession::send_full_update(const std::string&amp; symbol,
                                   QuickFAST::Messages::FieldSet&amp; message)
{
  // Mark this symbols as sent
  std::pair<StringSet::iterator, bool> result = sent_symbols_.insert(symbol);

  // If this symbol is new for the session
  if (result.second) {
    QuickFAST::Codecs::DataDestination dest;
    // Add or update sequence number in message
    set_sequence_num(message);
    encoder_.encodeMessage(dest, TID_DEPTH_MESSAGE, message);
    WorkingBufferPtr wb = connection_-&gt;reserve_send_buffer();
    dest.toWorkingBuffer(*wb);

    // Perform the send
    SendHandler send_handler = boost::bind(&amp;DepthFeedSession::on_send,
                                           this, wb, _1, _2);
    boost::asio::const_buffers_1 buffer(
        boost::asio::buffer(wb-&gt;begin(), wb-&gt;size()));
    socket_.async_send(buffer, 0, send_handler);
  }
}
</pre>

<p>
  The first private method, <code>set_sequence_num()</code> is used to set 
  the sequence number field in the outgoing message.  The sequence number is 
  unique to each session, so it must be encoded for each copy of the outbound
  message:
</p>

<div class="listing">depth_feed_connection.cpp: DepthFeedSession class implementation, continued</div>
<pre class="code">
void
DepthFeedSession::set_sequence_num(QuickFAST::Messages::FieldSet&amp; message)
{
  // Create the field
  QuickFAST::Messages::FieldCPtr value =
      QuickFAST::Messages::FieldUInt32::create(++seq_num_);
  // Update the sequence number
  if (!message.replaceField(TemplateConsumer::id_seq_num_, value)) {
    // Not found, add the sequence number
    message.addField(TemplateConsumer::id_seq_num_, value);
  }
}
</pre>

<p>
  The method begins by creating a QUICKFAST field for the incremented sequence
  number.  Since this code resides within a session, there is a copy of the
  member variable <code>seq_num_</code> for each subscriber.  The first message
  will have a sequence number of 1.
</p>

<p>
  The method attempts to update the sequence number field in the message, and 
  if the sequence number is not yet present in the message, the field is added.
  Using this technique, the message can be built once, and then have the 
  sequence number customized for each subscriber, without having to build the
  message from scratch for each subscriber.
</p>

<p>
  The other private method, <code>on_send()</code> handles a sent message, 
  logging any error code, and calling the <code>DepthFeedConnection</code>
  version of the method:
</p>

<div class="listing">depth_feed_connection.cpp: DepthFeedSession class implementation, continued</div>
<pre class="code">
void
DepthFeedSession::on_send(WorkingBufferPtr wb,
                          const boost::system::error_code&amp; error,
                          std::size_t bytes_transferred)
{
  if (error) {
    std::cout &lt;&lt; "Error " &lt;&lt; error &lt;&lt; " sending message" &lt;&lt; std::endl;
    connected_ = false;
  }

  // Keep buffer for later
  connection_-&gt;on_send(wb, error, bytes_transferred);
}

</pre>

<p>
  The <code>DepthFeedConnection</code> class is used by both the publisher and
  the subscriber.  It has a constructor that accepts the command line
  arguments:
</p>

<div class="listing">depth_feed_connection.h: DepthFeedConnection class declaration</div>
<pre class="code">
  typedef boost::shared_ptr<DepthFeedSession> SessionPtr;

  class DepthFeedConnection : boost::noncopyable {
  public:
    DepthFeedConnection(int argc, const char* argv[]);
</pre>

<p>
  Next are an accessor for the templates parsed by the connection, and two
  methods for connecting the publisher and subscribers.  The publisher calls
  <code>accept()</code> and the subscriber calls <code>connect()</code>:
</p>

<div class="listing">depth_feed_connection.h: DepthFeedConnection class declaration, continued</div>
<pre class="code">
    // Get the template registry
    const QuickFAST::Codecs::TemplateRegistryPtr&amp;
          get_templates() { return templates_; }

    // Connect to publisher
    void connect();

    // Accept connection from subscriber
    void accept();
</pre>

<p>
  The next method, <code>run()</code> issues all the ASIO callbacks.  Both the
  publisher and subscriber must cal <code>run()</code>.
</p>

<div class="listing">depth_feed_connection.h: DepthFeedConnection class declaration, continued</div>
<pre class="code">
    // Let the IO service run
    void run();
</pre>

<p>
  The next two methods set up handlers for events:
</p>

<div class="listing">depth_feed_connection.h: DepthFeedConnection class declaration, continued</div>
<pre class="code">
    // Set a callback to handle a message
    void set_message_handler(MessageHandler msg_handler);

    // Set a callback to handle a reset connection
    void set_reset_handler(ResetHandler reset_handler);
</pre>

<p>
  The first, <code>set_message_handler()</code>, handles incoming messages in
  the subscriber.  The second, <code>set_reset_handler()</code> within the
  publisher handles disconnects within the subscriber to the publisher.
</p>

<p>
  The next two methods are for buffer management:
</p>

<div class="listing">depth_feed_connection.h: DepthFeedConnection class declaration, continued</div>
<pre class="code">
    // Reserve a buffer for receiving a message
    BufferPtr reserve_recv_buffer();

    // Reserve a buffer for sending a message
    WorkingBufferPtr reserve_send_buffer();
</pre>

<p>
  Rather than constantly allocating and deallocating buffers, the publisher
  and subscriber have a pool of buffers ready to be used again and again.
  The publisher and subscriber will reserve these buffers from
  <code>DepthFeedConnection</code>, bind them to an ASIO asynchronous callback,
  and then restore the buffers back to the <code>DepthFeedConnection</code>. 
</p>

<p>
  The first, <code>reserve_recv_buffer()</code> is called by the subscriber for
  a buffer to receive a message.  Likewise, <code>reserve_send_buffer()</code>
  reserves a buffer to serialize an encoded QuickFAST message, for the
  publisher to send to the subscriber.
</p>

<p>
  Next, are three methods for the publisher to send a message to each
  subscriber:
</p>

<div class="listing">depth_feed_connection.h: DepthFeedConnection class declaration, continued</div>
<pre class="code">
    // Send a trade messsage to all clients
    void send_trade(QuickFAST::Messages::FieldSet&amp; message);

    // Send an incremental update
    //   return true if all sessions could handle an incremental update
    bool send_incr_update(const std::string&amp; symbol, 
                          QuickFAST::Messages::FieldSet&amp; message);

    // Send a full update to those which have not yet received for this symbol
    void send_full_update(const std::string&amp; symbol, 
                          QuickFAST::Messages::FieldSet&amp; message);
</pre>

<p>
  These three methods call the corresponding method on
  <code>DepthFeedSession</code> for each subscriber.  The public interface ends
  with a number of event handlers:
</p>

<div class="listing">depth_feed_connection.h: DepthFeedConnection class declaration, continued</div>
<pre class="code">
    // Handle a connection
    void on_connect(const boost::system::error_code&amp; error);

    // Handle an accepted connection
    void on_accept(SessionPtr session,
                   const boost::system::error_code&amp; error);

    // Handle a received message
    void on_receive(BufferPtr bp, 
                    const boost::system::error_code&amp; error,
                    std::size_t bytes_transferred);
    // Handle a sent message
    void on_send(WorkingBufferPtr wb,
                 const boost::system::error_code&amp; error,
                 std::size_t bytes_transferred);
</pre>

<p>
  These methods handle, the result of a subscriber connecting to the publisher,
  a publisher's accepted connection from a subscriber, a message received from
  the publisher, and a message sent to the subscriber, respectively.
</p>

<p>
  The private members of <code>DepthFeedConnection</code> start with some
  configuration date: the name of the file containing the FAST templates, the
  host for the subscriber to connect to, and the connection port.  These are
  followed by the event handlers, the parsed QuickFAST templates, buffer pools,
  the <code>DepthFeedSession</code> instances, and some boost ASIO helpers.
</p>

<p>
  The private section ends with some methods, to issue a read, and parse the
  command line arguments:
</p>

<div class="listing">depth_feed_connection.h: DepthFeedConnection class declaration, continued</div>
<pre class="code">
  private:
    typedef std::deque&lt;BufferPtr&gt; Buffers;
    typedef std::vector&lt;SessionPtr&gt; Sessions;
    const char* template_filename_;
    const char* host_;
    int port_;
    MessageHandler msg_handler_;
    ResetHandler reset_handler_;
    QuickFAST::Codecs::TemplateRegistryPtr templates_;

    Buffers        unused_recv_buffers_;
    WorkingBuffers unused_send_buffers_;
    Sessions sessions_;
    boost::shared_ptr&lt;boost::asio::ip::tcp::acceptor&gt; acceptor_;
    boost::asio::io_service ios_;
    boost::asio::ip::tcp::socket socket_;
    boost::shared_ptr&lt;boost::asio::io_service::work&gt; work_ptr_;

    void issue_read();
    static const char* template_file_from_args(int argc, const char* argv[]);
    static const char* host_from_args(int argc, const char* argv[]);
    static int port_from_args(int argc, const char* argv[]);
  };
} } // End namespace
</pre>

<p>
  The <code>DepthFeedConnection</code> constructor derives the configuration
  variables by passing the command-line arguments to the 
  <code>*_from_args()</code> methods:
</p>

<div class="listing">depth_feed_connection.cpp: DepthFeedConnection class implementation</div>
<pre class="code">
DepthFeedConnection::DepthFeedConnection(int argc, const char* argv[])
: template_filename_(template_file_from_args(argc, argv)),
  host_(host_from_args(argc, argv)),
  port_(port_from_args(argc, argv)),
  templates_(TemplateConsumer::parse_templates(template_filename_)),
  socket_(ios_)
{
}
</pre>

<p>
  It also invokes <code>parse_templates()</code> to parse the QuickFAST
  templates, and initializes the subscriber socket.  Next, 
  <code>connect()</code> does a standard ASIO asynchronous connection, using
  <code>on_connect()</code> as its callback:
</p>

<div class="listing">depth_feed_connection.cpp: DepthFeedConnection class implementation, continued</div>
<pre class="code">
void
DepthFeedConnection::connect()
{
  std::cout &lt;&lt; "Connecting to feed" &lt;&lt; std::endl;
  tcp::endpoint endpoint(address::from_string(host_), port_);
  socket_.async_connect(endpoint, boost::bind(&amp;DepthFeedConnection::on_connect,
                                              this, _1));
}
</pre>

<p>
  The <code>accept()</code> method implements the publisher's side of ASIO
  connectivity.  The first time <code>accept()</code> id called a TCP acceptor
  is created and initialized.  For each call, a <code>DepthFeedSession</code>
  is created, and an asynchronous accept is started, using to
  <code>on_accept()</code> as its callback:
</p>

<div class="listing">depth_feed_connection.cpp: DepthFeedConnection class implementation, continued</div>
<pre class="code">
void
DepthFeedConnection::accept()
{
  if (!acceptor_) {
    acceptor_.reset(new tcp::acceptor(ios_));
    tcp::endpoint endpoint(tcp::v4(), port_);
    acceptor_-&gt;open(endpoint.protocol());
    boost::system::error_code ec;
    acceptor_-&gt;set_option(boost::asio::socket_base::reuse_address(true), ec);
    acceptor_-&gt;bind(endpoint);
    acceptor_-&gt;listen();
  }
  SessionPtr session(new DepthFeedSession(ios_, this, templates_));
  acceptor_-&gt;async_accept(
      session-&gt;socket(), 
      boost::bind(&amp;DepthFeedConnection::on_accept, this, session, _1));
}
</pre>

<p>
  The <code>run()</code> method invokes the <code>run()</code> method on the IO
  service object.  Usually <code>run()</code> will exit when there are no more
  callbacks to make.  Using the <code>io_service::work</code> object ensures
  <code>run()</code> does not exit:
</p>

<div class="listing">depth_feed_connection.cpp: DepthFeedConnection class implementation, continued</div>
<pre class="code">
void
DepthFeedConnection::run()
{
std::cout &lt;&lt; "DepthFeedConnection::run()" &lt;&lt; std::endl;
  // Keep on running
  work_ptr_.reset(new boost::asio::io_service::work(ios_));
  ios_.run();
}
</pre>

<p>
  The next two methods simply save off the two message handlers:
</p>

<div class="listing">depth_feed_connection.cpp: DepthFeedConnection class implementation, continued</div>
<pre class="code">

void
DepthFeedConnection::set_message_handler(MessageHandler handler)
{
  msg_handler_ = handler;
}

void
DepthFeedConnection::set_reset_handler(ResetHandler handler)
{
  reset_handler_ = handler;
}
</pre>

<p>
  As stated above, the <code>DepthFeedConnection</code> uses buffer pools to
  reduce heap allocations and deallocations.  The following two methods 
  provide unused buffers to the caller:
</p>

<div class="listing">depth_feed_connection.cpp: DepthFeedConnection class implementation</div>
<pre class="code">
BufferPtr
DepthFeedConnection::reserve_recv_buffer()
{
  if (unused_recv_buffers_.empty()) {
    return BufferPtr(new Buffer());
  } else {
    BufferPtr bp = unused_recv_buffers_.front();
    unused_recv_buffers_.pop_front();
    return bp;
  }
}

WorkingBufferPtr
DepthFeedConnection::reserve_send_buffer()
{
  if (unused_send_buffers_.empty()) {
    return WorkingBufferPtr(new QuickFAST::WorkingBuffer());
  } else {
    WorkingBufferPtr wb = unused_send_buffers_.front();
    unused_send_buffers_.pop_front();
    return wb;
  }
}
</pre>

<p>
  In both methods, an unused buffer is returned, and one is created if none are
  available.
</p>

<p>
  The <code>send_trade()</code>, <code>send_incr_update()</code> and
  <code>send_full_update()</code> methods send a message to all subscribers by
  iterating through the sessions and invoking the corresponding method on each:
</p>

<div class="listing">depth_feed_connection.cpp: DepthFeedConnection class implementation, continued</div>
<pre class="code">
void
DepthFeedConnection::send_trade(QuickFAST::Messages::FieldSet&amp; message)
{
  // For each session
  Sessions::iterator session;
  for (session = sessions_.begin(); session != sessions_.end(); ) {
    // If the session is connected
    if ((*session)-&gt;connected()) {
      // conditionally send on that session
      (*session)-&gt;send_trade(message);
      ++session;
    } else {
      // Remove the session
      session = sessions_.erase(session);
    }
  }
}

bool
DepthFeedConnection::send_incr_update(const std::string&amp; symbol,
                                      QuickFAST::Messages::FieldSet&amp; message)
{
  bool none_new = true;
  // For each session
  Sessions::iterator session;
  for (session = sessions_.begin(); session != sessions_.end(); ) {
    // If the session is connected
    if ((*session)-&gt;connected()) {
      // send on that session
      if (!(*session)-&gt;send_incr_update(symbol, message)) {
        none_new = false;
      }
      ++session;
    } else {
      // Remove the session
      session = sessions_.erase(session);
    }
  }
  return none_new;
}

void
DepthFeedConnection::send_full_update(const std::string&amp; symbol,
                                      QuickFAST::Messages::FieldSet&amp; message)
{
  // For each session
  Sessions::iterator session;
  for (session = sessions_.begin(); session != sessions_.end(); ) {
    // If the session is connected
    if ((*session)-&gt;connected()) {
      // conditionally send on that session
      (*session)-&gt;send_full_update(symbol, message);
      ++session;
    } else {
      // Remove the session
      session = sessions_.erase(session);
    }
  }
}
</pre>

<p>
  In any of the cases, if the session is no longer connected, the session
  is removed.  There is one important difference in
  <code>send_incr_update()</code> - the method's return value indicates if any
  of the sessions gave back a return value of false.
</p>

<p>
  The next method handles a connection within the subscriber:
</p>

<div class="listing">depth_feed_connection.cpp: DepthFeedConnection class implementation, continued</div>
<pre class="code">

void
DepthFeedConnection::on_connect(const boost::system::error_code&amp; error)
{
  if (!error) {
    std::cout &lt;&lt; "connected to feed" &lt;&lt; std::endl;
    reset_handler_();
    issue_read();
  } else {
    std::cout &lt;&lt; "on_connect, error=" &lt;&lt; error &lt;&lt; std::endl;
    socket_.close();
    sleep(3);
    // Try again
    connect();
  }
}
</pre>

<p>
  When <code>on_connect()</code> is called, the method tries again, in case of
  failure, or calls the reset handler and then begins to read on the socket, in
  case of success.
</p>
<p>
  The next handler is the counterpart in the publisher:
</p>

<div class="listing">depth_feed_connection.cpp: DepthFeedConnection class implementation, continued</div>
<pre class="code">
void
DepthFeedConnection::on_accept(SessionPtr session,
                               const boost::system::error_code&amp; error)
{
  if (!error) {
    std::cout &lt;&lt; "accepted client connection" &lt;&lt; std::endl;
    sessions_.push_back(session);
    session-&gt;set_connected();
  } else {
    std::cout &lt;&lt; "on_accept, error=" &lt;&lt; error &lt;&lt; std::endl;
    session.reset();
    sleep(2);
  }
  // accept again
  accept();
}
</pre>

<p>
  In the case of <code>on_accept()</code>, the publisher always restarts the
  accept cycle - so that another subscriber may connect.  The subscriber, on
  the other hand, only wants one connection.
</p>
<p>
  In the case of a successful connection, the publisher saves off the session, 
  and marks it as connected, so that messages may be sent to it.  If there is
  failure, the session is deleted (by resetting the shared pointer) and the
  publisher waits momentarily before starting again.
</p>
<p>
  The next event handler handles a message received by the subscriber:
</p>

<div class="listing">depth_feed_connection.cpp: DepthFeedConnection class implementation, continued</div>
<pre class="code">
void
DepthFeedConnection::on_receive(BufferPtr bp,
                                const boost::system::error_code&amp; error,
                                std::size_t bytes_transferred)
{
  if (!error) {
    // Next read
    issue_read();

    // Handle the buffer
    if (!msg_handler_(bp, bytes_transferred)) {
      socket_.close();
    }
  } else {
    std::cout &lt;&lt; "Error " &lt;&lt; error &lt;&lt; " receiving message" &lt;&lt; std::endl;
    socket_.close();
    sleep(3);
    connect();
  }
  // Restore buffer
  unused_recv_buffers_.push_back(bp);
}
</pre>

<p>
  When the receive is handled, if the read was successful, the subscriber
  first issues a new read, and then calls the receive handler, which parses
  the message.  Should this parsing fail, the socket will close (and the next
  read will then fail).
</p>
<p>
  Should the read fail, the socket is closed, and the connection cycle starts
  anew.  In all cases, the buffer is restored back to the pool.
</p>
<p>
  The next handler is called after a send of a message to the subscriber:
</p>

<div class="listing">depth_feed_connection.cpp: DepthFeedConnection class implementation, continued</div>
<pre class="code">
void
DepthFeedConnection::on_send(WorkingBufferPtr wb,
                             const boost::system::error_code&amp; error,
                             std::size_t bytes_transferred)
{
  // Keep buffer for later
  unused_send_buffers_.push_back(wb);
}
</pre>

<p>
  Recall that this method is called from within
  <code>DepthFeedSession::on_send()</code> which has already examined the
  error code.  What remains is to restore the buffer back to the pool.
</p>
<p>
  The next method starts an ASIO read on the subscriber's socket:
</p>

<div class="listing">depth_feed_connection.cpp: DepthFeedConnection class implementation, continued</div>
<pre class="code">

void
DepthFeedConnection::issue_read()
{
  BufferPtr bp = reserve_recv_buffer();
  RecvHandler recv_handler = boost::bind(&amp;DepthFeedConnection::on_receive, 
                                         this, bp, _1, _2);
  boost::asio::mutable_buffers_1 buffer(
      boost::asio::buffer(*bp, bp-&gt;size()));
  socket_.async_receive(buffer, 0, recv_handler);
}
</pre>

<p>
  <code>issue_read()</code> reserves a buffer, binds the buffer to the 
  <code>on_receive()</code> method, and issues the asynchronous read.
</p>
<p>
  The next three methods look through the command line arguments for
  configuration variables:
</p>

<div class="listing">depth_feed_connection.cpp: DepthFeedConnection class implementation, continued</div>
<pre class="code">
const char*
DepthFeedConnection::template_file_from_args(int argc, const char* argv[])
{
  bool next_is_name = false;
  for (int i = 0; i &lt; argc; ++i) {
    if (next_is_name) {
      return argv[i];
    } else if (strcmp(argv[i], "-t") == 0) {
      next_is_name = true;
    }
  }
  return "./templates/depth.xml";
}

const char*
DepthFeedConnection::host_from_args(int argc, const char* argv[])
{
  bool next_is_host = false;
  for (int i = 0; i &lt; argc; ++i) {
    if (next_is_host) {
      return argv[i];
    } else if (strcmp(argv[i], "-h") == 0) {
      next_is_host = true;
    }
  }
  return "127.0.0.1";
}

int
DepthFeedConnection::port_from_args(int argc, const char* argv[])
{
  bool next_is_port = false;
  for (int i = 0; i &lt; argc; ++i) {
    if (next_is_port) {
      return atoi(argv[i]);
    } else if (strcmp(argv[i], "-p") == 0) {
      next_is_port = true;
    }
  }
  return 10003;
}

} } // End namespace
</pre>
<p>
  The first method looks for a -t followed by a template file name.  The 
  second looks foe a -h followed by a host name.  The last one looks for a -p
  followed by a port number.  All three return default values.
</p>

<h3>Handling Liquibook Events</h3>
<p>
  At this point, the publisher is able to accept subscriber connections and
  route QuickFAST messages to the subscriber.  What is missing is handling
  Liquibook events, and creating the QuickFAST messages to send.  As shown in 
  <a href="#figure2">figure 2</a>, the publisher needs a properly-typed
  <code>TradeListener</code> for trade events, and a properly-typed
  <code>DepthListener</code> for depth events.
</p>

<p>
  The <code>TradeListener</code> interface has a single method to implement:
</p>

<div class="listing">trade_listener.h: TradeListener interface declaration</div>
<pre class="code">
namespace liquibook { namespace book {

/// @brief listener of trade events.   Implement to build a trade feed.
template &lt;class OrderBook &gt;
class TradeListener {
public:
  /// @brief callback for a trade
  /// @param book the order book of the fill (not defined whether this is before
  ///      or after fill)
  /// @param qty the quantity of this fill
  /// @param cost the cost of this fill (qty * price)
  virtual void on_trade(const OrderBook* book,
                        Quantity qty,
                        Cost cost) = 0;
};

} }
</pre>

<p>
  Likewise, the <code>DepthListener</code> interface has a single method to
  implement:
</p>

<div class="listing">trade_listener.h: TradeListener interface declaration</div>
<pre class="code">
namespace liquibook { namespace book {

/// @brief listener of depth events.  Implement to build an aggregate depth 
/// feed.
template &lt;class OrderBook &gt;
class DepthListener {
public:
  /// @brief callback for change in tracked aggregated depth
  virtual void on_depth_change(
      const OrderBook* book,
      const typename OrderBook::DepthTracker* depth) = 0;
};

} }
</pre>

<p>
  The publisher implements these interfaces in the
  <code>DepthFeedPublisher</code> class:
</p>

<div class="listing">depth_feed_publisher.h: DepthFeedPublisher class declaration</div>
<pre class="code">
namespace liquibook { namespace examples {

class DepthFeedPublisher : public ExampleOrderBook::TypedDepthListener,
                           public ExampleOrderBook::TypedTradeListener,
                           public TemplateConsumer {
</pre>

<p>
  The <code>DepthFeedPublisher</code> class implements both the 
  <code>ExampleOrderBook::TypedDepthListener</code> and the 
  <code>ExampleOrderBook::TypedTradeListener</code> interfaces, which bind the
  <code>book::DepthListener</code> and the <code>book::TradeListener</code> 
  templates to those used in <code>ExampleOrderBook</code>. 
  <code>DepthFeedPublisher</code> also inherits from
  <code>TemplateConsumer</code>, but this is simply a convenience (to reduce
  the need for name qualifiers), as <code>TemplateConsumer</code>'s data and
  methods are entirely static.
</p>

<p>
  The <code>DepthFeedPublisher</code> has a simple constructor, for
  initializing its pointer.
</p>

<div class="listing">depth_feed_publisher.h: DepthFeedPublisher class declaration, continued</div>
<pre class="code">
public:
  DepthFeedPublisher();
</pre>

<p>
  The <code>DepthFeedPublisher</code> keeps a pointer to a
  <code>DepthFeedConnection</code>, and receives that pointer through a setter
  method:
</p>

<div class="listing">depth_feed_publisher.h: DepthFeedPublisher class declaration, continued</div>
<pre class="code">
  void set_connection(DepthFeedConnection* connection);
</pre>

<p>
  Next are the two event handlers to implement the two interfaces:
</p>

<div class="listing">depth_feed_publisher.h: DepthFeedPublisher class declaration, continued</div>
<pre class="code">
  virtual void on_trade(
      const book::OrderBook&lt;OrderPtr&gt;* order_book,
      book::Quantity qty,
      book::Cost cost);

  virtual void on_depth_change(
      const book::DepthOrderBook&lt;OrderPtr&gt;* order_book,
      const book::DepthOrderBook&lt;OrderPtr&gt;::DepthTracker* tracker);
</pre>

<p>
  The private section has a single member - the
  <code>DepthFeedConnection</code> pointer:
</p>

<div class="listing">depth_feed_publisher.h: DepthFeedPublisher class declaration, continued</div>
<pre class="code">
private:
  DepthFeedConnection* connection_;
</pre>

<p>
  Next are methods to build the feed messages:
</p>

<div class="listing">depth_feed_publisher.h: DepthFeedPublisher class declaration, continued</div>
<pre class="code">
  // Build an trade message
  void build_trade_message(
      QuickFAST::Messages::FieldSet&amp; message,
      const std::string&amp; symbol,
      book::Quantity qty,
      book::Cost cost);

  // Build an incremental depth message
  void build_depth_message(
      QuickFAST::Messages::FieldSet&amp; message,
      const std::string&amp; symbol,
      const book::DepthOrderBook&lt;OrderPtr&gt;::DepthTracker* tracker,
      bool full_message);
  void build_depth_level(
      QuickFAST::Messages::SequencePtr&amp; level_seq,
      const book::DepthLevel* level,
      int level_index);
</pre>

<p>
  And finally a method to generate a time stamp for the messages:
</p>

<div class="listing">depth_feed_publisher.h: DepthFeedPublisher class declaration, continued</div>
<pre class="code">
  uint32_t time_stamp();
};

} } // End namespace
</pre>

<p>
  The <code>DepthFeedPublisher</code> constructor initializes its connection
  pointer, which is set by a separate setter method:
</p>

<div class="listing">depth_feed_publisher.cpp: DepthFeedPublisher class implementation</div>
<pre class="code">
namespace liquibook { namespace examples { 

using namespace QuickFAST::Messages;

DepthFeedPublisher::DepthFeedPublisher()
: connection_(NULL)
{
}

void
DepthFeedPublisher::set_connection(DepthFeedConnection* connection)
{
  connection_ = connection;
}
</pre>

<p>
  The <code>DepthFeedPublisher</code>'s primary responsibility is to build 
  feed messages, in response to events.  The first event handler is
  <code>on_trade()</code>:
</p>

<div class="listing">depth_feed_publisher.cpp: DepthFeedPublisher class implementation, continued</div>
<pre class="code">

void
DepthFeedPublisher::on_trade(
    const book::OrderBook&lt;OrderPtr&gt;* order_book,
    book::Quantity qty,
    book::Cost cost)
{
  // Publish trade
  QuickFAST::Messages::FieldSet message(20);
  const ExampleOrderBook* exob = 
          dynamic_cast&lt;const ExampleOrderBook*&gt;(order_book);
  std::cout &lt;&lt; "Got trade for " &lt;&lt; exob-&gt;symbol() 
            &lt;&lt; " qty " &lt;&lt; qty
            &lt;&lt; " cost " &lt;&lt; cost &lt;&lt; std::endl;
  build_trade_message(message, exob-&gt;symbol(), qty, cost);
  connection_-&gt;send_trade(message);
}
</pre>

<p>
  <code>on_trade()</code> starts by casting the order book to the derived type.
  The reader will recall that the derived order book is used to store the
  symbol of the order book.  The method then builds the trade message,
  through a call to <code>build_trade_message()</code>, and distributes the
  message through the <code>DepthFeedConnection</code>.
</p>

<p>
  The next event handler handles depth change events:
</p>

<div class="listing">depth_feed_publisher.cpp: DepthFeedPublisher class implementation, continued</div>
<pre class="code">
void
DepthFeedPublisher::on_depth_change(
    const book::DepthOrderBook&lt;OrderPtr&gt;* order_book,
    const book::DepthOrderBook&lt;OrderPtr&gt;::DepthTracker* tracker)
{
  // Publish changed levels of order book
  QuickFAST::Messages::FieldSet message(20);
  const ExampleOrderBook* exob = 
          dynamic_cast&lt;const ExampleOrderBook*&gt;(order_book);
  build_depth_message(message, exob-&gt;symbol(), tracker, false);
  if (!connection_-&gt;send_incr_update(exob-&gt;symbol(), message)) {
    // Publish all levels of order book
    QuickFAST::Messages::FieldSet full_message(20);
    build_depth_message(full_message, exob-&gt;symbol(), tracker, true);
    connection_-&gt;send_full_update(exob-&gt;symbol(), full_message);
  }
}
</pre>

<p>
  Similar to the trade event handler, the depth change event handler casts the
  order book to the derived class, builds a feed message, and sends it to the
  clients through the <code>DepthFeedConnection</code>.  What is different in
  <code>on_depth_change()</code> is that since the depth feed is incremental,
  as noted above, the clients must be known to be in a valid state for the
  security of the update to handle the next message.
</p>

<p>
  The reader will recall that the call to
  <code>DepthFeedConnection::send_incr_update()</code> returns true if and only
  if all of the clients could handle the message.  If not, false is returned, 
  and <code>on_depth_change()</code> builds and sends a full update instead.
  In the steady state, only the incremental message need by built.
</p>

<p>
  The <code>build_trade_message()</code> method is used by
  <code>on_trade()</code> to build a trade message:
</p>

<div class="listing">depth_feed_publisher.cpp: DepthFeedPublisher class implementation, continued</div>
<pre class="code">
void
DepthFeedPublisher::build_trade_message(
    QuickFAST::Messages::FieldSet&amp; message,
    const std::string&amp; symbol,
    book::Quantity qty,
    book::Cost cost)
{
  message.addField(id_timestamp_, FieldUInt32::create(time_stamp()));
  message.addField(id_symbol_, FieldString::create(symbol));
  message.addField(id_qty_, FieldUInt32::create(qty));
  message.addField(id_cost_, FieldUInt32::create(cost));
}
</pre>

<p>
  Building a QuickFAST message is relatively simple.  Each field is added to
  the message through the <code>addField()</code> call.  The sequence number,
  added later, is particular to a specific client.
</p>

<p>
  The next message builder, <code>build_depth_message()</code>, builds
  repeating sequences of data, and is more complex:
</p>

<div class="listing">depth_feed_publisher.cpp: DepthFeedPublisher class implementation, continued</div>
<pre class="code">
void
DepthFeedPublisher::build_depth_message(
    QuickFAST::Messages::FieldSet&amp; message,
    const std::string&amp; symbol,
    const book::DepthOrderBook&lt;OrderPtr&gt;::DepthTracker* tracker,
    bool full_message)
{
  size_t bid_count(0), ask_count(0);

  message.addField(id_timestamp_, FieldUInt32::create(time_stamp()));
  message.addField(id_symbol_, FieldString::create(symbol));

  // Build the changed levels
  book::ChangeId last_published_change = tracker-&gt;last_published_change();
  
  // Build changed bids
  {
    SequencePtr bids(new Sequence(id_bids_length_, 1));
    int index = 0;
    const book::DepthLevel* bid = tracker-&gt;bids();
    // Create sequence of bids
    while (true) {
      if (full_message || bid-&gt;changed_since(last_published_change)) {
        build_depth_level(bids, bid, index);
        ++bid_count;
      }
      ++index;
      if (bid == tracker-&gt;last_bid_level()) {
        break;
      } else {
        ++bid;
      }
    }
    message.addField(id_bids_, FieldSequence::create(bids));
  }

  // Build changed asks
  {
    SequencePtr asks(new Sequence(id_asks_length_, 1));
    int index = 0;
    const book::DepthLevel* ask = tracker-&gt;asks();
    // Create sequence of asks
    while (true) {
      if (full_message || ask-&gt;changed_since(last_published_change)) {
        build_depth_level(asks, ask, index);
        ++ask_count;
      }
      ++index;
      if (ask == tracker-&gt;last_ask_level()) {
        break;
      } else {
        ++ask;
      }
    }
    message.addField(id_asks_, FieldSequence::create(asks));
  }
  std::cout &lt;&lt; "Encoding " &lt;&lt; (full_message ? "full" : "incr")
            &lt;&lt; " depth message for symbol " &lt;&lt; symbol 
            &lt;&lt; " with " &lt;&lt; bid_count &lt;&lt; " bids, "
            &lt;&lt; ask_count &lt;&lt; " asks" &lt;&lt; std::endl;
}
</pre>

<p>
  This method starts like <code>add_trade()</code>, adding the timestamp and 
  symbol fields.  Next, two sequences are built, for the changed bid and ask
  levels.  To support an incremental feed, Liquibook tracks a change number
  (called <code>ChangeId</code>) on each depth level, and the ID of the last
  published change in the depth, which is set after the callback completes.
  To determine the levels to publish, the publisher needs to compare the last
  change of each level to the last published change of the depth as a whole.
</p>

<p>
  In <code>build_depth_message()</code>, a bid sequence is created, and an
  associated length is attached.  The changed bid levels are each added to the
  sequence through calls to <code>build_depth_level()</code>.  The iteration 
  logic here must be careful to not move beyond the last bid level.  Finally,
  the sequence is added to the message.  The same is done for changed ask
  levels.
</p>

<p>
  By comparison, <code>build_depth_level()</code> is simple:
</p>

<div class="listing">depth_feed_publisher.cpp: DepthFeedPublisher class implementation, continued</div>
<pre class="code">
void
DepthFeedPublisher::build_depth_level(
    QuickFAST::Messages::SequencePtr&amp; level_seq,
    const book::DepthLevel* level,
    int level_index)
{
  FieldSetPtr level_fields(new FieldSet(4));
  level_fields-&gt;addField(id_level_num_, FieldUInt8::create(level_index));
  level_fields-&gt;addField(id_order_count_, 
                         FieldUInt32::create(level-&gt;order_count()));
  level_fields-&gt;addField(id_price_,
                         FieldUInt32::create(level-&gt;price()));
  level_fields-&gt;addField(id_size_,
                         FieldUInt32::create(level-&gt;aggregate_qty()));
  level_seq-&gt;addEntry(level_fields);
}
</pre>

<p>
  This method shows how to add an entry to a QuickFAST sequence - by creating
  a <code>FieldSet</code>, adding fields to it, and adding the
  <code>FieldSet</code> to the sequence.
</p>

<p>
  The final method is a helper, to create an integer timestamp for a message:
</p>

<div class="listing">depth_feed_publisher.cpp: DepthFeedPublisher class implementation, continued</div>
<pre class="code">
uint32_t
DepthFeedPublisher::time_stamp()
{
  time_t now;
  time(&amp;now);
  return now;
}

} } // End namespace
</pre>

<h3>Publisher Initialization</h3>
<p>
  At this point the publisher has all the necessary parts to publish a trade
  feed and an incremental depth feed for the exchange.  The only things that
  remains are to create, initialize, and associate the various parts, and to
  generate the random orders.
</p>

<p>
  The exchange is initialized in the file <code>publisher_main.cpp</code>.
  The <code>main()</code> function starts by establishing the securities to
  "trade" in the exchange - in this case the NASDAQ 100 - taken from a
  snapshot in April 2013.  <code>main()</code> records the symbol and a base
  price in a structure:
</p>

<div class="listing">publisher_main.cpp: SecurityInfo structure</div>
<pre class="code">
struct SecurityInfo {
  std::string symbol;
  double ref_price;

  SecurityInfo(const char* sym, double price)
  : symbol(sym),
    ref_price(price)
  {
  }
};

typedef std::vector<SecurityInfo> SecurityVector;
</pre>

<p>
  The base price serves as a basis for generating random prices for the
  example exchange.  <code>main()</code> keeps this security information in a
  <code>SecurityVector</code>  and populates it in the 
  <code>create_securities()</code> method, later in <code>main()</code>.
</p>

<p>
  The <code>main()</code> function begins by creating the
  <code>DepthFeedConnection</code>, and uses Boost's thread library to 
  create a background thread to accept connections from subscribers:
</p>

<div class="listing">publisher_main.cpp: main() function</div>
<pre class="code">
int main(int argc, const char* argv[])
{
  // Feed connection
  examples::DepthFeedConnection connection(argc, argv);

  // Open connection in background thread
  connection.accept();
  boost::function<void ()> acceptor(
      boost::bind(&amp;examples::DepthFeedConnection::run, &amp;connection));
  boost::thread acceptor_thread(acceptor);
</pre>

<p>
  Next, the <code>DepthFeedPublisher</code> and the <code>Exchange</code> are
  created, and the<code>DepthFeedPublisher</code> is made aware of the 
  <code>DepthFeedConnection</code>:
</p>

<div class="listing">publisher_main.cpp: main() function, continued</div>
<pre class="code">
  // Create feed publisher
  examples::DepthFeedPublisher feed;
  feed.set_connection(&amp;connection);

  // Create exchange
  examples::Exchange exchange(&amp;feed, &amp;feed);
</pre>

<p>
  Note that the exchange constructor requires a <code>TradeListener</code> and
  a <code>DepthListener</code>, both of which the
  <code>DepthFeedPublisher</code> implements.
</p>

<p>
  Finally, the securities are created and used to populate the exchange, and
  orders are generated.
</p>

<div class="listing">publisher_main.cpp: main() function, continued</div>
<pre class="code">
  // Create securities
  SecurityVector securities;
  create_securities(securities);

  // Populate exchange with securities
  populate_exchange(exchange, securities);
  
  // Generate random orders
  generate_orders(exchange, securities);

  return 0;
}
</pre>

<p>
  The final function call, to <code>generate_orders()</code> is an infinite
  loop, so there is no need to worry about the <code>main()</code> method
  returning.
</p>

<p>
  The helper function <code>create_securities()</code> adds 100 securities to
  the <code>SecurityVector</code>:
</p>

<div class="listing">publisher_main.cpp: main() helper functions</div>
<pre class="code">
void
create_securities(SecurityVector&amp; securities) {
  securities.push_back(SecurityInfo("AAPL", 436.36));
  securities.push_back(SecurityInfo("ADBE", 45.06));
  securities.push_back(SecurityInfo("ADI", 43.93));

  // Repeated for 100 securities...
}
</pre>

</p>
  Due to the mercy of the author, the other 97 securities were omitted from
  this paper.  The next helper function, <code>populate_exchange()</code> adds
  these securities to the exchange:
<p>

<div class="listing">publisher_main.cpp: main() helper functions, continued</div>
<pre class="code">
void
populate_exchange(examples::Exchange&amp; exchange, const SecurityVector&amp; securities) {
  SecurityVector::const_iterator sec;
  for (sec = securities.begin(); sec != securities.end(); ++sec) {
    exchange.add_order_book(sec-&gt;symbol);
  }
}
</pre>

</p>
  The final helper function, <code>generate_orders()</code> creates random
  orders and adds them to the exchange:
<p>

<div class="listing">publisher_main.cpp: main() helper functions, continued</div>
<pre class="code">
void
generate_orders(examples::Exchange&amp; exchange, const SecurityVector&amp; securities) {
  time_t now;
  time(&amp;now);
  std::srand(now);

  size_t num_securities = securities.size();
  while (true) {
    // which security
    size_t index = std::rand() % num_securities;
    const SecurityInfo&amp; sec = securities[index];
    // side
    bool is_buy = std::rand() % 2;
    // price
    uint32_t price_base = sec.ref_price * 100;
    uint32_t delta_range = price_base / 50;  // +/- 2% of base
    int32_t delta = std::rand() % delta_range;
    delta -= (delta_range / 2);
    double price = double (price_base + delta) / 100;

    // qty
    book::Quantity qty = (std::rand() % 10 + 1) * 100;

    // order
    examples::OrderPtr order(new examples::Order(is_buy, price, qty));

    // add order
    exchange.add_order(sec.symbol, order);

    // Wait for eyes to read
    sleep(1);
  }
}
</pre>

<p>
  The implementation of <code>generate_orders()</code> is somewhat complex, but
  the details are not relevant.  There is a call to <code>sleep()</code>
  inside the loop, so that the data stream is produced at a somewhat readable
  pace.  At this point, the publisher is complete.
</p>

<h2>Subscriber Application</h2>

<p>
  With the publisher in place, focus turns to the subscriber.  The subscriber
  must connect to the publisher, decode the FAST feed, recreate the depth 
  state for each security and display the results.  As noted earlier, the
  connection is handled by the <code>DepthFeedConnection</code> class.  The 
  decoding and display of messages is handled by a the 
  <code>DepthFeedSubscriber</code> class:
</p>

<div class="listing">depth_feed_subscriber.h: DepthFeedSubscriber class declaration</div>
<pre class="code">
namespace liquibook { namespace examples {

  class DepthFeedSubscriber : public TemplateConsumer {
  public:
    DepthFeedSubscriber(
        const QuickFAST::Codecs::TemplateRegistryPtr&amp; templates);

    // Handle a reset of the connection
    void handle_reset();

    // Handle a message
    // return false if failure
    bool handle_message(BufferPtr&amp; bp, size_t bytes_transferred);
</pre>

<p>
  The <code>DepthFeedSubscriber</code> class has a number of private members:
</p>

<div class="listing">depth_feed_subscriber.h: DepthFeedSubscriber class declaration, continued</div>
<pre class="code">
  private:
    QuickFAST::Codecs::Decoder decoder_;
    typedef std::map&lt;std::string, book::Depth&lt;5&gt; &gt; DepthMap;
    DepthMap depth_map_;
    uint64_t expected_seq_;

    static const uint64_t MSG_TYPE_DEPTH;
    static const uint64_t MSG_TYPE_TRADE;
</pre>

<p>
  These members include a QuickFAST decoder, an order book for each security
  (kept in a <code>std::map</code>), and the expected sequence number.  The 
  sequence number is tracked to validate the feed, so that the subscriber is
  sure that every message from the publisher has been handled.
</p>
<p>
  Finally, there are two constants for determining message type.  The reader
  will note that these message type fields appear in the message, but the
  publisher nowhere encoded them.  This is because of their field
  instructions, found in the FAST template, of constant.
</p>

<p>
  In addition, there are a few private methods, to process incoming methods:
</p>

<div class="listing">depth_feed_subscriber.h: DepthFeedSubscriber class declaration, continued</div>
<pre class="code">
    void log_depth(book::Depth&lt;5&gt;&amp; depth);
    bool handle_trade_message(const std::string&amp; symbol,
                              uint64_t&amp; seq_num,
                              uint64_t&amp; timestamp,
                              QuickFAST::Messages::Message&amp; msg);
    bool handle_depth_message(const std::string&amp; symbol,
                              uint64_t&amp; seq_num,
                              uint64_t&amp; timestamp,
                              QuickFAST::Messages::Message&amp; msg);
  };
} }
</pre>

<p>
  After initializing the static members comes the class constructor:
</p>

<div class="listing">depth_feed_subscriber.cpp: DepthFeedSubscriber class implementation</div>
<pre class="code">
namespace liquibook { namespace examples {

const uint64_t DepthFeedSubscriber::MSG_TYPE_DEPTH(11);
const uint64_t DepthFeedSubscriber::MSG_TYPE_TRADE(22);

using QuickFAST::ValueType;

DepthFeedSubscriber::DepthFeedSubscriber(
        const QuickFAST::Codecs::TemplateRegistryPtr&amp; templates)
: decoder_(templates),
  expected_seq_(1)
{
}
</pre>

<p>
  The constructor passes the templates to the decoder, and initializes the
  expected sequence number.  Next is the <code>handle_reset()</code> method, 
  which is called when the connection to the publisher is reset:
</p>

<div class="listing">depth_feed_subscriber.cpp: DepthFeedSubscriber class implementation, continued</div>
<pre class="code">
void
DepthFeedSubscriber::handle_reset()
{
  expected_seq_ = 1;
}
</pre>

<p>
  This simple method just resets the expected sequence number to one.  Next, is
  the meaty method <code>handle_message()</code>:
</p>

<div class="listing">depth_feed_subscriber.cpp: DepthFeedSubscriber class implementation, continued</div>
<pre class="code">

bool
DepthFeedSubscriber::handle_message(BufferPtr&amp; bp, size_t bytes_transferred)
{
  // Decode the message
  QuickFAST::Codecs::DataSourceBuffer source(bp-&gt;c_array(), bytes_transferred);
  QuickFAST::Codecs::SingleMessageConsumer consumer;
  QuickFAST::Codecs::GenericMessageBuilder builder(consumer);
  decoder_.decodeMessage(source, builder);
  QuickFAST::Messages::Message&amp; msg(consumer.message());

  // Examine message contents
  uint64_t seq_num, msg_type, timestamp;
  const QuickFAST::StringBuffer* string_buffer;
  size_t bids_length, asks_length;
  std::string symbol;
  if (!msg.getUnsignedInteger(id_seq_num_, ValueType::UINT32, seq_num)) {
    std::cout &lt;&lt; "Could not get seq num from msg" &lt;&lt; std::endl;
    return false;
  }
  if (seq_num != expected_seq_) {
    std::cout &lt;&lt; "ERROR: Got Seq num " &lt;&lt; seq_num &lt;&lt; ", expected " 
              &lt;&lt; expected_seq_ &lt;&lt; std::endl;
    return false;
  }
  if (!msg.getUnsignedInteger(id_msg_type_, ValueType::UINT32, msg_type)) {
    std::cout &lt;&lt; "Could not get msg type from msg" &lt;&lt; std::endl;
    return false;
  }
  if (!msg.getString(id_symbol_, ValueType::ASCII, string_buffer)) {
    std::cout &lt;&lt; "Could not get symbol from msg" &lt;&lt; std::endl;
    return false;
  }
  if (!msg.getUnsignedInteger(id_timestamp_, ValueType::UINT32, timestamp)) {
    std::cout &lt;&lt; "Could not get timestamp from msg" &lt;&lt; std::endl;
    return false;
  }
  bool result = false;
  symbol = (std::string)*string_buffer;
  switch (msg_type) {
  case MSG_TYPE_DEPTH:
    result = handle_depth_message(symbol, seq_num, timestamp, msg);
    break;
  case MSG_TYPE_TRADE:
    result = handle_trade_message(symbol, seq_num, timestamp, msg);
    break;
  default:
    std::cout &lt;&lt; "ERROR: Unknown message type " &lt;&lt; msg_type 
              &lt;&lt; " seq num " &lt;&lt; seq_num &lt;&lt; std::endl;
    return false;
  }
  ++expected_seq_;
  return result;
}
</pre>

<p>
  This method first decodes the FAST message.  This is done by stuffing the 
  message contents into a QuickFAST buffer specific for decoding.  Next, a
  message builder and consumer are created and associated, and the message is
  decoded into the builder.  After this, the  message is available from the 
  consumer.
</p>

<p>
  The common fields are then checked, by using the type-specific extractors on
  the <code>QuickFAST::Messages::Message</code> class, such as
  <code>getUnsignedInteger()</code>.  This starts with the sequence number, 
  which is validated against the expected sequence number.  Next the message
  type, the symbol, and the timestamp are extracted.  If any of these fail, the
  method exits with an error value.
</p>

<p>
  Finally, since the message type is known, the proper message-type-specific
  handler is called.
</p>

<p>
  The first helper method logs the contents of the depth for a security:
</p>
<div class="listing">depth_feed_subscriber.cpp: DepthFeedSubscriber class implementation, continued</div>
<pre class="code">
void
DepthFeedSubscriber::log_depth(book::Depth&lt;5&gt;&amp; depth)
{
  book::DepthLevel* bid = depth.bids();
  book::DepthLevel* ask = depth.asks();
  printf("----------BID----------    ----------ASK----------\n");
  while (bid || ask) {
    if (bid &amp;&amp; bid-&gt;order_count()) {
      printf("%8.2f %9d [%2d]", 
             (double)bid-&gt;price() / Order::precision_,
             bid-&gt;aggregate_qty(), bid-&gt;order_count());
      if (bid == depth.last_bid_level()) {
        bid = NULL;
      } else {
        ++bid;
      }
    } else {
      // Blank lines
      printf("                       ");
      bid = NULL;
    }

    if (ask &amp;&amp; ask-&gt;order_count()) {
      printf("    %8.2f %9d [%2d]\n",
             (double)ask-&gt;price() / Order::precision_,
             ask-&gt;aggregate_qty(), ask-&gt;order_count());
      if (ask == depth.last_ask_level()) {
        ask = NULL;
      } else {
        ++ask;
      }
    } else {
      // Newline
      printf("\n");
      ask = NULL;
    }
  }
}
</pre>

<p>
  This method is complex, because it logs both bid and ask on the same line.
  In its loop, there could be a bid and an ask, only a bid, or only an ask.
</p>

<p>
  The next helper handles a depth message:
</p>

<div class="listing">depth_feed_subscriber.cpp: DepthFeedSubscriber class implementation, continued</div>
<pre class="code">
bool
DepthFeedSubscriber::handle_depth_message(
  const std::string&amp; symbol,
  uint64_t&amp; seq_num,
  uint64_t&amp; timestamp,
  QuickFAST::Messages::Message&amp; msg)
{
  size_t bids_length, asks_length;
  std::cout &lt;&lt; timestamp
            &lt;&lt; " Got depth msg " &lt;&lt; seq_num 
            &lt;&lt; " for symbol " &lt;&lt; symbol &lt;&lt; std::endl;

  // Create or find depth
  std::pair&lt;DepthMap::iterator, bool&gt; results = depth_map_.insert(
      std::make_pair(symbol, book::Depth&lt;5&gt;()));
  book::Depth&lt;5&gt;&amp; depth = results.first-&gt;second;

  if (msg.getSequenceLength(id_bids_, bids_length)) {
    for (size_t i = 0; i &lt; bids_length; ++i) {
      const QuickFAST::Messages::MessageAccessor* accessor;
      if (msg.getSequenceEntry(id_bids_, i, accessor)) {
        uint64_t level_num, price, order_count, aggregate_qty;
        if (!accessor-&gt;getUnsignedInteger(id_level_num_, ValueType::UINT8,
                                         level_num)) {
          std::cout &lt;&lt; "Could not get Bid level from depth msg" &lt;&lt; std::endl;
          return false;
        }
        if (!accessor-&gt;getUnsignedInteger(id_price_, ValueType::UINT32,
                                         price)) {
          std::cout &lt;&lt; "Could not get Bid price from depth msg" &lt;&lt; std::endl;
          return false;
        }
        if (!accessor-&gt;getUnsignedInteger(id_order_count_, ValueType::UINT32,
                                         order_count)) {
          std::cout &lt;&lt; "Could not get Bid count from depth msg" &lt;&lt; std::endl;
          return false;
        }
        if (!accessor-&gt;getUnsignedInteger(id_size_, ValueType::UINT32,
                                         aggregate_qty)) {
          std::cout &lt;&lt; "Could not get Bid agg qty  from depth msg" &lt;&lt; std::endl;
          return false;
        }

        book::DepthLevel&amp; level = depth.bids()[level_num];
        level.set(price, aggregate_qty, order_count);

      } else {
        std::cout &lt;&lt; "Failed to get bid " &lt;&lt; i &lt;&lt; std::endl;
        return false;
      }
      msg.endSequenceEntry(id_bids_, i, accessor);
    }
  }
  if (msg.getSequenceLength(id_asks_, asks_length)) {
    for (size_t i = 0; i &lt; asks_length; ++i) {
      const QuickFAST::Messages::MessageAccessor* accessor;
      if (msg.getSequenceEntry(id_asks_, i, accessor)) {
        uint64_t level_num, price, order_count, aggregate_qty;
        if (!accessor-&gt;getUnsignedInteger(id_level_num_, ValueType::UINT8,
                                         level_num)) {
          std::cout &lt;&lt; "Could not get Ask level from depth msg " &lt;&lt; std::endl;
          return false;
        }
        if (!accessor-&gt;getUnsignedInteger(id_price_, ValueType::UINT32,
                                         price)) {
          std::cout &lt;&lt; "Could not get Ask price  from depth msg" &lt;&lt; std::endl;
          return false;
        }
        if (!accessor-&gt;getUnsignedInteger(id_order_count_, ValueType::UINT32,
                                         order_count)) {
          std::cout &lt;&lt; "Could not get Ask count from depth msg " &lt;&lt; std::endl;
          return false;
        }
        if (!accessor-&gt;getUnsignedInteger(id_size_, ValueType::UINT32,
                                         aggregate_qty)) {
          std::cout &lt;&lt; "Could not get Ask agg qty from depth msg " &lt;&lt; std::endl;
          return false;
        }

        book::DepthLevel&amp; level = depth.asks()[level_num];
        level.set(price, aggregate_qty, order_count);

      } else {
        std::cout &lt;&lt; "Failed to get ask " &lt;&lt; i &lt;&lt; std::endl;
        return false;
      }
      msg.endSequenceEntry(id_asks_, i, accessor);
    }
  }
  log_depth(depth);
  return true;
}
</pre>

<p>
  This method has the common fields passed to it, and logs the symbol of the 
  message.  It then finds the proper depth object for the security, or creates
  one if this is the first depth message for the security. 
</p>
<p>
  Next the method must iterate through the sequence of changed bids.  To do
  this, the sequence length is first extracted, and each entry accessed through
  an accessor.
</p>
<p>
  Each bid field is then accessed using type-specific extractors, including the
  level number, the price, the number of orders, and the aggregate quantity.
  These updated values are then used to update the depth level for that
  security through a call to <code>set()</code>.  Similar logic is performed
  for changed ask levels, and the resulting depth is logged using
  <code>log_depth()</code>.
</p>

<p>
  The final helper method handles a trade message:
</p>

<div class="listing">depth_feed_subscriber.cpp: DepthFeedSubscriber class implementation, continued</div>
<pre class="code">
bool
DepthFeedSubscriber::handle_trade_message(
  const std::string&amp; symbol,
  uint64_t&amp; seq_num,
  uint64_t&amp; timestamp,
  QuickFAST::Messages::Message&amp; msg)
{
  uint64_t qty, cost;
  // Get trade fields
  if (!msg.getUnsignedInteger(id_qty_, ValueType::UINT32, qty)) {
    std::cout &lt;&lt; "Could not qty from trade msg" &lt;&lt; std::endl;
    return false;
  }
  if (!msg.getUnsignedInteger(id_cost_, ValueType::UINT32, cost)) {
    std::cout &lt;&lt; "Could not get cost from trade msg" &lt;&lt; std::endl;
    return false;
  }

  double price = (double) cost / (qty * Order::precision_);
  std::cout &lt;&lt; timestamp
            &lt;&lt; " Got trade msg " &lt;&lt; seq_num 
            &lt;&lt; " for symbol " &lt;&lt; symbol 
            &lt;&lt; ": " &lt;&lt; qty &lt;&lt; "@" &lt;&lt; price
            &lt;&lt; std::endl;

  return true;
}

} }
</pre>

<p>
  Like <code>handle_depth_message()</code>, <code>handle_trade_message()</code>
  is passed the common fields.  It extracts the trade quantity and cost, from
  which it calculates the price of the trade (after adjusting for the 
  precision).  The result is then logged.
</p>

<h3>Subscriber Initialization</h3>

<p>
  Finally, the subscriber can create, initialize, and associate its components.
  The <code>main()</code> function of subscriber starts by creating the
  <code>DepthFeedConnection</code> and the <code>DepthFeedSubscriber</code>:
</p>

<div class="listing">subscriber_main.cpp: main() function</div>
<pre class="code">
int main(int argc, const char* argv[])
{
  // Create the connection
  liquibook::examples::DepthFeedConnection connection(argc, argv);

  // Create feed subscriber
  liquibook::examples::DepthFeedSubscriber feed(connection.get_templates());

  // Set up handlers
  liquibook::examples::MessageHandler msg_handler =
      boost::bind(&amp;liquibook::examples::DepthFeedSubscriber::handle_message,
                  &amp;feed, _1, _2);
  liquibook::examples::ResetHandler reset_handler =
      boost::bind(&amp;liquibook::examples::DepthFeedSubscriber::handle_reset,
                  &amp;feed);
  connection.set_message_handler(msg_handler);
  connection.set_reset_handler(reset_handler);

  // Connect to server
  connection.connect();
  connection.run();

  return 0;
}
</pre>

<p>
  Next, the handlers for messages and connection resets are set up, and 
  finally, the subscriber connects to the publisher.  This completes the 
  subscriber application.
</p>

<h2>Running the Example</h2>

<p>
  By default the publisher and subscriber connect on port 10003 on localhost.
  To run on a different port, add the -p &lt;port&gt; to each command line.
  The subscriber may also specify a remote host with the -h &lt;host&gt;
  option.  To specify a different template file, or template file location, use
  the -t &lt;filename&gt; option.
</p>
<p>
  For example:
</p>

<pre>
  $ ./depth_feed_publisher -p 9999
  $ ./depth_feed_subscriber -p 9999
</pre>

<p>
  The subscriber produces output like this:
</p>

<pre>
1369167639 Got depth msg 8182 for symbol GILD
----------BID----------    ----------ASK----------
   49.93       400 [ 1]       50.12       500 [ 1]
   49.66       300 [ 1]       50.13       500 [ 1]
   49.59      1000 [ 3]       50.14       500 [ 1]
   49.58       700 [ 1]       50.19       500 [ 1]
                              50.26       800 [ 1]
1369167640 Got depth msg 8183 for symbol XLNX
----------BID----------    ----------ASK----------
   37.40       400 [ 1]       37.60      1000 [ 2]
   37.36       400 [ 1]       37.61      1900 [ 3]
   37.35      1000 [ 1]       37.65       900 [ 3]
   37.28       500 [ 2]       37.70       600 [ 1]
   37.25      1700 [ 2]       37.71      1000 [ 2]
1369167641 Got depth msg 8184 for symbol GOLD
----------BID----------    ----------ASK----------
   78.30       300 [ 1]       78.43       300 [ 1]
   78.18       300 [ 1]       78.48       700 [ 1]
   78.17       900 [ 1]       78.64       600 [ 1]
   78.09       200 [ 1]       78.70       600 [ 1]
   78.06       900 [ 1]       79.12       500 [ 1]
1369167642 Got trade msg 8185 for symbol HSIC: 600@89.47
1369167642 Got trade msg 8186 for symbol HSIC: 200@89.49
1369167642 Got depth msg 8187 for symbol HSIC
----------BID----------    ----------ASK----------
   89.32       700 [ 1]       89.49       500 [ 1]
   89.09       500 [ 1]       89.52       200 [ 1]
   88.96       600 [ 1]       89.68       500 [ 1]
   88.82       200 [ 1]       89.76       900 [ 1]
   88.59       100 [ 1]       89.91       100 [ 1]
</pre>

<p>
  These updates show the full depth for each security updated, and not just 
  the changed levels.  The first three updates shows the depth for various
  securities, followed by some trades and a depth update for a fourth security.
  The trades go hand-in-hand with a depth update.  Some depth updates cause
  trades, and some do not.
</p>
<p>
  Note that a very good test of the incremental feed is to start two
  subscribers at different times, so that the first subscriber has handled a
  good number of messages before the second subscriber is started.  The two
  should still be perfectly in sync, and produce identical output.
</p>

<h2>Summary</h2>

<p>
  Although much code has been walked through in this example, very little
  custom code was related to the order book.  Most of the code was either 
  general networking code using ASIO, or QuickFAST code.  In addition, the
  complexities of order book maintenance and depth-building are contained -
  most of the code shown was simple, or even trivial.
</p>

<p>
  Liquibook makes it very simple to implement a limit order book matching 
  engine, whether you are building an exchange, or are a trader simulating an
  exchange's matching capability.
</p>

<h2>References</h2>

<ul>
    <li>Liquibook -
        <a href="https://github.com/objectcomputing/liquibook" shape="rect">https://github.com/objectcomputing/liquibook</a>
    </li>
    <li>QuickFAST -
        <a href="http://quickfast.org" shape="rect">http://quickfast.org</a>
    </li>
    <li>Boost -
        <a href="http://www.boost.org/" shape="rect">http://www.boost.org/</a>
    </li>
    <li>Xerces C -
        <a href="http://xerces.apache.org/xerces-c/" shape="rect">http://xerces.apache.org/xerces-c/</a>
    </li>
    <li>FIX protocol -
        <a href="http://www.fixprotocol.org" shape="rect">http://www.fixprotocol.org</a>
    </li>
    <li>FAST protocol -
        <a href="http://www.fixprotocol.org/fast" shape="rect">http://www.fixprotocol.org/fast</a>
    </li>
    <li>NASDAQ 100 -
        <a href="http://www.nasdaq.com/markets/indices/nasdaq-100.aspx" shape="rect">http://www.nasdaq.com/markets/indices/nasdaq-100.aspx</a>
    </li>
</ul>

<p class="footer">
    Inquiries regarding
    <a class="career" href="http://www.ociweb.com/careers/index.html">Career Opportunities</a>
    can be directed to: <a href="mailto:hr@ociweb.com">hr@ociweb.com</a>.
</p>
<p class="footer">
    The <em><strong>Software Engineering Tech Trends</strong></em>
    is a monthly newsletter. The purpose and intent of this publication is to
    partner with clients in developing solutions for their most demanding mission critical
    systems, by leveraging our deep experience in software and systems engineering. We promote
    non-proprietary, standards-based solutions that afford our clients greater control and
    broader choice. Knowledge transfer is a fundamental aspect of services we offer our customers.
    We tailor our engagements to meet customer needs. We foster the professional growth of our
    employees and reward excellence.

    To
    <a href="mailto:sett-join@ociweb.com">subscribe</a>
    or
    <a href="mailto:sett-leave@ociweb.com">unsubscribe</a>
</p>
<p class="footer">
    Copyright
    &copy;2008-2013.
    Object Computing, Inc. All rights reserved.<br/>
    <br/>Java and all
    Java-based marks are trademarks or registered trademarks of Oracle Corporation.<br/>
    <br/>
    .NET, C#, and .NET-based marks are trademarks or registered trademarks of Microsoft
    Corporation.
</p>
<div style="margin-top: 25px">
    <div style="float:left">
        <a href="http://validator.w3.org/check?uri=referer"><img src="http://www.w3.org/Icons/valid-xhtml10"
                                                                 alt="Valid XHTML 1.0 Strict" height="31" width="88"/></a>
        <a href="http://feed2.w3.org/check.cgi?url=http%3A//ociweb.com/sett/rss.xml"><img src="images/valid-rss-rogers.png" alt="[Valid RSS]" title="Validate my RSS feed" /></a>
    </div>
    <div>
        <a href="http://ociweb.com/sett/rss.xml"><span class="rssRow">RSS</span></a>
    </div>
    <div style="float:right">
        <a href="#top">Top</a>
    </div>

</div>
<script type="text/javascript">
    (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
        (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
            m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
    })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

    ga('create', 'UA-40828801-1', 'ociweb.com');
    ga('send', 'pageview');

</script>

<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js" type="text/javascript"></script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shAutoloader.js" type="text/javascript"></script>

<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJava.js" type="text/javascript"></script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushXml.js" type="text/javascript"></script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPhp.js" type="text/javascript"></script>

<script type="text/javascript">SyntaxHighlighter.all()</script>

</body>
</html>


================================================
FILE: doc/settAug2013/settAug2013_files/paper.css
================================================
.listing, .figure {
  font-weight: bold;
  font-size: .9em;
}
.listing {
  margin-bottom: -16px;
}


================================================
FILE: doc/settAug2013/styles/SETT.css
================================================
body {
    color: rgb(0, 0, 0);
    background-color: rgb(255, 255, 255);
    font-family: Verdana, sans-serif;
    margin-left: 0.25in;
    margin-right: 0.25in;
    min-width: 800px;
}

.header {
    float: left;
    width: 100%;
}

.left {
    float: left;
}

.right {
    float: right;
}

.lower_header {
    height: 34px;
    float: left;
    width: 100%;
    border-top: 1px solid lightgrey;
    border-bottom: 1px solid lightgrey;
    margin-bottom: 25px;
}

.social {
    margin-top: 6px;
}

span.rssRow  {
    margin: 33px;
    background:transparent url(../images/rss_icon_12x12.gif) no-repeat scroll 0;
    padding-left:18px;
}

.headimage_container {
    position: relative;
    float: left;
    width: 100%;
    background: url('../images/Middle.png') repeat-x 35px;
    height: 123px;
}

.headerimage_leftlogo {
    position: absolute;
    left: 0;
    top: 0;
    background-color: white;
    width: 180px;
    height: 123px;
}

.headimage_left {
    position: absolute;
    background-color: white;
    top: 33px;
    left: 180px;
    width: 10px;
}

.headimage_right {
    position: absolute;
    background-color: white;
    width: 278px;
    right: 0;
    margin-top: 33px;
}

a.career {
    font-weight: bold;
}

code {
    font-family: "Courier New", monospace;
}

div.center {
    text-align: center;
}

h1 {
    text-align: center;
}

h2 {
    text-align: left;
}

h3 {
    text-align: left;
}

h4 {
    text-align: left;
}

h5 {
    text-align: left;
}

hr {
    height: 1px;
    color: rgb(17, 59, 86);
    background-color: transparent;
}

kbd {
    font-family: "Courier New", monospace;
}

p {
    text-align: justify;
}

p.author {
    text-align: center;
}

p.footer {
    text-align: justify;
}

pre {
    font-family: "Courier New", monospace;
}

.quicklinks {
    text-align: right;
}

.red {
    color: rgb(255, 0, 0);
    background-color: rgb(255, 255, 255);
}

.green {
    color: rgb(0, 128, 0);
    background-color: rgb(255, 255, 255);
}

.blue {
    color: rgb(0, 0, 192);
    background-color: rgb(255, 255, 255);
}

.code {
    background-color: #FFFFF0;
    border: dashed black 1px;
    padding-left: 10px;
}

.comment {
    color: rgb(128, 128, 128);
    font-weight: normal;
    font-style: italic;
}



================================================
FILE: env.sh
================================================
SOURCE="${BASH_SOURCE[0]}"
SOURCE_DIR=`dirname $SOURCE`

if test "$LIQUIBOOK_ROOT" = ""; then
    READLINK='readlink'
    $READLINK --version >/dev/null 2>/dev/null
    if (( $? != 0 )); then
      echo "readlink does not exist or it does not support --version"
      echo "maybe it is not GNU readlink but BSD"
      echo "trying with greadlink..."
      READLINK='greadlink'
    fi
    $READLINK --version >/dev/null 2>/dev/null
    if (( $? != 0 )); then
      echo "greadlink does not exist or an error occurred"
      UNAME=`uname`
      if [[ $UNAME == "Darwin" ]]; then
            echo "You are running on a Mac OSX system."
            echo "Consider installing homebrew."
            echo "Then install coreutils."
            echo "# brew install coreutils"
      fi
    else
      echo "$READLINK found at `which $READLINK`."
    fi
    $READLINK -f $SOURCE_DIR
    if (( $? != 0 )); then
      echo "trying exporting LIQUIBOOK_ROOT by pwd."
      export LIQUIBOOK_ROOT=`pwd`
      echo "LIQUIBOOK_ROOT = $LIQUIBOOK_ROOT"
    else
      export LIQUIBOOK_ROOT=`$READLINK -f $SOURCE_DIR`
    fi
fi

if test "$QUICKFAST_ROOT" == "";  then
  export QUICKFAST_ROOT=`pwd`/noQuickFAST
  echo QuickFAST support disabled
fi

if test "$BOOST_VERSION" = ""; then
  echo Please export BOOST_VERSION, and BOOST_CFG
  echo you can also set BOOST_ROOT if it is not /usr/boost/BOOST_VERSION
else
  if test "$BOOST_ROOT" = ""; then
    export BOOST_ROOT=/usr/boost/$BOOST_VERSION
  fi
  if test "$BOOST_ROOT_LIB" = ""; then
    export BOOST_ROOT_LIB=$BOOST_ROOT/lib
  fi
  if test "$BOOST_CFG" = ""; then  
    export BOOST_CFG=-gcc62-mt-1_63
  fi
  if test "$BOOST_STATIC_LIB_PREFIX" = ""; then
    export BOOST_STATIC_LIB_PREFIX=
  fi
fi

LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$LIQUIBOOK_ROOT/lib
# CIAO is not used, set so MPC does not give warning
export CIAO_ROOT=/dev/null


================================================
FILE: examples/.gitignore
================================================



================================================
FILE: examples/mt_order_entry/Market.cpp
================================================
// Copyright (c) 2017 Object Computing, Inc.
// All rights reserved.
// See the file license.txt for licensing information.
#include "Market.h"
#include "Util.h"

#include <functional> 
#include <cctype>
#include <locale>

namespace {
    ///////////////////////
    // depth display helper
    void displayDepthLevel(std::ostream & out, const liquibook::book::DepthLevel & level)
    {
        out << "\tPrice "  <<  level.price();
        out << " Count: " << level.order_count();
        out << " Quantity: " << level.aggregate_qty();
        if(level.is_excess())
        {
            out << " EXCESS";
        }
        out << " Change id#: " << level.last_change();
        out << std::endl;
    }

    void publishDepth(std::ostream & out, const orderentry::BookDepth & depth)
    {
        liquibook::book::ChangeId published = depth.last_published_change();
        bool needTitle = true;
        // Iterate awkwardly
        auto pos = depth.bids();
        auto back = depth.last_bid_level();
        bool more = true;
        while(more)
        {
            if(pos->aggregate_qty() !=0 && pos->last_change() > published)
            {
                if(needTitle)
                {
                    out << "\n\tBIDS:\n";
                    needTitle = false;
                }
                displayDepthLevel(out, *pos);
            }
            ++pos;
            more = pos != back;
        }

        needTitle = true;
        pos = depth.asks();
        back = depth.last_ask_level();
        more = true;
        while(more)
        {
            if(pos->aggregate_qty() !=0 && pos->last_change() > published)
            {
                if(needTitle)
                {
                    out << "\n\tASKS:\n";
                    needTitle = false;
                }
                displayDepthLevel(out, *pos);
            }
            ++pos;
            more = pos != back;
        }
    }
}

namespace orderentry
{

uint32_t Market::orderIdSeed_ = 0;

Market::Market(std::ostream * out)
: logFile_(out)
{
}

Market::~Market()
{
}

const char * 
Market::prompt()
{
    return "\t(B)uy\n\t(S)ell\n\t(M)odify\n\t(C)ancel\n\t(D)isplay\n";
}

void 
Market::help(std::ostream & out)
{
    out << "Buy: Create a new Buy order and add it to the book\n"
        << "Sell: Create a new Sell order and add it to the book\n"
        << "  Arguments for BUY or SELL\n"
        << "     <Quantity>\n"
        << "     <Symbol>\n"
        << "     <Price> or MARKET\n"
        << "     AON            (optional)\n"
        << "     IOC            (optional)\n"
        << "     STOP <Price>   (optional)\n"
        << "     ;              end of order\n"
        << std::endl;

    out << "Modify: Request Modify an existing order\n"
        << "  Arguments:\n"
        << "     <order#>\n"
        << "     PRICE <new price>\n"
        << "     QUANTITY <new initial quantity>\n"
        << "     ;              end of modify requet\n"
        << std::endl;

    out << "Cancel: Request cancel an existing order\n"
        << "  Arguments:\n"
        << "     <order#>\n"
        << "     ;              end of cancel request (optional)\n"
        << std::endl;

    out << "Display: Display status of an existing order\n"
        << "  Arguments:\n"
        << "     +       (enable verbose display)\n"
        << "     <order#> or <symbol> or \"all\"\n"
        << std::endl;

}

bool 
Market::apply(const std::vector<std::string> & tokens)
{
    const std::string & command = tokens[0];
    if(command == "BUY" || command == "B")
    {
        return doAdd("BUY", tokens, 1);
    }
    if(command == "SELL" || command == "S")
    {
        return doAdd("SELL", tokens, 1);
    }
    else if (command == "CANCEL" || command == "C")
    {
        return doCancel(tokens, 1);
    }
    else if(command == "MODIFY" || command == "M")
    {
        return doModify(tokens, 1);
    }
    else if(command == "DISPLAY" || command == "D")
    {
        return doDisplay(tokens, 1);
    }
    return false;
}


////////
// ADD
bool 
Market::doAdd(const  std::string & side, const std::vector<std::string> & tokens, size_t pos)
{
    //////////////
    // Quantity
    liquibook::book::Quantity quantity;
    std::string qtyStr = nextToken(tokens, pos);
    if(!qtyStr.empty())
    {
        quantity = toUint32(qtyStr);
    }
    else
    {
        quantity = promptForUint32("Quantity");
    }
    // sanity check
    if(quantity == 0 || quantity > 1000000000)
    {
        out() << "--Expecting quantity" << std::endl;
        return false;
    }

    //////////////
    // SYMBOL
    std::string symbol = nextToken(tokens, pos);
    if(symbol.empty())
    {
        symbol = promptForString("Symbol");
    }
    if(!symbolIsDefined(symbol))
    {
        if(symbol[0] == '+' || symbol[0] == '!')
        {
            bool useDepth = symbol[0] == '!';
            symbol = symbol.substr(1);
            if(!symbolIsDefined(symbol))
            {
                addBook(symbol, useDepth);
            }
        }
        else
        {
            std::string bookType;
            while(bookType != "S" 
              && bookType != "D" 
              && bookType != "N")
            {
              bookType = promptForString(
              "New Symbol " + symbol +  
              ". \nAdd [S]imple book, or [D]epth book, or 'N' to cancel request.\n[SDN}");
            }
            if(bookType == "N")
            {
                out() << "Request ignored" << std::endl;
                return false;
            }
            bool useDepth = bookType == "D";
            addBook(symbol, useDepth);
        }
    }

    ///////////////
    // PRICE
    uint32_t price = 0;
    std::string priceStr = nextToken(tokens, pos);
    if(!priceStr.empty())
    {
        price = stringToPrice(priceStr);
    }
    else
    {
        price = promptForPrice("Limit Price or MKT");
    }
    if(price > 10000000)
    {
        out() << "--Expecting price or MARKET" << std::endl;
        return false;
    }

    //////////////////////////
    // OPTIONS: AON, IOC STOP
    bool aon = false;
    bool ioc = false;
    liquibook::book::Price stopPrice = 0;
    bool go = false;
    while(!go)
    {
        bool prompted = false;
        bool optionOk = false;
        std::string option = nextToken(tokens, pos);
        if(option.empty())
        {
            prompted = true;
            option = promptForString("AON, or IOC, or STOP, or END");
        }
        if(option == ";" || option == "E" || option == "END")
        {
            go = true;
            optionOk = true;
        }
        else if(option == "A" || option == "AON")
        {
            aon = true;
            optionOk = true;
        }
        else if(option == "I" || option == "IOC")
        {
            ioc = true;
            optionOk = true;
        }
        else if(option == "S" || option == "STOP")
        {
            std::string stopstr = nextToken(tokens, pos);

            if(!stopstr.empty())
            {
                stopPrice = stringToPrice(stopstr);
            } 
            else
            {
                stopPrice = promptForUint32("Stop Price");
                prompted = true;
            }
            optionOk = stopPrice <= 10000000;
        }
        if(!optionOk)
        {
            out() << "Unknown option " << option << std::endl;
            if(!prompted)
            {
                out() << "--Expecting AON IOC STOP or END" << std::endl;
                return false;
            }
        }
    }

    std::string orderId = std::to_string(++orderIdSeed_);

    OrderPtr order = std::make_shared<Order>(orderId, side == "BUY", quantity, symbol, price, stopPrice, aon, ioc);

    const liquibook::book::OrderConditions AON(liquibook::book::oc_all_or_none);
    const liquibook::book::OrderConditions IOC(liquibook::book::oc_immediate_or_cancel);
    const liquibook::book::OrderConditions NOC(liquibook::book::oc_no_conditions);

    const liquibook::book::OrderConditions conditions = 
        (aon ? AON : NOC) | (ioc ? IOC : NOC);


    auto book = findBook(symbol);
    if(!book)
    {
        out() << "--No order book for symbol" << symbol << std::endl;
        return false;
    }

    order->onSubmitted();
    out() << "ADDING order:  " << *order << std::endl;

    orders_[orderId] = order;
    book->add(order, conditions);
    return true;
}

///////////
// CANCEL
bool
Market::doCancel(const std::vector<std::string> & tokens, size_t position)
{
    OrderPtr order;
    OrderBookPtr book;
    if(!findExistingOrder(tokens, position, order, book))
    {
        return false;
    }
    out() << "Requesting Cancel: " << *order << std::endl;
    book->cancel(order);
    return true;
}

///////////
// MODIFY
bool
Market::doModify(const std::vector<std::string> & tokens, size_t position)
{
    OrderPtr order;
    OrderBookPtr book;
    if(!findExistingOrder(tokens, position, order, book))
    {
        return false;
    }

    //////////////
    // options
    //////////////////////////
    // OPTIONS: PRICE (price) ; QUANTITY (delta)

    int64_t quantityChange = liquibook::book::SIZE_UNCHANGED;
    liquibook::book::Price price = liquibook::book::PRICE_UNCHANGED;

    bool go = false;
    while(!go)
    {
        bool prompted = false;
        bool optionOk = false;
        std::string option = nextToken(tokens, position);
        if(option.empty())
        {
            prompted = true;
            option = promptForString("PRICE, or QUANTITY, or END");
        }
        if(option == ";" || option == "E" || option == "END")
        {
            go = true;
            optionOk = true;
        }
        else if(option == "P" || option == "PRICE")
        {
            uint32_t newPrice = INVALID_UINT32;
            std::string priceStr = nextToken(tokens, position);
            if(priceStr.empty())
            {
                newPrice = promptForUint32("New Price");
            }
            else
            {
                newPrice = toUint32(priceStr);
            }

            if(newPrice > 0 && newPrice != INVALID_UINT32)
            {
                price = newPrice;
                optionOk = true;
            }
            else
            {
                out() << "Invalid price" << std::endl;
            }
        }
        else if(option == "Q" || option == "QUANTITY")
        {
            int32_t qty = INVALID_INT32;
            std::string qtyStr = nextToken(tokens, position);
            if(qtyStr.empty())
            {
                qty = promptForInt32("Change in quantity");
            }
            else
            {
                qty = toInt32(qtyStr);
            }
            if(qty != INVALID_INT32)
            {
                quantityChange = qty;
                optionOk = true;
            }
            else
            {
                out() << "Invalid quantity change." << std::endl;
            }
        }

        if(!optionOk)
        {
            out() << "Unknown or invalid option " << option << std::endl;
            if(!prompted)
            {
                out() << "--Expecting PRICE <price>, or QUANTITY <change>, or  END" << std::endl;
                return false;
            }
        }
    }

    book->replace(order, quantityChange, price);
    out() << "Requested Modify" ;
    if(quantityChange != liquibook::book::SIZE_UNCHANGED)
    {
        out() << " QUANTITY  += " << quantityChange;
    }
    if(price != liquibook::book::PRICE_UNCHANGED)
    {
        out() << " PRICE " << price;
    }
    out() << std::endl;
    return true;
}

///////////
// DISPLAY
bool
Market::doDisplay(const std::vector<std::string> & tokens, size_t pos)
{
    bool verbose = false;
    // see if first token could be an order id.
    // todo: handle prompted imput!
    std::string parameter = nextToken(tokens, pos);
    if(parameter.empty())
    {
        parameter = promptForString("+ or #OrderId or -orderOffset or symbol or \"ALL\"");
    }
    else
    {
        --pos; // Don't consume this parameter yet.
    }
    if(parameter[0] == '+')
    {
        verbose = true;
        if(parameter.length() > 1)
        {
            parameter = parameter.substr(1);
        }
        else
        {
            ++pos; // now we can consume the first parameter (whether or not it's there!)
            parameter = nextToken(tokens, pos);
            if(parameter.empty())
            {
                parameter = promptForString("#OrderId or -orderOffset or symbol or \"ALL\"");
            }
            else
            {
                --pos; // Don't consume this parameter yet.
            }
        }
    }
    if(parameter[0] == '#' || parameter[0] == '-' || isdigit(parameter[0]))
    {
        OrderPtr order;
        OrderBookPtr book;
        if(findExistingOrder(parameter, order, book))
        {
            out() << *order << std::endl;
            return true;
        }
    }

    // Not an order id.  Try for a symbol:
    std::string symbol = parameter;
    if(symbolIsDefined(symbol))
    {
        for(auto pOrder = orders_.begin(); pOrder != orders_.end(); ++pOrder)
        {
            const OrderPtr & order = pOrder->second;
            if(order->symbol() == symbol)
            {
                out() << order->verbose(verbose) << std::endl;
                order->verbose(false);
            }
        }
        auto book = findBook(symbol);
        if(!book)
        {
            out() << "--No order book for symbol" << symbol << std::endl;
        }
        else
        {
            book->log(out());
        }
        return true;
    }
    else if( symbol == "ALL")
    {
        for(auto pOrder = orders_.begin(); pOrder != orders_.end(); ++pOrder)
        {
            const OrderPtr & order = pOrder->second;
            out() << order->verbose(verbose) << std::endl;
            order->verbose(false);
        }

        for(auto pBook = books_.begin(); pBook != books_.end(); ++pBook)
        {
            out() << "Order book for " << pBook->first << std::endl;
            pBook->second->log(out());
        }
        return true;
    }
    else
    {
        out() << "--Unknown symbol: " << symbol << std::endl;
    }
    return false;
}

/////////////////////////////
// Order book interactions

bool
Market::symbolIsDefined(const std::string & symbol)
{
    auto book = books_.find(symbol);
    return book != books_.end();
}

OrderBookPtr
Market::addBook(const std::string & symbol, bool useDepthBook)
{
    OrderBookPtr result;
    if(useDepthBook)
    {
        out() << "Create new depth order book for " << symbol << std::endl;
        DepthOrderBookPtr depthBook = std::make_shared<DepthOrderBook>(symbol);
        depthBook->set_bbo_listener(this);
        depthBook->set_depth_listener(this);
        result = depthBook;
    }
    else
    {
        out() << "Create new order book for " << symbol << std::endl;
        result = std::make_shared<OrderBook>(symbol);
    }
    result->set_order_listener(this);
    result->set_trade_listener(this);
    result->set_order_book_listener(this);
    books_[symbol] = result;
    return result;
}

OrderBookPtr
Market::findBook(const std::string & symbol)
{
    OrderBookPtr result;
    auto entry = books_.find(symbol);
    if(entry != books_.end())
    {
        result = entry->second;
    }
    return result;
}

bool Market::findExistingOrder(const std::vector<std::string> & tokens, size_t & position, OrderPtr & order, OrderBookPtr & book)
{
    ////////////////
    // Order ID
    std::string orderId = nextToken(tokens, position);
    trim(orderId);
    if(orderId.empty())
    {
        orderId = promptForString("Order Id#");
        trim(orderId);
    }
    // discard leading # if any
    if(orderId[0] == '#')
    {
        orderId = orderId.substr(1);
        trim(orderId);
        if(orderId.empty())
        {
            out() << "--Expecting #orderID" << std::endl;
            return false;
        }
    }

    if(orderId[0] == '-') // relative addressing
    {
        int32_t orderOffset = toInt32(orderId);
        if(orderOffset == INVALID_INT32)
        {
            out() << "--Expecting orderID or offset" << std::endl;
            return false;
        }
        uint32_t orderNumber = orderIdSeed_  + 1 + orderOffset;
        orderId = std::to_string(orderNumber);
    }
    return findExistingOrder(orderId, order, book);
}

bool Market::findExistingOrder(const std::string & orderId, OrderPtr & order, OrderBookPtr & book)
{
    auto orderPosition = orders_.find(orderId);
    if(orderPosition == orders_.end())
    {
        out() << "--Can't find OrderID #" << orderId << std::endl;
        return false;
    }

    order = orderPosition->second;
    std::string symbol = order->symbol();
    book = findBook(symbol);
    if(!book)
    {
        out() << "--No order book for symbol" << symbol << std::endl;
        return false;
    }
    return true;
}

/////////////////////////////////////
// Implement OrderListener interface

void 
Market::on_accept(const OrderPtr& order)
{
    order->onAccepted();
    out() << "\tAccepted: " <<*order<< std::endl;
}

void 
Market::on_reject(const OrderPtr& order, const char* reason)
{
    order->onRejected(reason);
    out() << "\tRejected: " <<*order<< ' ' << reason << std::endl;

}

void 
Market::on_fill(const OrderPtr& order, 
    const OrderPtr& matched_order, 
    liquibook::book::Quantity fill_qty, 
    liquibook::book::Cost fill_cost)
{
    order->onFilled(fill_qty, fill_cost);
    matched_order->onFilled(fill_qty, fill_cost);
    out() << (order->is_buy() ? "\tBought: " : "\tSold: ") 
        << fill_qty << " Shares for " << fill_cost << ' ' <<*order<< std::endl;
    out() << (matched_order->is_buy() ? "\tBought: " : "\tSold: ") 
        << fill_qty << " Shares for " << fill_cost << ' ' << *matched_order << std::endl;
}

void 
Market::on_cancel(const OrderPtr& order)
{
    order->onCancelled();
    out() << "\tCanceled: " << *order<< std::endl;
}

void Market::on_cancel_reject(const OrderPtr& order, const char* reason)
{
    order->onCancelRejected(reason);
    out() << "\tCancel Reject: " <<*order<< ' ' << reason << std::endl;
}

void Market::on_replace(const OrderPtr& order, 
    const int64_t& size_delta, 
    liquibook::book::Price new_price)
{
    order->onReplaced(size_delta, new_price);
    out() << "\tModify " ;
    if(size_delta != liquibook::book::SIZE_UNCHANGED)
    {
        out() << " QUANTITY  += " << size_delta;
    }
    if(new_price != liquibook::book::PRICE_UNCHANGED)
    {
        out() << " PRICE " << new_price;
    }
    out() <<*order<< std::endl;
}

void 
Market::on_replace_reject(const OrderPtr& order, const char* reason)
{
    order->onReplaceRejected(reason);
    out() << "\tReplace Reject: " <<*order<< ' ' << reason << std::endl;
}

////////////////////////////////////
// Implement TradeListener interface

void 
Market::on_trade(const OrderBook* book, 
    liquibook::book::Quantity qty, 
    liquibook::book::Cost cost)
{
    out() << "\tTrade: " << qty <<  ' ' << book->symbol() << " Cost "  << cost  << std::endl;
}

/////////////////////////////////////////
// Implement OrderBookListener interface

void 
Market::on_order_book_change(const OrderBook* book)
{
    out() << "\tBook Change: " << ' ' << book->symbol() << std::endl;
}



/////////////////////////////////////////
// Implement BboListener interface
void 
Market::on_bbo_change(const DepthOrderBook * book, const BookDepth * depth)
{
    out() << "\tBBO Change: " << ' ' << book->symbol() 
        << (depth->changed() ? " Changed" : " Unchanged")
        << " Change Id: " << depth->last_change()
        << " Published: " << depth->last_published_change()
        << std::endl;

}

/////////////////////////////////////////
// Implement DepthListener interface
void 
Market::on_depth_change(const DepthOrderBook * book, const BookDepth * depth)
{
    out() << "\tDepth Change: " << ' ' << book->symbol();
    out() << (depth->changed() ? " Changed" : " Unchanged")
        << " Change Id: " << depth->last_change()
        << " Published: " << depth->last_published_change();
    publishDepth(out(), *depth);
    out() << std::endl;
}

}  // namespace orderentry


================================================
FILE: examples/mt_order_entry/Market.h
================================================
// Copyright (c) 2017 Object Computing, Inc.
// All rights reserved.
// See the file license.txt for licensing information.
#pragma once

#include <book/depth_order_book.h>

#include "Order.h"

#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
#include <map>
#include <memory>

namespace orderentry
{
typedef liquibook::book::OrderBook<OrderPtr> OrderBook;
typedef std::shared_ptr<OrderBook> OrderBookPtr;
typedef liquibook::book::DepthOrderBook<OrderPtr> DepthOrderBook;
typedef std::shared_ptr<DepthOrderBook> DepthOrderBookPtr;
typedef liquibook::book::Depth<> BookDepth;

class Market 
    : public liquibook::book::OrderListener<OrderPtr>
    , public liquibook::book::TradeListener<OrderBook>
    , public liquibook::book::OrderBookListener<OrderBook>
    , public liquibook::book::BboListener<DepthOrderBook>
    , public liquibook::book::DepthListener<DepthOrderBook>
{
    typedef std::map<std::string, OrderPtr> OrderMap;
    typedef std::map<std::string, OrderBookPtr> SymbolToBookMap;
public:
    Market(std::ostream * logFile = &std::cout);
    ~Market();

    /// @brief What to display to user when requesting input
    static const char * prompt();

    /// @brief Help for user's input
    static void help(std::ostream & out = std::cout);

    /// @brief Apply a user command that has been parsed into tokens.
    bool apply(const std::vector<std::string> & tokens);

public:
    /////////////////////////////////////
    // Implement OrderListener interface

    /// @brief callback for an order accept
    virtual void on_accept(const OrderPtr& order);

    /// @brief callback for an order reject
    virtual void on_reject(const OrderPtr& order, const char* reason);

    /// @brief callback for an order fill
    /// @param order the inbound order
    /// @param matched_order the matched order
    /// @param fill_qty the quantity of this fill
    /// @param fill_cost the cost of this fill (qty * price)
    virtual void on_fill(const OrderPtr& order, 
        const OrderPtr& matched_order, 
        liquibook::book::Quantity fill_qty, 
        liquibook::book::Cost fill_cost);

    /// @brief callback for an order cancellation
    virtual void on_cancel(const OrderPtr& order);

    /// @brief callback for an order cancel rejection
    virtual void on_cancel_reject(const OrderPtr& order, const char* reason);

    /// @brief callback for an order replace
    /// @param order the replaced order
    /// @param size_delta the change to order quantity
    /// @param new_price the updated order price
    virtual void on_replace(const OrderPtr& order, 
        const int64_t& size_delta, 
        liquibook::book::Price new_price);

    /// @brief callback for an order replace rejection
    virtual void on_replace_reject(const OrderPtr& order, const char* reason);

    ////////////////////////////////////
    // Implement TradeListener interface

    /// @brief callback for a trade
    /// @param book the order book of the fill (not defined whether this is before
    ///      or after fill)
    /// @param qty the quantity of this fill
    /// @param cost the cost of this fill (qty * price)
    virtual void on_trade(const OrderBook* book, 
        liquibook::book::Quantity qty, 
        liquibook::book::Cost cost);

    /////////////////////////////////////////
    // Implement OrderBookListener interface

    /// @brief callback for change anywhere in order book
    virtual void on_order_book_change(const OrderBook* book);

    /////////////////////////////////////////
    // Implement BboListener interface
    void on_bbo_change(const DepthOrderBook * book, const BookDepth * depth);

    /////////////////////////////////////////
    // Implement DepthListener interface
    void on_depth_change(const DepthOrderBook * book, const BookDepth * depth);

private:
    ////////////////////////////////////
    // Command implementatiokns
    bool doAdd(const std::string & side, const std::vector<std::string> & tokens, size_t pos);
    bool doCancel(const std::vector<std::string> & tokens, size_t position);
    bool doModify(const std::vector<std::string> & tokens, size_t position);
    bool doDisplay(const std::vector<std::string> & tokens, size_t position);

    ////////////////////////
    // Order book interactions
    bool symbolIsDefined(const std::string & symbol);
    OrderBookPtr findBook(const std::string & symbol);
    OrderBookPtr addBook(const std::string & symbol, bool useDepthBook);
    bool findExistingOrder(const std::vector<std::string> & tokens, size_t & position, OrderPtr & order, OrderBookPtr & book);
    bool findExistingOrder(const std::string & orderId, OrderPtr & order, OrderBookPtr & book);

    std::ostream & out() 
    {
        return *logFile_;
    }
private:
    static uint32_t orderIdSeed_;

    std::ostream * logFile_;

    OrderMap orders_;
    SymbolToBookMap books_;

};

} // namespace orderentry


================================================
FILE: examples/mt_order_entry/Order.cpp
================================================
// Copyright (c) 2017 Object Computing, Inc.
// All rights reserved.
// See the file license.txt for licensing information.
#include "Order.h"
#include <sstream>

namespace orderentry
{

Order::Order(const std::string & id,
    bool buy_side,
    liquibook::book::Quantity quantity,
    std::string symbol,
    liquibook::book::Price price,
    liquibook::book::Price stopPrice,
    bool aon,
    bool ioc)
    : id_(id)
    , buy_side_(buy_side)
    , symbol_(symbol)
    , quantity_(quantity)
    , price_(price)
    , stopPrice_(stopPrice)
    , ioc_(ioc)
    , aon_(aon)
    , quantityFilled_(0)
    , quantityOnMarket_(0)
    , fillCost_(0)
    , verbose_(false)

{
}

std::string 
Order::order_id() const
{
    return id_;
}

bool 
Order::is_limit() const
{
    return price() != 0;
}

bool 
Order::is_buy() const
{
    return buy_side_;
}

bool 
Order::all_or_none() const
{
    return aon_;
}

bool 
Order::immediate_or_cancel() const
{
    return ioc_;
}

std::string 
Order::symbol() const
{
   return symbol_;
}

liquibook::book::Price 
Order::price() const
{
    return price_;
}

liquibook::book::Quantity 
Order::order_qty() const
{
    return quantity_;
}


liquibook::book::Price 
Order::stop_price() const
{
    return stopPrice_;
}

uint32_t 
Order::quantityOnMarket() const
{
    return quantityOnMarket_;
}

uint32_t 
Order::quantityFilled() const
{
    return quantityFilled_;
}

uint32_t 
Order::fillCost() const
{
    return fillCost_;
}


const Order::History & 
Order::history() const
{
    return history_;
}

const Order::StateChange & 
Order::currentState() const
{
    return history_.back();
}


Order & 
Order::verbose(bool verbose)
{
    verbose_ = verbose;
    return *this;
}

bool
Order::isVerbose() const
{
    return verbose_;
}

void 
Order::onSubmitted()
{
    std::stringstream msg;
    msg << (is_buy() ? "BUY " : "SELL ") << quantity_ << ' ' << symbol_ << " @";
    if( price_ == 0)
    {
        msg << "MKT";
    }
    else
    {
        msg << price_;
    }
    history_.emplace_back(Submitted, msg.str());
}

void 
Order::onAccepted()
{
    quantityOnMarket_ = quantity_;
    history_.emplace_back(Accepted);
}

void 
Order::onRejected(const char * reason)
{
    history_.emplace_back(Rejected, reason);
}

void 
Order::onFilled(
    liquibook::book::Quantity fill_qty, 
    liquibook::book::Cost fill_cost)
{
    quantityOnMarket_ -= fill_qty;
    fillCost_ += fill_cost;

    std::stringstream msg;
    msg << fill_qty << " for " << fill_cost;
    history_.emplace_back(Filled, msg.str());
}

void 
Order::onCancelRequested()
{
    history_.emplace_back(CancelRequested);
}

void 
Order::onCancelled()
{
    quantityOnMarket_ = 0;
    history_.emplace_back(Cancelled);
}

void 
Order::onCancelRejected(const char * reason)
{
    history_.emplace_back(CancelRejected, reason);
}

void 
Order::onReplaceRequested(
    const int32_t& size_delta, 
    liquibook::book::Price new_price)
{
    std::stringstream msg;
    if(size_delta != liquibook::book::SIZE_UNCHANGED)
    {
        msg << "Quantity change: " << size_delta << ' ';
    }
    if(new_price != liquibook::book::PRICE_UNCHANGED)
    {
        msg << "New Price " << new_price;
    }
    history_.emplace_back(ModifyRequested, msg.str());
}

void 
Order::onReplaced(const int32_t& size_delta, 
    liquibook::book::Price new_price)
{
    std::stringstream msg;
    if(size_delta != liquibook::book::SIZE_UNCHANGED)
    {
        quantity_ += size_delta;
        quantityOnMarket_ += size_delta;
        msg << "Quantity change: " << size_delta << ' ';
    }
    if(new_price != liquibook::book::PRICE_UNCHANGED)
    {
        price_ = new_price;
        msg << "New Price " << new_price;
    }
    history_.emplace_back(Modified, msg.str());
}

void 
Order::onReplaceRejected(const char * reason)
{
    history_.emplace_back(ModifyRejected, reason);
}

std::ostream & operator << (std::ostream & out, const Order::StateChange & event)
{
    out << "{";
    switch(event.state_)
    {
    case Order::Submitted:
        out << "Submitted ";
        break;
    case Order::Rejected: 
        out << "Rejected "; 
        break;
    case Order::Accepted:
        out << "Accepted ";
        break;
    case Order::ModifyRequested:
        out << "ModifyRequested ";
        break;
    case Order::ModifyRejected:
        out << "ModifyRejected ";
        break;
    case Order::Modified:
        out << "Modified ";
        break;
    case Order::PartialFilled:
        out << "PartialFilled ";
        break;
    case Order::Filled: 
        out << "Filled "; 
        break;
    case Order::CancelRequested:
        out << "CancelRequested ";
        break;
    case Order::CancelRejected:
        out << "CancelRejected ";
        break;
    case Order::Cancelled: 
        out << "Cancelled "; 
        break;
    case Order::Unknown:
        out << "Unknown ";
        break;
    }
    out << event.description_;
    out << "}";
    return out;
}

std::ostream & operator << (std::ostream & out, const Order & order)
{
    out << "[#" << order.order_id(); 
    out << ' ' << (order.is_buy() ? "BUY" : "SELL");
    out << ' ' << order.order_qty();
    out << ' ' << order.symbol();
    if(order.price() == 0)
    {
        out << " MKT";
    }
    else
    {
        out << " $" << order.price();
    }

    if(order.stop_price() != 0)
    {
       out << " STOP " << order.stop_price();
    }

    out  << (order.all_or_none() ? " AON" : "")
        << (order.immediate_or_cancel() ? " IOC" : "");

    auto onMarket = order.quantityOnMarket();
    if(onMarket != 0)
    {
        out << " Open: " << onMarket;
    }

    auto filled = order.quantityFilled();
    if(filled != 0)
    {
        out << " Filled: " << filled;
    }

    auto cost = order.fillCost();
    if(cost != 0)
    {
        out << " Cost: " << cost;
    }

    if(order.isVerbose())
    {
        const Order::History & history = order.history();
        for(auto event = history.begin(); event != history.end(); ++event)
        {
            out << "\n\t" << *event;
        } 
    }
    else
    {
        out << " Last Event:" << order.currentState();
    }

   out << ']';
   
   return out;
}


}


================================================
FILE: examples/mt_order_entry/Order.h
================================================
// Copyright (c) 2017 Object Computing, Inc.
// All rights reserved.
// See the file license.txt for licensing information.
#pragma once

#include "OrderFwd.h"
#include <book/types.h>

#include <string>
#include <vector>

namespace orderentry
{

class Order
{
public:
    enum State{
        Submitted,
        Rejected, // Terminal state
        Accepted,
        ModifyRequested,
        ModifyRejected,
        Modified,
        PartialFilled,
        Filled, // Terminal State
        CancelRequested,
        CancelRejected,
        Cancelled, // Terminal state
        Unknown
    };

    struct StateChange
    {
        State state_;
        std::string description_;
        StateChange()
          : state_(Unknown)
          {}

        StateChange(State state, const std::string & description = "")
            : state_(state)
            , description_(description)
        {}
    };    
    typedef std::vector<StateChange> History;
public:
    Order(const std::string & id,
        bool buy_side,
        liquibook::book::Quantity quantity,
        std::string symbol,
        liquibook::book::Price price,
        liquibook::book::Price stopPrice,
        bool aon,
        bool ioc);

    //////////////////////////
    // Implement the 
    // liquibook::book::order
    // concept.

    /// @brief is this a limit order?
    bool is_limit() const;

    /// @brief is this order a buy?
    bool is_buy() const;

    /// @brief get the price of this order, or 0 if a market order
    liquibook::book::Price price() const;

    /// @brief get the stop price (if any) for this order.
    /// @returns the stop price or zero if not a stop order
    liquibook::book::Price stop_price() const;

    /// @brief get the quantity of this order
    liquibook::book::Quantity order_qty() const;

    /// @brief if no trades should happen until the order
    /// can be filled completely.
    /// Note: one or more trades may be used to fill the order.
    virtual bool all_or_none() const;

    /// @brief After generating as many trades as possible against
    /// orders already on the market, cancel any remaining quantity.
    virtual bool immediate_or_cancel() const;

    std::string symbol() const;

    std::string order_id() const;

    uint32_t quantityFilled() const;

    uint32_t quantityOnMarket() const;

    uint32_t fillCost() const;

    Order & verbose(bool verbose = true);
    bool isVerbose()const;
    const History & history() const;
    const StateChange & currentState() const;

    ///////////////////////////
    // Order life cycle events
    void onSubmitted();
    void onAccepted();
    void onRejected(const char * reason);

    void onFilled(
        liquibook::book::Quantity fill_qty, 
        liquibook::book::Cost fill_cost);

    void onCancelRequested();
    void onCancelled();
    void onCancelRejected(const char * reason);

    void onReplaceRequested(
        const int32_t& size_delta, 
        liquibook::book::Price new_price);

    void onReplaced(const int32_t& size_delta, 
        liquibook::book::Price new_price);

    void onReplaceRejected(const char * reaseon);

private:
    std::string id_;
    bool buy_side_;
    std::string symbol_;
    liquibook::book::Quantity quantity_;
    liquibook::book::Price price_;
    liquibook::book::Price stopPrice_;

    bool aon_;
    bool ioc_;

    liquibook::book::Quantity quantityFilled_;
    int32_t quantityOnMarket_;
    uint32_t fillCost_;
    
    std::vector<StateChange> history_;
    bool verbose_;
};

std::ostream & operator << (std::ostream & out, const Order & order);
std::ostream & operator << (std::ostream & out, const Order::StateChange & event);

}


================================================
FILE: examples/mt_order_entry/OrderFwd.h
================================================
// Copyright (c) 2017 Object Computing, Inc.
// All rights reserved.
// See the file license.txt for licensing information.
#pragma once
#include <memory>
namespace orderentry
{
    class Order;
    typedef std::shared_ptr<Order> OrderPtr;
}


================================================
FILE: examples/mt_order_entry/TestOneAonBidTwoAsk.script
================================================
## Copyright (c) 2017 Object Computing, Inc.
## All rights reserved.
## See the file license.txt for licensing information.

# Run the One AON bid, two AON ask test
BUY 300 +IBM 1251 AON;
SELL 100 IBM 1251;
SELL 200 IBM 1251;
#expect execution, but currently the test fails
D + ALL


================================================
FILE: examples/mt_order_entry/Util.cpp
================================================
// Copyright (c) 2017 Object Computing, Inc.
// All rights reserved.
// See the file license.txt for licensing information.
#include "Util.h"
#include <iostream>
#include <algorithm>
#include <functional>
#include <cctype>

namespace orderentry
{
std::string nextToken(const std::vector<std::string> & tokens, size_t & pos)
{
    if(pos < tokens.size())
    {
        return tokens[pos++];
    }
    return "";
}

uint32_t toUint32(const std::string & input)
{
    char * end;
    uint32_t value = strtoul(input.c_str(), &end, 10);
    if(*end != '\0')
    {
        value = INVALID_UINT32;
    }
    return value;
}

uint32_t toInt32(const std::string & input)
{
    char * end;
    uint32_t value = strtol(input.c_str(), &end, 10);
    if(*end != '\0')
    {
        value = INVALID_INT32;
    }
    return value;
}


liquibook::book::Price stringToPrice(const std::string & str)
{
    if(str == "MARKET" || str == "MKT")
    {
        return 0;
    } else
    {
        return toUint32(str);
    }
}

std::string promptForString(const std::string & prompt, bool uppercase)
{
    std::cout << "\n" << prompt << ": " << std::flush;
    std::string input;
    std::getline(std::cin, input);
    if(uppercase)
    {
      std::transform(input.begin(), input.end(), input.begin(), toupper);
    }
    return input;
}

liquibook::book::Price promptForPrice(const std::string & prompt)
{
    std::string str = promptForString(prompt);
    return stringToPrice(str);
}

uint32_t promptForUint32(const std::string & prompt)
{
    std::cout << "\n" << prompt << ": " << std::flush;
    std::string input;
    std::getline(std::cin, input);
    return toUint32(input);
}

int32_t promptForInt32(const std::string & prompt)
{
    std::cout << "\n" << prompt << ": " << std::flush;
    std::string input;
    std::getline(std::cin, input);
    return toInt32(input);
}

bool promptForYesNo(const std::string & prompt)
{
    while(true)
    {
        std::string input = promptForString(prompt);
        if(input == "Y" || input == "YES" || input == "T" || input == "TRUE")
        {
            return true;
        }
        if(input == "N" || input == "NO" || input == "F" || input == "FALSE")
        {
            return false;
        }
    }
}

// trim from start (in place)
std::string &  ltrim(std::string &s) {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), 
        std::not1(std::ptr_fun<int, int>(std::isspace))));
    return s;
}

// trim from end (in place)
std::string &  rtrim(std::string &s) {
    s.erase(std::find_if(s.rbegin(), s.rend(), 
        std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
    return s;
}

// trim from both ends (in place)
std::string &  trim(std::string &s) 
{
    return ltrim(rtrim(s));
}

// trim from start (copying)
std::string ltrimmed(std::string s) 
{
    ltrim(s);
    return s;
}

// trim from end (copying)
std::string rtrimmed(std::string s) 
{
    rtrim(s);
    return s;
}

// trim from both ends (copying)
std::string trimmed(std::string s) 
{
    trim(s);
    return s;
}

}



================================================
FILE: examples/mt_order_entry/Util.h
================================================
// Copyright (c) 2017 Object Computing, Inc.
// All rights reserved.
// See the file license.txt for licensing information.

/// @brief Command parsing helpers
#pragma once
#include <book/types.h>
#include <string>
#include <vector>

namespace orderentry
{
static const uint32_t INVALID_UINT32 = UINT32_MAX;
static const int32_t INVALID_INT32 = INT32_MAX;

/// @brief Parse a string into tokens breaking on delimiters.
/// @param input The string to parse.
/// @param delimiter A set of delimiter characters.
/// @param[out] tokens  the tokens parsed from the string.
template<typename INPUT_STRING,typename DELIMITER_STRING,typename STRING_CONTAINER>
void split(const INPUT_STRING & input, const DELIMITER_STRING & delimiter, STRING_CONTAINER & tokens)
{
    size_t pos = 0;
    size_t end = input.length();
    while(pos < end)
    {
        auto last = input.find_first_of(delimiter, pos);
        if(last == std::string::npos)
        {
            last = end;
        }
        tokens.push_back(input.substr(pos, last - pos));
        pos = ++last;
    }
}

/// @brief Get the next token from a vector of tokens.
/// @param tokens The vector of tokens
/// @param[inout] pos the current position in the vector. Will be updated if a token is found.
/// @return the next token or an empty string if no more tokens.
std::string nextToken(const std::vector<std::string> & tokens, size_t & pos);

/// @brief Convert a string to uint32
/// @param input the input string.
/// @return the converted number or INVALID_UINT32 if the string ill-formed.
uint32_t toUint32(const std::string & input);

/// @brief Convert a string to int32
/// @param input the input string.
/// @return the converted number or INVALID_INT32 if the string ill-formed.
uint32_t toInt32(const std::string & input);

/// @brief Convert a string to Price
/// @param input the input string which may be a number or "MARKET", "MKT", etc.
/// @return the converted price (MARKET is 0) or INVALID_UINT32 if the string ill-formed.
liquibook::book::Price stringToPrice(const std::string & input);

/// @brief Display a prompt and ask the console user for a string.
/// @param prompt What to display
/// @returns what the user typed.
std::string promptForString(const std::string & prompt, bool uppercase = true);

/// @brief Display a prompt and ask the console user for a price.
/// @param prompt What to display
/// @returns what the user typed converted to a price.
liquibook::book::Price promptForPrice(const std::string & prompt);

/// @brief Display a prompt and ask the console user for a uint32.
/// @param prompt What to display
/// @returns what the user typed converted to a uint32.
uint32_t promptForUint32(const std::string & prompt);

/// @brief Display a prompt and ask the console user for a int32.
/// @param prompt What to display
/// @returns what the user typed converted to a int32.
int32_t promptForInt32(const std::string & prompt);

/// @brief Display a prompt and ask the console user for "yes" or "no"
/// @param prompt What to display
/// @returns what the user typed converted to a bool.
bool promptForYesNo(const std::string & prompt);

/// @brief Remove whitespace from the start of a string (in place)
/// @param[inout] s is the string to be trimmed in place.
/// @returns a reference to the string.
std::string & ltrim(std::string &s);

/// @brief Remove whitespace from the end of a string (in place)
/// @param[inout] s is the string to be trimmed in place.
/// @returns a reference to the string.
std::string & rtrim(std::string &s);

/// @brief Remove whitespace from both ends of a string (in place)
/// @param[inout] s is the string to be trimmed in place.
/// @returns a reference to the string.
std::string & trim(std::string &s);

/// @brief Remove whitespace from the start of a constant string
/// @param s is the string to be trimmed.
/// @return the string after trimming.
std::string ltrimmed(std::string s);

/// @brief Remove whitespace from the end of a constant string
/// @param s is the string to be trimmed
/// @return the string after trimming.
std::string rtrimmed(std::string s);

/// @brief Remove whitespace from both ends of a constant string
/// @param s is the string to be trimmed.
/// @return the string after trimming.
std::string trimmed(std::string s);

}



================================================
FILE: examples/mt_order_entry/mt_order_entry.mpc
================================================
// Copyright (c) 2017 Object Computing, Inc.
// All rights reserved.
// See the file license.txt for licensing information.
project(*) : liquibook_book, liquibook_simple, liquibook_exe {
  requires += example_manual
  exename = *
}


================================================
FILE: examples/mt_order_entry/mt_order_entry_main.cpp
================================================
// Copyright (c) 2017 Object Computing, Inc.
// All rights reserved.
// See the file license.txt for licensing information.
#include "Market.h"
#include "Util.h"
#include <fstream>
#include <iomanip>
#include <string>
#include <locale>
#include <cstring>
#include <algorithm> 
#include <vector>
#include <iterator>

using namespace orderentry;

int main(int argc, const char * argv[])
{
    bool done = false;
    bool prompt = true;
    bool interactive = true;
    bool fileActive = false;
    std::ostream * log = &std::cout;
    std::ifstream commandFile;
    std::ofstream logFile;

    if(argc > 1)
    {
        std::string filename = argv[1];
        if(filename != "-")
        {
            commandFile.open(filename);
            if(!commandFile.good())
            {
                std::cerr << "Can't open command file " << filename << ". Exiting." << std::endl;
                return -1;
            }
            interactive = false;
            fileActive = true;
        }
    }
    if(argc > 2)
    {
        const char * filename = argv[2];
        logFile.open(filename);
        if(!logFile.good())
        {
            std::cerr << "Can't open log file " << filename << ". Exiting." << std::endl;
            return -1;
        }
        log = & logFile;
    }

    Market market(log);
    while( !done)
    {
        std::string input;
        if(fileActive) 
        {
            std::getline(commandFile, input);
            if(!commandFile.good())
            {
                if(interactive)
                {
                    input = "# Switching to console input.";               
                    fileActive = false;
                }
                else
                {
                    input = "# end of command file.";
                    done = true;
                }
            }
            // if it came from a file, echo it to the log
            if(input.substr(0,2) != "##") // don't log ## comments.
            {
                *log << input << std::endl;
            }
        }
        else
        {
            if(prompt)
            {
                std::cout << "Action[" << Market::prompt() 
                << "\t(?)    help for more options and detail.\n"
                << "\t(Quit) ]\n";
                prompt = false;
            }
            std::cout << "> " << std::flush;
            std::getline(std::cin, input);
        }
        std::transform(input.begin(), input.end(), input.begin(), toupper);
        if(log != &std::cout && !fileActive)
        {
            if(input.substr(0,2) != "##") // don't log ## comments.
            {
                *log << input << std::endl;
            }
        }

        // if input ends in a ';' be sure there's a space before it to simplify parsing.
        if(input.length() > 1)
        {
            if(input.back() == ';')
            {
                input.pop_back();
                if(input.back() == ' ')
                {
                    input.pop_back();
                }
                input.append(" ;");
            }
        }

        std::vector< std::string> words;
        split(input," \t\v\n\r", words);
        if(!words.empty())
        {        
            const std::string command = words[0];
            if(command == "QUIT")
            {
                done = true;
            }
            else if(command[0] == '#')
            {
                // nothing
            }
            else if(command == "F" || command == "FILE")
            {
                if(fileActive)
                {
                    std::cout << "Only one input file at a time can be open." << std::endl;
                }
                else
                {
                    std::cout << "Command file name: " << std::flush;
                    std::string filename;
                    std::getline(std::cin, filename);
                    commandFile.open(filename);
                    if(commandFile.good())
                    {
                        fileActive = true;
                    }
                    else
                    {
                        std::cout << "Cannot open " << filename << std::endl;
                    }
                }
            }
            else if(command == "?" || command == "HELP")
            {
                market.help();
                *log << "(F)ile  Open or Close a command input file\n"
                    << "\tArguments\n"
                    << "\t\t<FileName>  Required if no file is open. Must not appear if file is open.\n";
                *log << "QUIT  Exit from this program.\n";
                bool prompt = true;
            }
            else if(!market.apply(words))
            {
                std::cerr << "Cannot process command";
                for(auto word = words.begin(); word != words.end(); ++ word)
                {
                    std::cerr << ' ' << *word;
                }
                std::cerr << std::endl;
                bool prompt = true;
            }
        }
    }
    return 0;
}


================================================
FILE: examples/mt_order_entry/order_entry.script
================================================
# Copyright (c) 2017 Object Computing, Inc.
# All rights reserved.
# See the file license.txt for licensing information.
## A simple test script to use with mt_order_entry.
## To use it: make this file's name the first argument on the command line, or
## type "FILE filename" at the interactive prompt.
BUY 100 !IBM 50;
SELL 100 IBM 50;
D + ALL


================================================
FILE: examples/mt_order_entry/teststoporders.script
================================================
# Script to test stop orders

# set market price to 56
buy 100 +ibm mkt;
sell 100 ibm 56;

# enter seed orders that won't trade with each other
buy 100 ibm 53;
sell 100 ibm 58;

# add stop orders off market
buy 100 ibm mkt stop 57;
sell 100 ibm mkt stop 54;

# use large AOC orders to set a new market price to 57
# without trading with existing orders
# This will trigger the buy stop market order (#5)
# which will match (and trade with) the sell 58 limit order #4
buy 1000 ibm 57 aon;
sell 1000 ibm 57 aon;

# do the same thing on the other side to 
# hit the stop price of 54 for order #6
sell 1000 ibm 54 aon;
buy 1000 ibm 54 aon;

# and display the orders, all of which should be filled.
d ibm
quit


================================================
FILE: license.txt
================================================
Copyright (c) 2012, 2013 Object Computing, Inc. All rights reserved.

Liquibook is a licensed product, is protected by copyright, and is distributed under the following terms:

Liquibook is an open source implementation of a limit order book matching engine, developed and copyrighted by Object Computing Incorporated (OCI). 

Since Liquibook is open source and free of licensing fees, you are free to use, modify, and distribute the source code, as long as you include this copyright statement.

In particular, you can use Liquibook to build proprietary software and are under no obligation to redistribute any of your source code that is built using Liquibook. Note however, that you may not do anything to the Liquibook code, such as copyrighting it yourself or claiming authorship of the Liquibook code, that will prevent Liquibook from being distributed freely using an open source development model.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of Object Computing, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.

IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: liquibook.features
================================================
// Copyright (c) 2013-2017 Object Computing, Inc.
// All rights reserved.
// See the file license.txt for licensing information.
// This file defines MPC features for the liquibook project

example_pubsub=1
example_manual=1

// BOOST IS NEEDED BY THE  unit tests
boost=1
// QUICKFAST IS NEEDED BY THE PUBLISH/SUBSUBSCRIBE example
QuickFAST=1



================================================
FILE: liquibook.mwc
================================================
workspace(liquibook) {
  cmdline += -include mpc 
  cmdline += -include $QUICKFAST_ROOT
  cmdline += -feature_file liquibook.features
  cmdline += -expand_vars
  cmdline += -use_env
  
  specific(make) {
    cmdline += -value_template "configurations=Release Debug"
  }  
}



================================================
FILE: mpc/liquibook.mpb
================================================
project {
  includes += $(LIQUIBOOK_ROOT)/src
  libpaths += $(LIQUIBOOK_ROOT)/lib

  // Force use of Visual C++ DLL runtime libraries
  specific(vc71,vc8,vc9) {
    Debug::runtime_library = 3
    Release::runtime_library = 2
  }
  specific(vc10,vc11,vc12,vc13,vc14,vc15) {
    Debug::runtime_library = MultiThreadedDebugDLL
    Release::runtime_library = MultiThreadedDLL
  }
}



================================================
FILE: mpc/liquibook_book.mpb
================================================
project : liquibook {
    // obsolete
}



================================================
FILE: mpc/liquibook_exe.mpb
================================================
project {
  specific(prop:microsoft) {
    Release::exeout = $(LIQUIBOOK_ROOT)/Output/Release
    Debug::exeout = $(LIQUIBOOK_ROOT)/Output/Debug
  } else {
    exeout = $(LIQUIBOOK_ROOT)/bin
  }
}


================================================
FILE: mpc/liquibook_lib.mpb
================================================
project : liquibook {
  libout = $(LIQUIBOOK_ROOT)/lib
  macros += LIQUIBOOK_BUILD_DLL  
}



================================================
FILE: mpc/liquibook_simple.mpb
================================================
project : liquibook {
  libs += liquibook_simple
  after += liquibook_simple
}



================================================
FILE: mpc/liquibook_test.mpb
================================================
project : liquibook, liquibook_exe, liquibook_book, liquibook_simple{
  exeout = $(LIQUIBOOK_ROOT)/bin/test
}


================================================
FILE: mpc.bat
================================================
@REM Copyright (c) 2009, 2010 Object Computing, Inc.
@REM All rights reserved.
@REM See the file license.txt for licensing information.
@REM
@REM This batch file runs MWC which is part of the MPC package to create
@REM Visual Studio solution and project files.
@REM
@REM Note: -expand_vars -use_env options force MPC to expand $(BOOST_ROOT) into the absolute path.  This avoids
@REM        a problem that happened when people were starting Visual Studio from the Start menu rather than
@REM        from the command line where the BOOST_ROOT environment had been defined.
"%MPC_ROOT%\mwc.pl" -type vc%VCVER% liquibook.mwc


================================================
FILE: noQuickFAST/QuickFASTApplication.mpb
================================================
project{
  requires += QuickFAST
}


================================================
FILE: pt_run.bat
================================================
bin\test\pt_order_book.exe


================================================
FILE: src/book/bbo_listener.h
================================================
// Copyright (c) 2012, 2013 Object Computing, Inc.
// All rights reserved.
// See the file license.txt for licensing information.
#pragma once

namespace liquibook { namespace book {

/// @brief generic listener of top-of-book events
template <class OrderBook>
class BboListener {
public:
  /// @brief callback for top of book change
  virtual void on_bbo_change(
      const OrderBook* book, 
      const typename OrderBook::DepthTracker* depth) = 0;
};

} }


================================================
FILE: src/book/callback.h
================================================
// Copyright (c) 2012, 2013 Object Computing, Inc.
// All rights reserved.
// See the file license.txt for licensing information.
#pragma once

#include "types.h"

namespace liquibook { namespace book {

template <class OrderPtr>
class OrderBook;

// Callback events
//   New order accept
//     - order accept
//     - fill (2) and/or quote (if not complete)
//     - depth/bbo ?
//   New order reject
//     - order reject
//   Order fill
//     - fill (2)
//     - trade
//     - quote (2)
//     - depth/bbo ?
//   Order cancel
//     - order cancel
//     - quote
//     - depth/bbo ?
//   Order cancel reject
//     - order cancel reject
//   Order replace
//     - order replace
//     - fill (2) and/or quote (if not complete)
//     - depth/bbo ?
//   Order replace reject
//     - order replace reject

/// @brief notification from OrderBook of an event
template <typename OrderPtr>
class Callback {
public:
  typedef OrderBook<OrderPtr > TypedOrderBook;

  enum CbType {
    cb_unknown,
    cb_order_accept,
    cb_order_accept_stop,
    cb_order_trigger_stop,
    cb_order_reject,
    cb_order_fill,
    cb_order_cancel,
    cb_order_cancel_stop,
    cb_order_cancel_reject,
    cb_order_replace,
    cb_order_replace_reject,
    cb_book_update
  };

  enum FillFlags {
    ff_neither_filled = 0,
    ff_inbound_filled = 1,
    ff_matched_filled = 2,
    ff_both_filled    = 4
  };

  Callback();

  /// @brief create a new accept callback
  static Callback<OrderPtr> accept(const OrderPtr& order);
  /// @brief create a new accept callback
  static Callback<OrderPtr> accept_stop(const OrderPtr& order);
  /// @brief create a new accept callback
  static Callback<OrderPtr> trigger_stop(const OrderPtr& order);
  /// @brief create a new reject callback
  static Callback<OrderPtr> reject(const OrderPtr& order,
                                   const char* reason);
  /// @brief create a new fill callback
  static Callback<OrderPtr> fill(const OrderPtr& inbound_order,
                                 const OrderPtr& matched_order,
                                 const Quantity& fill_qty,
                                 const Price& fill_price,
                                 FillFlags fill_flags);
  /// @brief create a new cancel callback
Download .txt
gitextract_uq787oa9/

├── .gitignore
├── PERFORMANCE.md
├── README.md
├── README_ORDER_ENTRY.md
├── doc/
│   └── settAug2013/
│       ├── liquibook_sett.html
│       ├── settAug2013_files/
│       │   └── paper.css
│       └── styles/
│           └── SETT.css
├── env.sh
├── examples/
│   ├── .gitignore
│   └── mt_order_entry/
│       ├── Market.cpp
│       ├── Market.h
│       ├── Order.cpp
│       ├── Order.h
│       ├── OrderFwd.h
│       ├── TestOneAonBidTwoAsk.script
│       ├── Util.cpp
│       ├── Util.h
│       ├── mt_order_entry.mpc
│       ├── mt_order_entry_main.cpp
│       ├── order_entry.script
│       └── teststoporders.script
├── license.txt
├── liquibook.features
├── liquibook.mwc
├── mpc/
│   ├── liquibook.mpb
│   ├── liquibook_book.mpb
│   ├── liquibook_exe.mpb
│   ├── liquibook_lib.mpb
│   ├── liquibook_simple.mpb
│   └── liquibook_test.mpb
├── mpc.bat
├── noQuickFAST/
│   └── QuickFASTApplication.mpb
├── pt_run.bat
├── src/
│   ├── book/
│   │   ├── bbo_listener.h
│   │   ├── callback.h
│   │   ├── comparable_price.h
│   │   ├── depth.h
│   │   ├── depth_constants.h
│   │   ├── depth_level.h
│   │   ├── depth_listener.h
│   │   ├── depth_order_book.h
│   │   ├── liquibook.mpc
│   │   ├── logger.h
│   │   ├── main.cpp
│   │   ├── order.h
│   │   ├── order_book.h
│   │   ├── order_book_listener.h
│   │   ├── order_listener.h
│   │   ├── order_tracker.h
│   │   ├── trade_listener.h
│   │   ├── types.h
│   │   └── version.h
│   ├── liquibook_export.h
│   └── simple/
│       ├── liquibook_simple.mpc
│       ├── simple_order.cpp
│       ├── simple_order.h
│       └── simple_order_book.h
├── test/
│   ├── .gitignore
│   ├── latency/
│   │   ├── clock_gettime.h
│   │   ├── liquibook_latency.mpc
│   │   └── lt_order_book.cpp
│   ├── perf/
│   │   ├── liquibook_perf.mpc
│   │   └── pt_order_book.cpp
│   └── unit/
│       ├── changed_checker.h
│       ├── depth_check.h
│       ├── liquibook_unit.mpc
│       ├── ut_all_or_none.cpp
│       ├── ut_bbo_order_book.cpp
│       ├── ut_depth.cpp
│       ├── ut_immediate_or_cancel.cpp
│       ├── ut_listeners.cpp
│       ├── ut_main.cpp
│       ├── ut_market_price.cpp
│       ├── ut_order_book.cpp
│       ├── ut_order_book_shared_ptr.cpp
│       ├── ut_stop_orders.cpp
│       └── ut_utils.h
├── ut_run.bat
├── web/
│   ├── css/
│   │   └── liquibook.css
│   ├── easy.html
│   ├── fast.html
│   ├── flexible.html
│   ├── fluid.html
│   ├── get-started.html
│   ├── index.html
│   └── sub-template.html
├── winenv.bat
└── winenv_clear.bat
Download .txt
SYMBOL INDEX (252 symbols across 44 files)

FILE: examples/mt_order_entry/Market.cpp
  function displayDepthLevel (line 14) | void displayDepthLevel(std::ostream & out, const liquibook::book::DepthL...
  function publishDepth (line 27) | void publishDepth(std::ostream & out, const orderentry::BookDepth & depth)
  type orderentry (line 71) | namespace orderentry
    function OrderBookPtr (line 550) | OrderBookPtr
    function OrderBookPtr (line 574) | OrderBookPtr

FILE: examples/mt_order_entry/Market.h
  function namespace (line 17) | namespace orderentry

FILE: examples/mt_order_entry/Order.cpp
  type orderentry (line 7) | namespace orderentry
    function Order (line 121) | Order &

FILE: examples/mt_order_entry/Order.h
  function namespace (line 12) | namespace orderentry

FILE: examples/mt_order_entry/OrderFwd.h
  function namespace (line 6) | namespace orderentry

FILE: examples/mt_order_entry/Util.cpp
  type orderentry (line 10) | namespace orderentry
    function nextToken (line 12) | std::string nextToken(const std::vector<std::string> & tokens, size_t ...
    function toUint32 (line 21) | uint32_t toUint32(const std::string & input)
    function toInt32 (line 32) | uint32_t toInt32(const std::string & input)
    function stringToPrice (line 44) | liquibook::book::Price stringToPrice(const std::string & str)
    function promptForString (line 55) | std::string promptForString(const std::string & prompt, bool uppercase)
    function promptForPrice (line 67) | liquibook::book::Price promptForPrice(const std::string & prompt)
    function promptForUint32 (line 73) | uint32_t promptForUint32(const std::string & prompt)
    function promptForInt32 (line 81) | int32_t promptForInt32(const std::string & prompt)
    function promptForYesNo (line 89) | bool promptForYesNo(const std::string & prompt)
    function ltrimmed (line 126) | std::string ltrimmed(std::string s)
    function rtrimmed (line 133) | std::string rtrimmed(std::string s)
    function trimmed (line 140) | std::string trimmed(std::string s)

FILE: examples/mt_order_entry/Util.h
  function namespace (line 11) | namespace orderentry

FILE: examples/mt_order_entry/mt_order_entry_main.cpp
  function main (line 17) | int main(int argc, const char * argv[])

FILE: src/book/bbo_listener.h
  function namespace (line 6) | namespace liquibook { namespace book {

FILE: src/book/callback.h
  function namespace (line 8) | namespace liquibook { namespace book {

FILE: src/book/comparable_price.h
  function namespace (line 9) | namespace liquibook { namespace book {

FILE: src/book/depth.h
  function namespace (line 14) | namespace liquibook { namespace book {

FILE: src/book/depth_constants.h
  function namespace (line 8) | namespace liquibook { namespace book {

FILE: src/book/depth_level.h
  function namespace (line 8) | namespace liquibook { namespace book {

FILE: src/book/depth_listener.h
  function namespace (line 8) | namespace liquibook { namespace book {

FILE: src/book/depth_order_book.h
  function namespace (line 11) | namespace liquibook { namespace book {

FILE: src/book/logger.h
  function namespace (line 8) | namespace liquibook { namespace book {

FILE: src/book/main.cpp
  function main (line 16) | int main(int, const char**)

FILE: src/book/order.h
  function namespace (line 8) | namespace liquibook { namespace book {

FILE: src/book/order_book.h
  function namespace (line 38) | namespace liquibook { namespace book {
  function ComparablePrice (line 584) | ComparablePrice key(isBuy, tracker.ptr()->stop_price());

FILE: src/book/order_book_listener.h
  function namespace (line 8) | namespace liquibook { namespace book {

FILE: src/book/order_listener.h
  function namespace (line 6) | namespace liquibook { namespace book {

FILE: src/book/order_tracker.h
  function namespace (line 8) | namespace liquibook { namespace book {

FILE: src/book/trade_listener.h
  function namespace (line 6) | namespace liquibook { namespace book {

FILE: src/book/types.h
  function namespace (line 10) | namespace liquibook { namespace book {

FILE: src/book/version.h
  function namespace (line 6) | namespace liquibook {

FILE: src/simple/simple_order.cpp
  type liquibook (line 8) | namespace liquibook { namespace simple {
    type simple (line 8) | namespace simple {
      function OrderState (line 30) | const OrderState&

FILE: src/simple/simple_order.h
  function namespace (line 10) | namespace liquibook { namespace simple {

FILE: src/simple/simple_order_book.h
  function namespace (line 10) | namespace liquibook { namespace simple {

FILE: test/latency/clock_gettime.h
  function LARGE_INTEGER (line 14) | LARGE_INTEGER
  function clock_gettime (line 35) | int

FILE: test/latency/lt_order_book.cpp
  function build_histogram (line 19) | void build_histogram(timespec* timestamps, int count) {
  function run_test (line 54) | int run_test(TypedOrderBook& order_book, TypedOrder** orders,
  function build_and_run_test (line 82) | bool build_and_run_test(uint32_t num_to_try, bool dry_run = false) {
  function main (line 135) | int main(int argc, const char* argv[])

FILE: test/perf/pt_order_book.cpp
  function run_test (line 20) | int run_test(TypedOrderBook& order_book, TypedOrder** orders, clock_t en...
  function build_and_run_test (line 35) | bool build_and_run_test(uint32_t dur_sec, uint32_t num_to_try) {
  function main (line 96) | int main(int argc, const char* argv[])

FILE: test/unit/changed_checker.h
  function namespace (line 7) | namespace liquibook {

FILE: test/unit/depth_check.h
  function namespace (line 12) | namespace liquibook {

FILE: test/unit/ut_all_or_none.cpp
  type liquibook (line 10) | namespace liquibook {
    function BOOST_AUTO_TEST_CASE (line 44) | BOOST_AUTO_TEST_CASE(TestRegBidMatchAon)
    function BOOST_AUTO_TEST_CASE (line 87) | BOOST_AUTO_TEST_CASE(TestRegBidMatchMulti)
    function BOOST_AUTO_TEST_CASE (line 130) | BOOST_AUTO_TEST_CASE(TestAonBidNoMatch)
    function BOOST_AUTO_TEST_CASE (line 172) | BOOST_AUTO_TEST_CASE(TestAonBidMatchReg)
    function BOOST_AUTO_TEST_CASE (line 213) | BOOST_AUTO_TEST_CASE(TestAonBidMatchMulti)
    function BOOST_AUTO_TEST_CASE (line 263) | BOOST_AUTO_TEST_CASE(TestAonBidNoMatchMulti)
    function BOOST_AUTO_TEST_CASE (line 306) | BOOST_AUTO_TEST_CASE(TestAonBidMatchAon)
    function BOOST_AUTO_TEST_CASE (line 346) | BOOST_AUTO_TEST_CASE(TestRegAskMatchAon)
    function BOOST_AUTO_TEST_CASE (line 389) | BOOST_AUTO_TEST_CASE(TestRegAskMatchMulti)
    function BOOST_AUTO_TEST_CASE (line 443) | BOOST_AUTO_TEST_CASE(TestAonAskNoMatch)
    function BOOST_AUTO_TEST_CASE (line 489) | BOOST_AUTO_TEST_CASE(TestAonAskMatchReg)
    function BOOST_AUTO_TEST_CASE (line 529) | BOOST_AUTO_TEST_CASE(TestAonAskMatchMulti)
    function BOOST_AUTO_TEST_CASE (line 586) | BOOST_AUTO_TEST_CASE(TestOneAonBidOneAonAsk)
    function BOOST_AUTO_TEST_CASE (line 615) | BOOST_AUTO_TEST_CASE(TestTwoAonBidOneAonAsk)
    function BOOST_AUTO_TEST_CASE (line 648) | BOOST_AUTO_TEST_CASE(TestOneAonBidTwoAsk)
    function BOOST_AUTO_TEST_CASE (line 683) | BOOST_AUTO_TEST_CASE(TestOneBidTwoAonAsk)
    function BOOST_AUTO_TEST_CASE (line 716) | BOOST_AUTO_TEST_CASE(TestTwoAonBidTwoAonAsk)
    function BOOST_AUTO_TEST_CASE (line 764) | BOOST_AUTO_TEST_CASE(TestAonAskNoMatchMulti)
    function BOOST_AUTO_TEST_CASE (line 817) | BOOST_AUTO_TEST_CASE(TestAonAskMatchAon)
    function BOOST_AUTO_TEST_CASE (line 857) | BOOST_AUTO_TEST_CASE(TestReplaceAonBidSmallerMatch)
    function BOOST_AUTO_TEST_CASE (line 903) | BOOST_AUTO_TEST_CASE(TestReplaceAonBidPriceMatch)
    function BOOST_AUTO_TEST_CASE (line 949) | BOOST_AUTO_TEST_CASE(TestReplaceBidLargerMatchAon)

FILE: test/unit/ut_bbo_order_book.cpp
  type liquibook (line 18) | namespace liquibook {
    function BOOST_AUTO_TEST_CASE (line 31) | BOOST_AUTO_TEST_CASE(TestBboBidsMultimapSortCorrect)
    function BOOST_AUTO_TEST_CASE (line 70) | BOOST_AUTO_TEST_CASE(TestBboAsksMultimapSortCorrect)
    function BOOST_AUTO_TEST_CASE (line 110) | BOOST_AUTO_TEST_CASE(TestBboAddCompleteBid)
    function BOOST_AUTO_TEST_CASE (line 149) | BOOST_AUTO_TEST_CASE(TestBboAddCompleteAsk)
    function BOOST_AUTO_TEST_CASE (line 186) | BOOST_AUTO_TEST_CASE(TestBboAddMultiMatchBid)
    function BOOST_AUTO_TEST_CASE (line 231) | BOOST_AUTO_TEST_CASE(TestBboAddMultiMatchAsk)
    function BOOST_AUTO_TEST_CASE (line 280) | BOOST_AUTO_TEST_CASE(TestBboAddPartialMatchBid)
    function BOOST_AUTO_TEST_CASE (line 325) | BOOST_AUTO_TEST_CASE(TestBboAddPartialMatchAsk)
    function BOOST_AUTO_TEST_CASE (line 371) | BOOST_AUTO_TEST_CASE(TestBboAddMultiPartialMatchBid)
    function BOOST_AUTO_TEST_CASE (line 417) | BOOST_AUTO_TEST_CASE(TestBboAddMultiPartialMatchAsk)
    function BOOST_AUTO_TEST_CASE (line 465) | BOOST_AUTO_TEST_CASE(TestBboRepeatMatchBid)
    function BOOST_AUTO_TEST_CASE (line 530) | BOOST_AUTO_TEST_CASE(TestBboRepeatMatchAsk)
    function BOOST_AUTO_TEST_CASE (line 597) | BOOST_AUTO_TEST_CASE(TestBboAddMarketOrderBid)
    function BOOST_AUTO_TEST_CASE (line 636) | BOOST_AUTO_TEST_CASE(TestBboAddMarketOrderAsk)
    function BOOST_AUTO_TEST_CASE (line 675) | BOOST_AUTO_TEST_CASE(TestBboAddMarketOrderBidMultipleMatch)
    function BOOST_AUTO_TEST_CASE (line 715) | BOOST_AUTO_TEST_CASE(TestBboAddMarketOrderAskMultipleMatch)
    function BOOST_AUTO_TEST_CASE (line 755) | BOOST_AUTO_TEST_CASE(TestBboMatchMarketOrderBid)
    function BOOST_AUTO_TEST_CASE (line 792) | BOOST_AUTO_TEST_CASE(TestBboMatchMarketOrderAsk)
    function BOOST_AUTO_TEST_CASE (line 828) | BOOST_AUTO_TEST_CASE(TestBboMatchMultipleMarketOrderBid)
    function BOOST_AUTO_TEST_CASE (line 869) | BOOST_AUTO_TEST_CASE(TestBboMatchMultipleMarketOrderAsk)
    function BOOST_AUTO_TEST_CASE (line 909) | BOOST_AUTO_TEST_CASE(TestBboCancelBid)
    function BOOST_AUTO_TEST_CASE (line 943) | BOOST_AUTO_TEST_CASE(TestBboCancelAskAndMatch)
    function BOOST_AUTO_TEST_CASE (line 1000) | BOOST_AUTO_TEST_CASE(TestBboCancelBidFail)
    function BOOST_AUTO_TEST_CASE (line 1045) | BOOST_AUTO_TEST_CASE(TestBboCancelAskFail)
    function BOOST_AUTO_TEST_CASE (line 1092) | BOOST_AUTO_TEST_CASE(TestBboCancelBidRestore)
    function BOOST_AUTO_TEST_CASE (line 1184) | BOOST_AUTO_TEST_CASE(TestBboCancelAskRestore)
    function BOOST_AUTO_TEST_CASE (line 1274) | BOOST_AUTO_TEST_CASE(TestBboFillCompleteBidRestoreDepth)
    function BOOST_AUTO_TEST_CASE (line 1396) | BOOST_AUTO_TEST_CASE(TestBboFillCompleteAskRestoreDepth)
    function BOOST_AUTO_TEST_CASE (line 1529) | BOOST_AUTO_TEST_CASE(TestBboReplaceSizeDecrease)
    function BOOST_AUTO_TEST_CASE (line 1570) | BOOST_AUTO_TEST_CASE(TestBboReplaceSizeDecreaseCancel)
    function BOOST_AUTO_TEST_CASE (line 1656) | BOOST_AUTO_TEST_CASE(TestBboReplaceSizeDecreaseTooMuch)
    function BOOST_AUTO_TEST_CASE (line 1705) | BOOST_AUTO_TEST_CASE(TestBboReplaceSizeIncreaseDecrease)
    function BOOST_AUTO_TEST_CASE (line 1739) | BOOST_AUTO_TEST_CASE(TestBboReplaceBidPriceChange)
    function BOOST_AUTO_TEST_CASE (line 1799) | BOOST_AUTO_TEST_CASE(TestBboReplaceAskPriceChange)

FILE: test/unit/ut_depth.cpp
  type liquibook (line 12) | namespace liquibook {
    function verify_level (line 19) | bool verify_level(const DepthLevel*& level,
    function BOOST_AUTO_TEST_CASE (line 41) | BOOST_AUTO_TEST_CASE(TestAddBid)
    function BOOST_AUTO_TEST_CASE (line 51) | BOOST_AUTO_TEST_CASE(TestAddBids)
    function BOOST_AUTO_TEST_CASE (line 63) | BOOST_AUTO_TEST_CASE(TestAppendBidLevels)
    function BOOST_AUTO_TEST_CASE (line 78) | BOOST_AUTO_TEST_CASE(TestInsertBidLevels)
    function BOOST_AUTO_TEST_CASE (line 127) | BOOST_AUTO_TEST_CASE(TestInsertBidLevelsPast5)
    function BOOST_AUTO_TEST_CASE (line 179) | BOOST_AUTO_TEST_CASE(TestInsertBidLevelsTruncate5)
    function BOOST_AUTO_TEST_CASE (line 238) | BOOST_AUTO_TEST_CASE(TestCloseBid)
    function BOOST_AUTO_TEST_CASE (line 257) | BOOST_AUTO_TEST_CASE(TestCloseEraseBid)
    function BOOST_AUTO_TEST_CASE (line 289) | BOOST_AUTO_TEST_CASE(TestAddCloseAddBid)
    function BOOST_AUTO_TEST_CASE (line 309) | BOOST_AUTO_TEST_CASE(TestAddCloseAddHigherBid)
    function BOOST_AUTO_TEST_CASE (line 330) | BOOST_AUTO_TEST_CASE(TestCloseBidsFreeLevels)
    function BOOST_AUTO_TEST_CASE (line 404) | BOOST_AUTO_TEST_CASE(TestIncreaseBid)
    function BOOST_AUTO_TEST_CASE (line 442) | BOOST_AUTO_TEST_CASE(TestDecreaseBid)
    function BOOST_AUTO_TEST_CASE (line 480) | BOOST_AUTO_TEST_CASE(TestIncreaseDecreaseBid)
    function BOOST_AUTO_TEST_CASE (line 525) | BOOST_AUTO_TEST_CASE(TestAddAsk)
    function BOOST_AUTO_TEST_CASE (line 535) | BOOST_AUTO_TEST_CASE(TestAddAsks)
    function BOOST_AUTO_TEST_CASE (line 554) | BOOST_AUTO_TEST_CASE(TestAppendAskLevels)
    function BOOST_AUTO_TEST_CASE (line 579) | BOOST_AUTO_TEST_CASE(TestInsertAskLevels)
    function BOOST_AUTO_TEST_CASE (line 613) | BOOST_AUTO_TEST_CASE(TestInsertAskLevelsPast5)
    function BOOST_AUTO_TEST_CASE (line 665) | BOOST_AUTO_TEST_CASE(TestInsertAskLevelsTruncate5)
    function BOOST_AUTO_TEST_CASE (line 724) | BOOST_AUTO_TEST_CASE(TestCloseAsk)
    function BOOST_AUTO_TEST_CASE (line 740) | BOOST_AUTO_TEST_CASE(TestCloseEraseAsk)
    function BOOST_AUTO_TEST_CASE (line 767) | BOOST_AUTO_TEST_CASE(TestAddCloseAddAsk)
    function BOOST_AUTO_TEST_CASE (line 787) | BOOST_AUTO_TEST_CASE(TestAddCloseAddHigherAsk)
    function BOOST_AUTO_TEST_CASE (line 807) | BOOST_AUTO_TEST_CASE(TestCloseAsksFreeLevels)
    function BOOST_AUTO_TEST_CASE (line 870) | BOOST_AUTO_TEST_CASE(TestIncreaseAsk)
    function BOOST_AUTO_TEST_CASE (line 907) | BOOST_AUTO_TEST_CASE(TestDecreaseAsk)
    function BOOST_AUTO_TEST_CASE (line 944) | BOOST_AUTO_TEST_CASE(TestIncreaseDecreaseAsk)
    function BOOST_AUTO_TEST_CASE (line 989) | BOOST_AUTO_TEST_CASE(TestReplaceBid)
    function BOOST_AUTO_TEST_CASE (line 1030) | BOOST_AUTO_TEST_CASE(TestReplaceAsk)

FILE: test/unit/ut_immediate_or_cancel.cpp
  type liquibook (line 10) | namespace liquibook {
    function BOOST_AUTO_TEST_CASE (line 18) | BOOST_AUTO_TEST_CASE(TestIocBidNoMatch)
    function BOOST_AUTO_TEST_CASE (line 68) | BOOST_AUTO_TEST_CASE(TestIocBidPartialMatch)
    function BOOST_AUTO_TEST_CASE (line 120) | BOOST_AUTO_TEST_CASE(TestIocBidFullMatch)
    function BOOST_AUTO_TEST_CASE (line 173) | BOOST_AUTO_TEST_CASE(TestIocBidMultiMatch)
    function BOOST_AUTO_TEST_CASE (line 224) | BOOST_AUTO_TEST_CASE(TestFokBidNoMatch)
    function BOOST_AUTO_TEST_CASE (line 274) | BOOST_AUTO_TEST_CASE(TestFokBidPartialMatch)
    function BOOST_AUTO_TEST_CASE (line 327) | BOOST_AUTO_TEST_CASE(TestFokBidFullMatch)
    function BOOST_AUTO_TEST_CASE (line 380) | BOOST_AUTO_TEST_CASE(TestFokBidMultiMatch)
    function BOOST_AUTO_TEST_CASE (line 431) | BOOST_AUTO_TEST_CASE(TestIocAskNoMatch)
    function BOOST_AUTO_TEST_CASE (line 481) | BOOST_AUTO_TEST_CASE(TestIocAskPartialMatch)
    function BOOST_AUTO_TEST_CASE (line 533) | BOOST_AUTO_TEST_CASE(TestIocAskFullMatch)
    function BOOST_AUTO_TEST_CASE (line 585) | BOOST_AUTO_TEST_CASE(TestIocAskMultiMatch)
    function BOOST_AUTO_TEST_CASE (line 636) | BOOST_AUTO_TEST_CASE(TestFokAskNoMatch)
    function BOOST_AUTO_TEST_CASE (line 686) | BOOST_AUTO_TEST_CASE(TestFokAskPartialMatch)
    function BOOST_AUTO_TEST_CASE (line 739) | BOOST_AUTO_TEST_CASE(TestFokAskFullMatch)
    function BOOST_AUTO_TEST_CASE (line 791) | BOOST_AUTO_TEST_CASE(TestFokAskMultiMatch)

FILE: test/unit/ut_listeners.cpp
  type liquibook (line 13) | namespace liquibook {
    class TradeCbListener (line 23) | class TradeCbListener : public TradeListener<TypedOrderBook>
      method on_trade (line 26) | virtual void on_trade(const TypedOrderBook* order_book,
      method reset (line 34) | void reset()
    class OrderCbListener (line 43) | class OrderCbListener : public OrderListener<OrderPtr>
      method on_accept (line 46) | virtual void on_accept(const OrderPtr& order)
      method on_reject (line 50) | virtual void on_reject(const OrderPtr& order, const char* )
      method on_fill (line 54) | virtual void on_fill(const OrderPtr& order,
      method on_cancel (line 61) | virtual void on_cancel(const OrderPtr& order)
      method on_cancel_reject (line 65) | virtual void on_cancel_reject(const OrderPtr& order, const char* )
      method on_replace (line 69) | virtual void on_replace(const OrderPtr& order,
      method on_replace_reject (line 75) | virtual void on_replace_reject(const OrderPtr& order, const char* )
      method reset (line 80) | void reset()
    function BOOST_AUTO_TEST_CASE (line 101) | BOOST_AUTO_TEST_CASE(TestOrderCallbacks)
    class OrderBookCbListener (line 151) | class OrderBookCbListener : public OrderBookListener<TypedOrderBook>
      method on_order_book_change (line 155) | virtual void on_order_book_change(const TypedOrderBook* book)
      method reset (line 160) | void reset()
    function BOOST_AUTO_TEST_CASE (line 169) | BOOST_AUTO_TEST_CASE(TestOrderBookCallbacks)
    class DepthCbListener (line 215) | class DepthCbListener
      method on_depth_change (line 219) | virtual void on_depth_change(const TypedDepthOrderBook* book,
      method reset (line 225) | void reset()
    function BOOST_AUTO_TEST_CASE (line 234) | BOOST_AUTO_TEST_CASE(TestDepthCallbacks)
    class BboCbListener (line 285) | class BboCbListener
      method on_bbo_change (line 289) | virtual void on_bbo_change(const TypedDepthOrderBook* book,
      method reset (line 295) | void reset()
    function BOOST_AUTO_TEST_CASE (line 304) | BOOST_AUTO_TEST_CASE(TestBboCallbacks)
    function BOOST_AUTO_TEST_CASE (line 376) | BOOST_AUTO_TEST_CASE(TestTradeCallbacks)

FILE: test/unit/ut_market_price.cpp
  type liquibook (line 13) | namespace liquibook {
    function BOOST_AUTO_TEST_CASE (line 42) | BOOST_AUTO_TEST_CASE(TestNoMktToMktWithoutPreviousTrade)
    function BOOST_AUTO_TEST_CASE (line 58) | BOOST_AUTO_TEST_CASE(TestTradeSetsMarketPrice)
    function BOOST_AUTO_TEST_CASE (line 91) | BOOST_AUTO_TEST_CASE(TestExplicitlySettingMarketPriceAllowsMarketToMar...

FILE: test/unit/ut_order_book.cpp
  type liquibook (line 13) | namespace liquibook {
    function BOOST_AUTO_TEST_CASE (line 25) | BOOST_AUTO_TEST_CASE(TestBidsMultimapSortCorrect)
    function BOOST_AUTO_TEST_CASE (line 59) | BOOST_AUTO_TEST_CASE(TestAsksMultimapSortCorrect)
    function BOOST_AUTO_TEST_CASE (line 95) | BOOST_AUTO_TEST_CASE(TestAddCompleteBid)
    function BOOST_AUTO_TEST_CASE (line 145) | BOOST_AUTO_TEST_CASE(TestAddCompleteAsk)
    function BOOST_AUTO_TEST_CASE (line 183) | BOOST_AUTO_TEST_CASE(TestAddMultiMatchBid)
    function BOOST_AUTO_TEST_CASE (line 229) | BOOST_AUTO_TEST_CASE(TestAddMultiMatchAsk)
    function BOOST_AUTO_TEST_CASE (line 281) | BOOST_AUTO_TEST_CASE(TestAddPartialMatchBid)
    function BOOST_AUTO_TEST_CASE (line 330) | BOOST_AUTO_TEST_CASE(TestAddPartialMatchAsk)
    function BOOST_AUTO_TEST_CASE (line 378) | BOOST_AUTO_TEST_CASE(TestAddMultiPartialMatchBid)
    function BOOST_AUTO_TEST_CASE (line 426) | BOOST_AUTO_TEST_CASE(TestAddMultiPartialMatchAsk)
    function BOOST_AUTO_TEST_CASE (line 476) | BOOST_AUTO_TEST_CASE(TestRepeatMatchBid)
    function BOOST_AUTO_TEST_CASE (line 545) | BOOST_AUTO_TEST_CASE(TestRepeatMatchAsk)
    function BOOST_AUTO_TEST_CASE (line 616) | BOOST_AUTO_TEST_CASE(TestAddMarketOrderBid)
    function BOOST_AUTO_TEST_CASE (line 656) | BOOST_AUTO_TEST_CASE(TestAddMarketOrderAsk)
    function BOOST_AUTO_TEST_CASE (line 698) | BOOST_AUTO_TEST_CASE(TestAddMarketOrderBidMultipleMatch)
    function BOOST_AUTO_TEST_CASE (line 739) | BOOST_AUTO_TEST_CASE(TestAddMarketOrderAskMultipleMatch)
    function BOOST_AUTO_TEST_CASE (line 780) | BOOST_AUTO_TEST_CASE(TestMatchMarketOrderBid)
    function BOOST_AUTO_TEST_CASE (line 818) | BOOST_AUTO_TEST_CASE(TestMatchMarketOrderAsk)
    function BOOST_AUTO_TEST_CASE (line 857) | BOOST_AUTO_TEST_CASE(TestMatchMultipleMarketOrderBid)
    function BOOST_AUTO_TEST_CASE (line 901) | BOOST_AUTO_TEST_CASE(TestMatchMultipleMarketOrderAsk)
    function BOOST_AUTO_TEST_CASE (line 943) | BOOST_AUTO_TEST_CASE(TestCancelBid)
    function BOOST_AUTO_TEST_CASE (line 985) | BOOST_AUTO_TEST_CASE(TestCancelAskAndMatch)
    function BOOST_AUTO_TEST_CASE (line 1044) | BOOST_AUTO_TEST_CASE(TestCancelBidFail)
    function BOOST_AUTO_TEST_CASE (line 1093) | BOOST_AUTO_TEST_CASE(TestCancelAskFail)
    function BOOST_AUTO_TEST_CASE (line 1147) | BOOST_AUTO_TEST_CASE(TestCancelBidRestore)
    function BOOST_AUTO_TEST_CASE (line 1271) | BOOST_AUTO_TEST_CASE(TestCancelAskRestore)
    function BOOST_AUTO_TEST_CASE (line 1393) | BOOST_AUTO_TEST_CASE(TestFillCompleteBidRestoreDepth)
    function BOOST_AUTO_TEST_CASE (line 1555) | BOOST_AUTO_TEST_CASE(TestFillCompleteAskRestoreDepth)
    function BOOST_AUTO_TEST_CASE (line 1736) | BOOST_AUTO_TEST_CASE(TestReplaceSizeIncrease)
    function BOOST_AUTO_TEST_CASE (line 1784) | BOOST_AUTO_TEST_CASE(TestReplaceSizeDecrease)
    function BOOST_AUTO_TEST_CASE (line 1830) | BOOST_AUTO_TEST_CASE(TestReplaceSizeDecreaseCancel)
    function BOOST_AUTO_TEST_CASE (line 1940) | BOOST_AUTO_TEST_CASE(TestReplaceSizeDecreaseTooMuch)
    function BOOST_AUTO_TEST_CASE (line 1992) | BOOST_AUTO_TEST_CASE(TestReplaceSizeIncreaseDecrease)
    function BOOST_AUTO_TEST_CASE (line 2028) | BOOST_AUTO_TEST_CASE(TestReplaceBidPriceChange)
    function BOOST_AUTO_TEST_CASE (line 2109) | BOOST_AUTO_TEST_CASE(TestReplaceAskPriceChange)
    function BOOST_AUTO_TEST_CASE (line 2187) | BOOST_AUTO_TEST_CASE(TestReplaceBidPriceChangeErase)
    function BOOST_AUTO_TEST_CASE (line 2272) | BOOST_AUTO_TEST_CASE(TestReplaceAskPriceChangeErase)
    function BOOST_AUTO_TEST_CASE (line 2363) | BOOST_AUTO_TEST_CASE(TestBidMultiLevelFillRestore)
    function BOOST_AUTO_TEST_CASE (line 2421) | BOOST_AUTO_TEST_CASE(TestAskMultiLevelFillRestore)
    function BOOST_AUTO_TEST_CASE (line 2479) | BOOST_AUTO_TEST_CASE(TestReplaceBidMatch)
    function BOOST_AUTO_TEST_CASE (line 2565) | BOOST_AUTO_TEST_CASE(TestReplaceAskMatch)

FILE: test/unit/ut_order_book_shared_ptr.cpp
  type liquibook (line 15) | namespace liquibook {
    class SharedPtrOrderBook (line 24) | class SharedPtrOrderBook : public OrderBook<SimpleOrderPtr>
      method perform_callback (line 26) | virtual void perform_callback(OrderBook<SimpleOrderPtr>::TypedCallba...
    function BOOST_AUTO_TEST_CASE (line 53) | BOOST_AUTO_TEST_CASE(TestSharedPointerBuild)
    function BOOST_AUTO_TEST_CASE (line 82) | BOOST_AUTO_TEST_CASE(TestSharedCancelBid)

FILE: test/unit/ut_stop_orders.cpp
  type liquibook (line 13) | namespace liquibook {
    function BOOST_AUTO_TEST_CASE (line 47) | BOOST_AUTO_TEST_CASE(TestStopOrdersOffMarketNoTrade)
    function BOOST_AUTO_TEST_CASE (line 69) | BOOST_AUTO_TEST_CASE(TestStopMarketOrdersOnMarketTradeImmediately)
    function BOOST_AUTO_TEST_CASE (line 87) | BOOST_AUTO_TEST_CASE(TestStopMarketOrdersTradeWhenStopPriceReached)
    function BOOST_AUTO_TEST_CASE (line 132) | BOOST_AUTO_TEST_CASE(TestStopOrdersCancelBeforeTrigger)

FILE: test/unit/ut_utils.h
  function namespace (line 14) | namespace liquibook {
  function verify_filled (line 117) | void verify_filled() {
Condensed preview — 88 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (647K chars).
[
  {
    "path": ".gitignore",
    "chars": 538,
    "preview": "# Compiled Object files\n*.slo\n*.lo\n*.o\n*.obj\n*.iobj\n\n# Compiled Dynamic libraries\n*.so\n*.dylib\n*.dll\n\n# Compiled Static "
  },
  {
    "path": "PERFORMANCE.md",
    "chars": 1552,
    "preview": "Performance Test Results, Inserts Per Second\n============================================\n(newest results on top)\n \n<tab"
  },
  {
    "path": "README.md",
    "chars": 11219,
    "preview": "# Liquibook\n\nOpen source order matching engine\n\nLiquibook provides the low-level components that make up an order matchi"
  },
  {
    "path": "README_ORDER_ENTRY.md",
    "chars": 12334,
    "preview": "# Manual Order Entry -- Liquibook example program\n\nThis program accepts requests typed from the console or read from a s"
  },
  {
    "path": "doc/settAug2013/liquibook_sett.html",
    "chars": 109019,
    "preview": "<!DOCTYPE HTML>\n<head>\n    <meta charset=\"utf-8\">\n\n    <meta name=\"description\" content=\"Building a Market Data Feed Wit"
  },
  {
    "path": "doc/settAug2013/settAug2013_files/paper.css",
    "chars": 99,
    "preview": ".listing, .figure {\n  font-weight: bold;\n  font-size: .9em;\n}\n.listing {\n  margin-bottom: -16px;\n}\n"
  },
  {
    "path": "doc/settAug2013/styles/SETT.css",
    "chars": 2249,
    "preview": "body {\n    color: rgb(0, 0, 0);\n    background-color: rgb(255, 255, 255);\n    font-family: Verdana, sans-serif;\n    marg"
  },
  {
    "path": "env.sh",
    "chars": 1869,
    "preview": "SOURCE=\"${BASH_SOURCE[0]}\"\nSOURCE_DIR=`dirname $SOURCE`\n\nif test \"$LIQUIBOOK_ROOT\" = \"\"; then\n    READLINK='readlink'\n  "
  },
  {
    "path": "examples/.gitignore",
    "chars": 1,
    "preview": "\n"
  },
  {
    "path": "examples/mt_order_entry/Market.cpp",
    "chars": 20239,
    "preview": "// Copyright (c) 2017 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing informati"
  },
  {
    "path": "examples/mt_order_entry/Market.h",
    "chars": 4923,
    "preview": "// Copyright (c) 2017 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing informati"
  },
  {
    "path": "examples/mt_order_entry/Order.cpp",
    "chars": 6184,
    "preview": "// Copyright (c) 2017 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing informati"
  },
  {
    "path": "examples/mt_order_entry/Order.h",
    "chars": 3674,
    "preview": "// Copyright (c) 2017 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing informati"
  },
  {
    "path": "examples/mt_order_entry/OrderFwd.h",
    "chars": 242,
    "preview": "// Copyright (c) 2017 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing informati"
  },
  {
    "path": "examples/mt_order_entry/TestOneAonBidTwoAsk.script",
    "chars": 282,
    "preview": "## Copyright (c) 2017 Object Computing, Inc.\n## All rights reserved.\n## See the file license.txt for licensing informati"
  },
  {
    "path": "examples/mt_order_entry/Util.cpp",
    "chars": 3054,
    "preview": "// Copyright (c) 2017 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing informati"
  },
  {
    "path": "examples/mt_order_entry/Util.h",
    "chars": 4286,
    "preview": "// Copyright (c) 2017 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing informati"
  },
  {
    "path": "examples/mt_order_entry/mt_order_entry.mpc",
    "chars": 232,
    "preview": "// Copyright (c) 2017 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing informati"
  },
  {
    "path": "examples/mt_order_entry/mt_order_entry_main.cpp",
    "chars": 5052,
    "preview": "// Copyright (c) 2017 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing informati"
  },
  {
    "path": "examples/mt_order_entry/order_entry.script",
    "chars": 345,
    "preview": "# Copyright (c) 2017 Object Computing, Inc.\n# All rights reserved.\n# See the file license.txt for licensing information."
  },
  {
    "path": "examples/mt_order_entry/teststoporders.script",
    "chars": 705,
    "preview": "# Script to test stop orders\n\n# set market price to 56\nbuy 100 +ibm mkt;\nsell 100 ibm 56;\n\n# enter seed orders that won'"
  },
  {
    "path": "license.txt",
    "chars": 2332,
    "preview": "Copyright (c) 2012, 2013 Object Computing, Inc. All rights reserved.\n\nLiquibook is a licensed product, is protected by c"
  },
  {
    "path": "liquibook.features",
    "chars": 343,
    "preview": "// Copyright (c) 2013-2017 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing info"
  },
  {
    "path": "liquibook.mwc",
    "chars": 275,
    "preview": "workspace(liquibook) {\n  cmdline += -include mpc \n  cmdline += -include $QUICKFAST_ROOT\n  cmdline += -feature_file liqui"
  },
  {
    "path": "mpc/liquibook.mpb",
    "chars": 379,
    "preview": "project {\n  includes += $(LIQUIBOOK_ROOT)/src\n  libpaths += $(LIQUIBOOK_ROOT)/lib\n\n  // Force use of Visual C++ DLL runt"
  },
  {
    "path": "mpc/liquibook_book.mpb",
    "chars": 41,
    "preview": "project : liquibook {\n    // obsolete\n}\n\n"
  },
  {
    "path": "mpc/liquibook_exe.mpb",
    "chars": 197,
    "preview": "project {\n  specific(prop:microsoft) {\n    Release::exeout = $(LIQUIBOOK_ROOT)/Output/Release\n    Debug::exeout = $(LIQU"
  },
  {
    "path": "mpc/liquibook_lib.mpb",
    "chars": 92,
    "preview": "project : liquibook {\n  libout = $(LIQUIBOOK_ROOT)/lib\n  macros += LIQUIBOOK_BUILD_DLL  \n}\n\n"
  },
  {
    "path": "mpc/liquibook_simple.mpb",
    "chars": 80,
    "preview": "project : liquibook {\n  libs += liquibook_simple\n  after += liquibook_simple\n}\n\n"
  },
  {
    "path": "mpc/liquibook_test.mpb",
    "chars": 110,
    "preview": "project : liquibook, liquibook_exe, liquibook_book, liquibook_simple{\n  exeout = $(LIQUIBOOK_ROOT)/bin/test\n}\n"
  },
  {
    "path": "mpc.bat",
    "chars": 621,
    "preview": "@REM Copyright (c) 2009, 2010 Object Computing, Inc.\n@REM All rights reserved.\n@REM See the file license.txt for licensi"
  },
  {
    "path": "noQuickFAST/QuickFASTApplication.mpb",
    "chars": 35,
    "preview": "project{\n  requires += QuickFAST\n}\n"
  },
  {
    "path": "pt_run.bat",
    "chars": 27,
    "preview": "bin\\test\\pt_order_book.exe\n"
  },
  {
    "path": "src/book/bbo_listener.h",
    "chars": 460,
    "preview": "// Copyright (c) 2012, 2013 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing inf"
  },
  {
    "path": "src/book/callback.h",
    "chars": 6621,
    "preview": "// Copyright (c) 2012, 2013 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing inf"
  },
  {
    "path": "src/book/comparable_price.h",
    "chars": 5148,
    "preview": "// Copyright (c) 2017 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing informati"
  },
  {
    "path": "src/book/depth.h",
    "chars": 17393,
    "preview": "// Copyright (c) 2012 - 2017 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing in"
  },
  {
    "path": "src/book/depth_constants.h",
    "chars": 389,
    "preview": "// Copyright (c) 2012 - 2017 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing in"
  },
  {
    "path": "src/book/depth_level.h",
    "chars": 4204,
    "preview": "// Copyright (c) 2012, 2013 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing inf"
  },
  {
    "path": "src/book/depth_listener.h",
    "chars": 543,
    "preview": "// Copyright (c) 2012, 2013 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing inf"
  },
  {
    "path": "src/book/depth_order_book.h",
    "chars": 5938,
    "preview": "// Copyright (c) 2012, 2013 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing inf"
  },
  {
    "path": "src/book/liquibook.mpc",
    "chars": 114,
    "preview": "project (liquibook): liquibook_exe {\n  exename = *\n  Header_files {\n    *.h\n  }\n  Source_Files {\n    *.cpp\n  }\n}\n\n"
  },
  {
    "path": "src/book/logger.h",
    "chars": 469,
    "preview": "// Copyright (c) 2012, 2013 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing inf"
  },
  {
    "path": "src/book/main.cpp",
    "chars": 829,
    "preview": "#include <iostream>\n#include \"version.h\"\n\n#include \"depth_order_book.h\"\n#include \"order.h\"\n\nusing namespace liquibook;\nu"
  },
  {
    "path": "src/book/order.h",
    "chars": 1612,
    "preview": "// Copyright (c) 2012, 2013 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing inf"
  },
  {
    "path": "src/book/order_book.h",
    "chars": 37007,
    "preview": "// Copyright (c) 2012 - 2017 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing in"
  },
  {
    "path": "src/book/order_book_listener.h",
    "chars": 448,
    "preview": "// Copyright (c) 2012, 2013 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing inf"
  },
  {
    "path": "src/book/order_listener.h",
    "chars": 1830,
    "preview": "// Copyright (c) 2012, 2013 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing inf"
  },
  {
    "path": "src/book/order_tracker.h",
    "chars": 3365,
    "preview": "// Copyright (c) 2012 - 2017 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing in"
  },
  {
    "path": "src/book/trade_listener.h",
    "chars": 675,
    "preview": "// Copyright (c) 2012, 2013 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing inf"
  },
  {
    "path": "src/book/types.h",
    "chars": 902,
    "preview": "// Copyright (c) 2012, 2013 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing inf"
  },
  {
    "path": "src/book/version.h",
    "chars": 324,
    "preview": "// Copyright (c) 2012 - 2017 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing in"
  },
  {
    "path": "src/liquibook_export.h",
    "chars": 1272,
    "preview": "// Copyright (c) 2013 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing informati"
  },
  {
    "path": "src/simple/liquibook_simple.mpc",
    "chars": 77,
    "preview": "project : liquibook_lib, liquibook_book {\n  sharedname =\n  staticname = *\n}\n\n"
  },
  {
    "path": "src/simple/simple_order.cpp",
    "chars": 2243,
    "preview": "// Copyright (c) 2012, 2013 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing inf"
  },
  {
    "path": "src/simple/simple_order.h",
    "chars": 2777,
    "preview": "// Copyright (c) 2012, 2013 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing inf"
  },
  {
    "path": "src/simple/simple_order_book.h",
    "chars": 1623,
    "preview": "// Copyright (c) 2012, 2013 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing inf"
  },
  {
    "path": "test/.gitignore",
    "chars": 131,
    "preview": "pt_order_book\nut_all_or_none\nut_bbo_order_book\nut_depth\nut_immediate_or_cancel\nut_listeners\nut_order_book\nut_order_book_"
  },
  {
    "path": "test/latency/clock_gettime.h",
    "chars": 2111,
    "preview": "#pragma once\n// clock_gettime is not defined on Windows / msvc\n// Let's fix that.\n#include <time.h>\n#ifdef _MSC_VER \n// "
  },
  {
    "path": "test/latency/liquibook_latency.mpc",
    "chars": 93,
    "preview": "project (lt_order_book) : liquibook_book, liquibook_simple, liquibook_test {\n  exename = *\n}\n"
  },
  {
    "path": "test/latency/lt_order_book.cpp",
    "chars": 4550,
    "preview": "// Copyright (c) 2012, 2013 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing inf"
  },
  {
    "path": "test/perf/liquibook_perf.mpc",
    "chars": 93,
    "preview": "project (pt_order_book) : liquibook_book, liquibook_simple, liquibook_test {\n  exename = *\n}\n"
  },
  {
    "path": "test/perf/pt_order_book.cpp",
    "chars": 3744,
    "preview": "// Copyright (c) 2012, 2013 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing inf"
  },
  {
    "path": "test/unit/changed_checker.h",
    "chars": 4334,
    "preview": "// Copyright (c) 2012, 2013 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing inf"
  },
  {
    "path": "test/unit/depth_check.h",
    "chars": 2283,
    "preview": "// Copyright (c) 2012=2017 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing info"
  },
  {
    "path": "test/unit/liquibook_unit.mpc",
    "chars": 169,
    "preview": "project (liquibook_unit_test) : liquibook_test, boost_unit_test_framework, boost_base{\n   exename = *\n   \n   specific(ma"
  },
  {
    "path": "test/unit/ut_all_or_none.cpp",
    "chars": 33555,
    "preview": "// Copyright (c) 2012 - 2017 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing in"
  },
  {
    "path": "test/unit/ut_bbo_order_book.cpp",
    "chars": 59980,
    "preview": "// Copyright (c) 2012 - 2017 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing in"
  },
  {
    "path": "test/unit/ut_depth.cpp",
    "chars": 33238,
    "preview": "// Copyright (c) 2012 - 2017 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing in"
  },
  {
    "path": "test/unit/ut_immediate_or_cancel.cpp",
    "chars": 27356,
    "preview": "// Copyright (c) 2012 - 2017 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing in"
  },
  {
    "path": "test/unit/ut_listeners.cpp",
    "chars": 12768,
    "preview": "// Copyright (c) 2012 - 2017 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing in"
  },
  {
    "path": "test/unit/ut_main.cpp",
    "chars": 201,
    "preview": "// Copyright (c) 2017, Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing informat"
  },
  {
    "path": "test/unit/ut_market_price.cpp",
    "chars": 3735,
    "preview": "// Copyright (c) 2012 - 2017 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing in"
  },
  {
    "path": "test/unit/ut_order_book.cpp",
    "chars": 90399,
    "preview": "// Copyright (c) 2012 - 2017 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing in"
  },
  {
    "path": "test/unit/ut_order_book_shared_ptr.cpp",
    "chars": 3041,
    "preview": "// Copyright (c) 2012 - 2017 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing in"
  },
  {
    "path": "test/unit/ut_stop_orders.cpp",
    "chars": 5190,
    "preview": "// Copyright (c) 2012 - 2017 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing in"
  },
  {
    "path": "test/unit/ut_utils.h",
    "chars": 5197,
    "preview": "// Copyright (c) 2012 -- 2017 Object Computing, Inc.\n// All rights reserved.\n// See the file license.txt for licensing i"
  },
  {
    "path": "ut_run.bat",
    "chars": 33,
    "preview": "bin\\test\\liquibook_unit_test.exe\n"
  },
  {
    "path": "web/css/liquibook.css",
    "chars": 29,
    "preview": ".highlight {\n  color: red;\n}\n"
  },
  {
    "path": "web/easy.html",
    "chars": 4285,
    "preview": "\n<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <title>Liquibook</title>\n    <meta name=\"viewp"
  },
  {
    "path": "web/fast.html",
    "chars": 4200,
    "preview": "\n<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <title>Liquibook</title>\n    <meta name=\"viewp"
  },
  {
    "path": "web/flexible.html",
    "chars": 4443,
    "preview": "\n<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <title>Liquibook</title>\n    <meta name=\"viewp"
  },
  {
    "path": "web/fluid.html",
    "chars": 5897,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <title>Liquibook</title>\n    <meta name=\"viewpo"
  },
  {
    "path": "web/get-started.html",
    "chars": 4082,
    "preview": "\n<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <title>Liquibook</title>\n    <meta name=\"viewp"
  },
  {
    "path": "web/index.html",
    "chars": 5070,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <title>Liquibook Matching Engine</title>\n    <m"
  },
  {
    "path": "web/sub-template.html",
    "chars": 4082,
    "preview": "\n<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <title>Liquibook</title>\n    <meta name=\"viewp"
  },
  {
    "path": "winenv.bat",
    "chars": 6174,
    "preview": "@REM Copyright (c) 2017 Object Computing, Inc.\n@REM All rights reserved.\n@REM See the file license.txt for licensing inf"
  },
  {
    "path": "winenv_clear.bat",
    "chars": 417,
    "preview": "@REM Copyright (c) 2009, 2010 Object Computing, Inc.\n@REM All rights reserved.\n@REM See the file license.txt for licensi"
  }
]

About this extraction

This page contains the full source code of the objectcomputing/liquibook GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 88 files (605.2 KB), approximately 172.8k tokens, and a symbol index with 252 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!