Logo
Fast & minimalist limit-order-book implementation in Python, with almost no dependencies.

GitHub  |  PyPI

This package is still being developed, bugs are expected.

This is the very first version of the project, the idea was to have a working, correct and clean single-threaded version before making it fast. The next step is to rewrite the core parts in a concurrent fashion using multiprocessing. This should be done during summer 2025 (checkout branch `fast`).

For now, I have decided to keep it written only in Python (no interfacing with C/C++), but that may change in the future.


Quickstart


To install the package you can either install it using pip:

pip install fastlob

Otherwise, you can build the project from source:

git clone git@github.com:mrochk/fastlob.git
cd fastlob
pip install -r requirements.txt
pip install .

Examples


Placing a limit GTD order and getting his status.
 1import time, logging
 2from fastlob import Orderbook, OrderParams, OrderSide, OrderType
 3
 4logging.basicConfig(level=logging.INFO) # set maximum logging level
 5
 6lob = Orderbook(name='ABCD', start=True) # create a lob an start it
 7
 8# create an order
 9params = OrderParams(
10    side=OrderSide.BID,
11    price=123.32,
12    quantity=3.4,
13    otype=OrderType.GTD,
14    expiry=time.time() + 120 # order will expire in two minutes
15)
16
17result = lob(params); assert result.success() # place order
18
19status, qty_left = lob.get_status(result.orderid()) # query status of order
20print(f'Current order status: {status.name}, quantity left: {qty_left}.\n')
21
22lob.render() # pretty-print the lob
23
24lob.stop() # stop background processes

Simulate the LOB using various distributions.
 1import random, time, os
 2from scipy import stats
 3
 4from fastlob import Orderbook, OrderParams, OrderSide
 5
 6def generate_orders(T: int, midprice: float):
 7   result = list()
 8
 9   for _ in range(T):
10
11      n_ask_limits = stats.poisson.rvs(500)
12      n_bid_limits = stats.poisson.rvs(500)
13
14      ask_limits_price = stats.expon.rvs(loc=midprice, scale=1, size=n_ask_limits)
15      bid_limits_price = -stats.expon.rvs(loc=midprice, scale=1, size=n_bid_limits) + 2*midprice
16
17      ask_limits_quantities = stats.uniform.rvs(loc=1, scale=100, size=n_ask_limits)
18      bid_limits_quantities = stats.uniform.rvs(loc=1, scale=100, size=n_bid_limits)
19
20      ask_limits_params = [OrderParams(OrderSide.ASK, p, q) for (p, q) in zip(ask_limits_price, ask_limits_quantities)]
21      bid_limits_params = [OrderParams(OrderSide.BID, p, q) for (p, q) in zip(bid_limits_price, bid_limits_quantities)]
22
23      n_markets = stats.poisson.rvs(100)
24
25      markets_price = stats.norm.rvs(loc=midprice, scale=2, size=n_markets)
26      markets_quantities = stats.uniform.rvs(loc=1, scale=100, size=n_markets)
27      markets_bid_or_ask = [random.choice((OrderSide.BID, OrderSide.ASK)) for _ in range(n_markets)]
28
29      markets_params = [OrderParams(s, p, q) for (s, p, q) in zip(markets_bid_or_ask, markets_price, markets_quantities)]
30
31      orders = ask_limits_params + bid_limits_params + markets_params
32      random.shuffle(orders)
33
34      result.append(orders)
35
36   return result
37
38def simulate(orders: list, speed: float):
39   ob = Orderbook('Simulation')
40   ob.start()
41
42   for o in orders:
43      ob.process_many(o)
44      print()
45      ob.render()
46      time.sleep(speed)
47      os.system('clear')
48
49   ob.stop()
50
51orders = generate_orders(10, 100)
52simulate(orders, 0.5)

Use historical data.
 1from fastlob import Orderbook, OrderParams, OrderSide
 2
 3snapshot = {
 4   'bids': [
 5      (98.78, 11.56),
 6      (95.65, 67.78),
 7      (94.23, 56.76),
 8      (93.23, 101.59),
 9      (90.03, 200.68),
10   ],
11   'asks': [
12      (99.11, 12.87),
13      (100.89, 45.87),
14      (101.87, 88.56),
15      (103.78, 98.77),
16      (105.02, 152.43),
17   ]
18}
19
20updates = [
21   # update 1
22   {
23   'bids': [
24      (99.07, 10.01),
25      (95.65, 79.78),
26      (93.23, 89.59),
27      (90.03, 250.68),
28   ],
29   'asks': [
30      (99.11, 5.81),
31   ]},
32
33   # update 2
34   {
35   'bids': [
36      (99.07, 0.00),
37      (98.78, 3.56),
38      (79.90, 100.56),
39   ],
40   'asks': [
41      (103.78, 90.77),
42      (105.02, 123.43),
43   ]},
44
45   # update 3
46   {
47   'bids': [
48      (98.78, 11.56),
49      (95.65, 67.78),
50      (94.23, 56.76),
51      (93.23, 0.00),
52      (90.03, 0.00),
53   ],
54   'asks': [
55      (99.11, 0.00),
56      (100.89, 0.00),
57      (101.87, 0.00),
58      (103.78, 1.23),
59      (105.02, 152.43),
60   ]}]
61
62 ob = Orderbook.from_snapshot(snapshot, start=True)
63 ob.load_updates(updates)
64
65 ob.render()
66
67 ob.step()
68 ob.render()
69
70 ob(OrderParams(OrderSide.BID, 99.07, 1.98))
71
72 ob.step()
73 ob.render()
74
75 ob.step()
76 ob.render()
77
78 ob.stop()

API Reference