
Fast & minimalist limit-order-book implementation in Python, with almost no dependencies.
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()