A multi-threaded exchange simulator with price-time priority matching, supporting live trading, LOBSTER historical data replay, and NASDAQ ITCH 5.0 historical data replay.
Terminal 1: Start exchange server
python exchange_server.py -vTerminal 2: Start order client
python order_client_with_fsm.pyOr use the client programmatically:
from order_client_with_fsm import OrderClientWithFSM
client = OrderClientWithFSM()
client.connect()
order = client.create_order('limit', 100, 5000000, 'B') # Buy 100 @ $500
client.submit_order_sync(order)
print(f'Order {order.exchange_order_id}: {order.state_name}')
client.disconnect()Terminal 1: Start server with LOBSTER data
python exchange_server.py \
--historical AMZN_2012-06-21_34200000_57600000_message_1.csv \
--orderbook AMZN_2012-06-21_34200000_57600000_orderbook_1.csv \
--wait-ready --throttle 10000Terminal 2: Start market data client to build order book and plot
python udp_book_builder.py --save-plot bid_ask.pdf --plot-after 5000Then press Enter in Terminal 1 to start replay.
Note: Use --wait-ready or --wait-subscribers N so clients can connect before replay starts.
In this mode, a client replays LOBSTER data as orders to a live exchange server. This simulates a liquidity provider feeding orders into the market.
Terminal 1: Start exchange server
python exchange_server.py -vTerminal 2 (optional): Observe the order book
python udp_book_builder.py --levels 5Terminal 3: Start LOBSTER client to replay data
python historical_LOBSTER_client.py AMZN_2012-06-21_34200000_57600000_message_1.csv \
--throttle 10000-- Will there be any trading activity in this case? Fire up the stp_client.py and find out.
historical_ITCH_client.py reads a NASDAQ TotalView-ITCH 5.0 HDF5 file (produced by create_ITCH_HDF5_store.py) and replays every order-book event for a single stock through the live exchange server. The exchange book mirrors the real ITCH book tick-by-tick, so a strategy running in parallel can interact with genuine NASDAQ order flow.
Terminal 1: Start exchange server with market data
python exchange_server.py --market-data-port 10002Terminal 2 (optional): Watch the order book
python udp_book_builder.py --levels 10Terminal 3: Replay AAPL for the full day
python historical_ITCH_client.py \
--hdf5 data/itch.h5 \
--ticker AAPLpython historical_ITCH_client.py \
--hdf5 data/itch.h5\
--ticker AAPL \
--start-time 09:30:00 \
--end-time 16:00:00 \
--print-every 25000Useful when running alongside a strategy that needs time to react:
python historical_ITCH_client.py \
--hdf5 data/itch.h5 \
--ticker AAPL \
--start-time 09:30:00 \
--end-time 16:00:00 \
--throttle 100Terminal 1:
python exchange_server.py --market-data-port 10002Terminal 2:
python udp_book_builder.py --levels 20 --update-every 500 \
--diagnostics aapl_report.json --plot-book-shape aapl_shape.pdfTerminal 3:
python historical_ITCH_client.py \
--hdf5 data/itch.h5 \
--ticker AAPL \
--start-time 09:30:00 \
--end-time 16:00:00 \
--print-every 50000After the replay finishes, Ctrl+C the book builder to write the report and plot.
Terminal 1:
python exchange_server.py --market-data-port 10002Terminal 2: Book builder for observation
python udp_book_builder.py --levels 10 --update-every 100Terminal 3: ITCH replay drives the order book
python historical_ITCH_client.py \
--hdf5 data/itch.h5 \
--ticker AAPL \
--start-time 09:30:00 \
--end-time 16:00:00 \
--throttle 500Terminal 4: AS market maker quotes against real NASDAQ flow
python avellaneda_stoikov.py --gamma 0.1 --order-size 100 -vpython historical_ITCH_client.py \
--hdf5 data/itch.h5 \
--ticker AAPL \
--start-time 09:30:00 \
--max-messages 5000 \
--verbose \
--verbose-errorspython historical_ITCH_client.py \
--hdf5 data/itch.h5 \
--locate 7421Generate synthetic order flow and collect order book diagnostics (spread statistics, average book shape by level, message type breakdown).
Terminal 1: Start exchange server
python exchange_server.pyTerminal 2: Start liquidity provider
python liquidity_provider.py --mid-price 100.00 --throttle 50000 --log-file orders.csvTerminal 3: Build order book with diagnostics and book shape plot
python udp_book_builder.py --levels 20 --update-every 10 \
--diagnostics report.json --plot-book-shape lob_shape.pdfLet it run for a while, then Ctrl+C all three. On exit the book builder writes:
report.json— diagnostics (spread statistics, average book shape by level, message type breakdown)lob_shape.pdf— horizontal bar chart of average bid/ask depth by level- A human-readable summary printed to stdout
The same workflow works with historical_LOBSTER_client.py in place of the liquidity provider:
Terminal 1: Start exchange server
python exchange_server.pyTerminal 2: Build order book with diagnostics
python udp_book_builder.py --levels 20 --update-every 10 \
--diagnostics report.json --plot-book-shape lob_shape.pdfTerminal 3: Replay LOBSTER data as live orders
python historical_LOBSTER_client.py AMZN_2012-06-21_34200000_57600000_message_1.csv \
--throttle 10000After replay completes, Ctrl+C the book builder to generate the report and plot.
Run an optimal market maker (Avellaneda & Stoikov, 2008) that quotes bid/ask prices adjusted for inventory risk against the liquidity provider's synthetic order flow.
Terminal 1: Start exchange server
python exchange_server.pyTerminal 2 (optional): Observe the order book
python udp_book_builder.py --levels 5 --update-every 10Terminal 3: Start liquidity provider
python liquidity_provider.py --mid-price 100.00 --throttle 50000Terminal 4: Start AS market maker
python avellaneda_stoikov.py --gamma 0.1 --order-size 100 -vThe LP derives its mid price from the live BBO via UDP market data — there is no artificial random walk. Price movement emerges from market order imbalance: autocorrelated runs of buys sweep the ask side (pushing mid up), then runs of sells sweep the bid side (pulling it back). Orders use exponential TTLs (short-lived at the inside for fast churn, long-lived deep in the book).
Three CLI knobs control volatility:
| Flag | Default | Effect |
|---|---|---|
--frac-d |
0.2 |
ARFIMA fractional differencing parameter (0 = iid, 0.2 = standard decay, < 0.5) |
--market-size-div |
5 |
Market order size divisor (lower = bigger orders = more levels consumed per trade) |
--market-weight |
0.40 |
Fraction of ticks that produce a market order (limit weights auto-adjust) |
For higher volatility:
python liquidity_provider.py --mid-price 100.00 --throttle 50000 \
--frac-d 0.35 --market-size-div 3The market maker continuously estimates volatility from BBO updates, computes a reservation price shifted by inventory risk, and derives an optimal spread. Quotes widen with larger inventory and higher volatility.
The central order book and matching engine. Accepts orders and broadcasts market data.
| Option | Description |
|---|---|
-v, --verbose |
Show order book after each order |
--historical FILE |
LOBSTER message file for replay mode |
--orderbook FILE |
LOBSTER orderbook file (seeds book, enables validation) |
--throttle MICROSECONDS |
Delay between messages |
--max-messages N |
Stop after N messages |
--no-validate |
Disable orderbook validation |
--wait-subscribers N |
Wait for N market data subscribers before starting |
Replays NASDAQ TotalView-ITCH 5.0 data from an HDF5 file as live orders to the exchange server. Each ITCH message type is translated to the appropriate exchange operation:
| ITCH type | Action |
|---|---|
/A, /F |
Submit limit order; track in order map |
/D |
Cancel resting order |
/X |
Partial cancel: cancel + resubmit with reduced size |
/E, printable /C |
Send market order (aggressor side) so the tape event is broadcast; reconcile resting order size |
non-printable /C |
Silently shrink resting order (no tape print) |
/U |
Cancel original order; submit replacement at new price/size (side preserved) |
/P |
Synthetic limit + market pair for hidden-order tape prints |
| Option | Description |
|---|---|
--hdf5 FILE |
Path to ITCH HDF5 file (required) |
--ticker SYMBOL |
Stock ticker, e.g. AAPL (mutually exclusive with --locate) |
--locate N |
NASDAQ stock locator integer (alternative to --ticker) |
--host HOST |
Exchange host (default: localhost) |
--port PORT |
Exchange TCP order port (default: 10000) |
--start-time HH:MM:SS |
Skip messages before this time |
--end-time HH:MM:SS |
Stop after this time |
--throttle MICROSECONDS |
Sleep between messages in µs (default: 0) |
--max-messages N |
Stop after N messages |
-v, --verbose |
Print each message as processed |
--verbose-errors |
Print full error details |
Replays LOBSTER data as live orders to the exchange server.
| Option | Description |
|---|---|
message_file |
LOBSTER message file (required) |
--host HOST |
Exchange host (default: localhost) |
--throttle MICROSECONDS |
Delay between messages |
--max-messages N |
Stop after N messages |
-v, --verbose |
Print each message |
Subscribes to market data, builds order book, plots bid/ask.
| Option | Description |
|---|---|
--levels N |
Book levels to display (default: 10) |
--save-plot FILE |
Save bid/ask plot to PDF |
--plot-after N |
Save plot after N messages |
--no-clear |
Don't clear screen between updates |
--diagnostics FILE |
Collect microstructure diagnostics; write JSON report to FILE on exit |
--plot-book-shape FILE |
Save average book shape plot to FILE (requires --diagnostics) |
Generates synthetic order flow following empirical microstructure stylized facts.
| Option | Description |
|---|---|
--mid-price PRICE |
Bootstrap mid price in dollars before BBO exists (default: 100.00) |
--md-port PORT |
UDP market data port (default: 10002) |
--throttle MICROSECONDS |
Microseconds between orders (default: 50000) |
--frac-d FLOAT |
ARFIMA fractional differencing parameter (default: 0.2) |
--market-size-div INT |
Market order size divisor (default: 5) |
--market-weight FLOAT |
Market order probability per tick (default: 0.40) |
--log-file FILE |
Save order log to CSV |
--user USER |
User identifier (default: lp) |
--seed INT |
Random seed for reproducible output |
-v, --verbose |
Print each order |
Avellaneda-Stoikov optimal market maker. Quotes bid/ask adjusted for inventory risk.
| Option | Description |
|---|---|
--host HOST |
Exchange host (default: localhost) |
--order-port PORT |
Order TCP port (default: 10000) |
--md-port PORT |
Market data UDP port (default: 10002) |
--gamma FLOAT |
Risk aversion parameter (default: 0.01) |
--k FLOAT |
Order arrival intensity (default: 20.0) |
--horizon FLOAT |
Time horizon in seconds (default: 300) |
--sigma-window N |
Mid-price observations for volatility (default: 100) |
--max-inventory N |
Maximum absolute position (default: 500) |
--order-size N |
Quote size per side (default: 100) |
--min-requote FLOAT |
Minimum seconds between re-quotes (default: 0.1) |
--warmup FLOAT |
Seconds to wait before quoting (default: 5.0) |
--k-auto |
Dynamically estimate k from observed trade rate |
-v, --verbose |
Print quote updates and fills |
-o, --output FILE |
Output plot filename (default: as_market_maker.pdf) |
--signal {ofi} |
Enable alpha signal (default: none) |
--ofi-top-params A B |
Power-law coefficients for top quintile (default: 17.42 0.468) |
--ofi-bot-params A B |
Power-law coefficients for bottom quintile (default: -35.64 0.457) |
--ofi-top-edge FLOAT |
Imbalance threshold for bullish regime (default: 0.6) |
--ofi-bot-edge FLOAT |
Imbalance threshold for bearish regime (default: -0.333) |
--ofi-max-delta FLOAT |
Max delta in ms for extrapolation cap (default: 1.0) |
Python client library with order state tracking.
from order_client_with_fsm import OrderClientWithFSM
client = OrderClientWithFSM()
client.connect()
client.start_async_receive() # Enable async fill notifications
# Create and submit orders
order = client.create_order('limit', 100, 5000000, 'B') # limit buy
client.submit_order_sync(order)
# Cancel
client.cancel_order_sync(order.exchange_order_id)
client.disconnect()Subscribes to the trade feed and prints executed trades.
python stp_client.py| Option | Description |
|---|---|
--host HOST |
Exchange host (default: localhost) |
limit,size,price,side,user[,ttl] # Limit order
market,size,0,side,user # Market order
cancel,order_id,user # Cancel order
modify,order_id,size,price,user # Modify order (cancel-replace)
- Price: price * 10000 (e.g., $500.00 = 5000000)
- Side: B=Buy, S=Sell
- TTL: Time-to-live in seconds (default: 3600)
The exchange rejects orders that fail any of the following checks:
| Rule | Threshold | Reason |
|---|---|---|
| Minimum price | price >= 20000 ($2.00) | Reject sub-penny/sub-dollar stocks |
| Maximum size | size <= 999999 | Reject unreasonably large orders |
| Fat-finger detection | size <= price | If size > price, the fields are likely swapped (LOBSTER prices are always much larger than share quantities) |
Validation applies to new limit orders and modify orders. Market orders are exempt (they carry price=0 by design). Rejected orders receive a REJECT message with a descriptive reason.
Download sample data from LOBSTER. Files needed:
*_message_*.csv— Order events (time, type, id, size, price, direction)*_orderbook_*.csv— Book snapshots (ask1, size1, bid1, size1, ...)
ITCH data is stored in HDF5 format with one table per message type (/A, /F, /D, /E, /C, /X, /U, /P), indexed by stock_locate. Use create_ITCH_HDF5_store.py (or the accompanying notebook) to convert a raw ITCH binary file to the HDF5 store. The file is passed to historical_ITCH_client.py via --hdf5.
Note: The AMZN_message and AMZN_orderbook files must be present or accessible (symlinked) from the unittests directory for some of the tests.
pytest tests/ # All tests
pytest tests/unittests/ # Unit tests only
pytest tests/integrationtests/ # Integration tests only