Skip to main content

Orderbook

License Python PyPi coverage-lines coverage-functions

A fast L2/L3 orderbook data structure, in C, for Python

Installation

Python 3.12+ supported. In general, uv is preferred and will be utilized throughout this document.

To add it to a project uv add order-book or, to install it into an environment directly, uv pip install order-book

Installing from a checkout of this repository: uv pip install . (note a C compiler is required).

Basic Usage

from decimal import Decimal

import requests
from order_book import OrderBook

ob = OrderBook()

# get some orderbook data
data = requests.get("https://api.exchange.coinbase.com/products/BTC-USD/book?level=2").json()

ob.bids = {Decimal(price): size for price, size, _ in data['bids']}
ob.asks = {Decimal(price): size for price, size, _ in data['asks']}

# OR

for side in data:
    # there is additional data we need to ignore
    if side in {'bids', 'asks'}:
        ob[side] = {Decimal(price): size for price, size, _ in data[side]}


# Data is accessible by .index(), which returns a tuple of (price, size) at that level in the book
price, size = ob.bids.index(0)
print(f"Best bid price: {price} size: {size}")

price, size = ob.asks.index(0)
print(f"Best ask price: {price} size: {size}")

print(f"The spread is {ob.asks.index(0)[0] - ob.bids.index(0)[0]}\n\n")

# Negative indexes work as well, so the worst bid/ask is index -1
print(f"Worst bid: {ob.bids.index(-1)}")

# Data is accessible via iteration
# Note: bids/asks are iterators

print("Top 10 bids")
for count, price in enumerate(ob.bids):
    if count == 10:
        break
    print(f"Price: {price} Size: {ob.bids[price]}")


print("\n\nTop 10 asks")
for count, price in enumerate(ob.asks):
    if count == 10:
        break
    print(f"Price: {price} Size: {ob.asks[price]}")


# Membership tests and len() work as expected
print(f"\nBest bid still in book: {ob.bids.index(0)[0] in ob.bids}")
print(f"Bid levels: {len(ob.bids)}, ask levels: {len(ob.asks)}, both sides: {len(ob)}")


# Data can be exported to a sorted dictionary
# In Python3.7+ dictionaries remain in insertion ordering. The
# dict returned by .to_dict() has had its keys inserted in sorted order
print("\n\nTop 3 asks, as a dictionary")
print(dict(list(ob.asks.to_dict().items())[:3]))


# Data can also be exported as an ordered list
# .to_list() returns a list of (price, size) tuples
print("\nTop 5 Asks")
print(ob.asks.to_list()[:5])
print("\nTop 5 Bids")
print(ob.bids.to_list()[:5])


# .keys() returns the sorted prices as a tuple
print("\nTop 5 ask prices")
print(ob.asks.keys()[:5])


# The entire book can be exported at once. The keys are 'bid' and 'ask' (singular)
book = ob.to_dict()
print(f"\nto_dict() keys: {list(book)}")

Both sides accept any of bid, bids, BID, BIDS (and the ask equivalents), as attributes or as keys:

from order_book import OrderBook

ob = OrderBook()

ob.bids[100] = "1.5"     # attribute access
ob['bids'][99] = "2.0"   # key access
ob['BID'][98] = "0.5"    # case does not matter

print(ob.bid.to_list())  # [(100, '1.5'), (99, '2.0'), (98, '0.5')]

# assigning a dict to a side replaces that side wholesale
ob.asks = {101: "1.0", 102: "3.0"}
print(ob.asks.to_list())  # [(101, '1.0'), (102, '3.0')]

# levels are removed with del
del ob.asks[101]
print(ob.asks.to_list())  # [(102, '3.0')]

Max Depth

max_depth limits how many levels are visible. len(), iteration, keys(), index(), to_dict() and to_list() all respect it.

from order_book import OrderBook

ob = OrderBook(max_depth=3)
ob.bids = {price: price for price in range(10)}

print(len(ob.bids))        # 3
print(ob.bids.to_list())   # [(9, 9), (8, 8), (7, 7)]
print(ob.max_depth)        # 3

By default the levels beyond max_depth are still retained internally, they are just hidden. Pass max_depth_strict=True to have them deleted as the book is updated, which caps memory use but means out-of-depth levels can no longer be accessed:

from order_book import OrderBook

ob = OrderBook(max_depth=3, max_depth_strict=True)
for price in range(10):
    ob.bids[price] = price

print(ob.bids.to_list())   # [(9, 9), (8, 8), (7, 7)]

try:
    del ob.bids[0]         # level 0 was dropped, not merely hidden
except KeyError:
    print("level 0 is gone")

Checksums

Several exchanges publish a CRC32 checksum of the top of book so clients can detect a desynchronized book. Construct the book with checksum_format set to the exchange, then compare ob.checksum() against the value the exchange sent.

Supported formats: KRAKEN, OKX (and its alias OKCOIN), and BITGET.

from decimal import Decimal

from order_book import OrderBook

ob = OrderBook(checksum_format='KRAKEN')

ob.bids = {Decimal(f"{100 - i}.{i:02d}"): Decimal(f"{i + 1}.5") for i in range(10)}
ob.asks = {Decimal(f"{101 + i}.{i:02d}"): Decimal(f"{i + 1}.5") for i in range(10)}

print(ob.checksum())

Type conversion

to_dict() on either an OrderBook or a SortedDict accepts from_type and to_type keyword arguments, which convert keys and values as the dictionary is built. from_type restricts the conversion to values of that type; omit it to convert everything.

from order_book import OrderBook

ob = OrderBook()
ob.bids = {'1.1': 2, '3.3': 4}
ob.asks = {'5.5': 6, '7.7': 8}

print(ob.to_dict(from_type=str, to_type=float))
# {'bid': {3.3: 4, 1.1: 2}, 'ask': {5.5: 6, 7.7: 8}}
# note the bid side is in descending order, as always

API Summary

OrderBook(max_depth=0, max_depth_strict=False, checksum_format=None)

Member Description
.bids / .bid / .asks / .ask the SortedDict for that side; assigning a dict replaces the side
ob[key] same sides, by key. bid, bids, ask, asks, any case
.max_depth the configured max depth (read only)
.to_dict(from_type=None, to_type=None) {'bid': {...}, 'ask': {...}}
.checksum() CRC32 checksum in the configured exchange's format
len(ob) total number of levels across both sides

SortedDict(data=None, ordering='ASC', max_depth=0, truncate=False)

Member Description
.keys() tuple of keys in sorted order
.index(n) (key, value) tuple at position n; negative indexes supported
.to_dict(from_type=None, to_type=None) dict with keys inserted in sorted order
.to_list() list of (key, value) tuples in sorted order
.truncate() drop everything past max_depth
sd[key], sd[key] = v, del sd[key], key in sd, len(sd), iteration as expected; iteration yields keys in sorted order

Main Features

  • Sides maintained in correct order
  • Can perform orderbook checksums
  • Supports max depth and depth truncation

Running code coverage

The script coverage.sh will compile the source using the -coverage CFLAG, run the unit tests, and build a coverage report in HTML. It manages its own environment via uv, so it can be run directly.

Note that it rebuilds .venv with an instrumented, unoptimized-for-timing build, so re-run uv pip install ".[tests]" afterwards to get back to a normal development environment.

Running the performance tests

You can run the performance tests like so: uv run perf/performance_test.py. The program will profile the time to run for random data samples of various sizes as well as the construction of a sorted orderbook using live L2 orderbook data from Coinbase.

The performance of constructing a sorted orderbook (using live data from Coinbase) using this C library, versus a pure Python sorted dictionary library:

Library Time, in seconds
C Library 0.01547479629517
Python Library 0.02890801429749

The performance of constructing sorted dictionaries using the same libraries, as well as the cost of building unsorted, python dictionaies for dictionaries of random floating point data:

Library Number of Keys Time, in seconds
C Library 100 0.00002408027649
Python Library 100 0.00004816055298
Python Dict 100 0.00002312660217
C Library 500 0.00014019012451
Python Library 500 0.00027227401733
Python Dict 500 0.00012207031250
C Library 1000 0.00029301643372
Python Library 1000 0.00055193901062
Python Dict 1000 0.00024676322937

This represents a roughly 2x speedup compared to a pure python implementation, and in many cases is close to the performance of an unsorted python dictionary.

For other performance metrics, run performance_test.py as well as the other performance tests in perf/

Changelog

0.7.0 (2026-08-05)

  • Update: Modernize project (uv, pyproject.toml, etc)
  • Update: Update readme, tests, examples, etc
  • Update: revamp wheel building

0.6.1 (2024-04-22)

  • Update: to_list's behavior matches that of to_dict (respects max_depth, if set).
  • Update: resolve build warnings on some compilers.

0.6.0 (2022-10-19)

  • Update: Drop support for python 3.7
  • Feature: to_list method
  • Bugfix: Initialize iterator correctly

0.5.0 (2022-08-23)

  • Bugfix: fix segmentation fault when calculating checksum on empty orderbook
  • Bugfix: fix missing reference decrement
  • Performance: Improvement to marking dirty keys

0.4.3 (2022-05-29)

  • Bugfix: handle scientific notation of small values in Kraken checksum
  • Update: calculate Kraken checksum on order books less than 10 levels deep
  • Bugfix: fix occasional incorrect checksums for OKX, FTX and Bitget

0.4.2 (2022-04-17)

  • Update: OKEx renamed OKX (for checksum validation)
  • Feature: Add support for orderbook checksums with Bitget

0.4.1 (2021-10-12)

  • Bugfix: unnecessary reference counting prevented sorted dictionaries from being deallocated
  • Bugfix: setting ordering on a sorted dict before checking that it was created successfully

0.4.0 (2021-09-16)

  • Feature: changes to code and setup.py to enable compiling on windows
  • Feature: add from_type/to_type kwargs to the to_dict methods, allowing for type conversion when creating the dictionary

0.3.2 (2021-09-04)

  • Bugfix: depth was incorrectly ignored when converting sorteddict to python dict

0.3.1 (2021-09-01)

  • Bugfix: truncate and max_depth not being passed from orderbook to sorteddict object correctly
  • Feature: let checksum_format kwarg be set to None

0.3.0 (2021-07-16)

  • Update classifiers to indicate this projects only supports MacOS/Linux
  • Bugfix: Using less than the minimum number of levels for a checksum with Kraken not raising error correctly
  • Update: add del examples to test code

0.2.1 (2021-03-29)

  • Bugfix: Invalid deallocation of python object

0.2.0 (2021-03-12)

  • Feature: Add branch prediction hints around error handling code
  • Bugfix: Fix regression from adding branch predictors
  • Bugfix: Fix error corner case when iterating twice on an empty dataset
  • Feature: Add contains function for membership test
  • Bugfix: Fix issues around storing L3 data
  • Feature: Enhance testing, add in L3 book test cases

0.1.1 (2021-02-12)

  • Feature: Checksum support for orderbooks
  • Feature: FTX checksum support
  • Feature: Kraken checksum support
  • Feature: OkEX/OKCoin checksum support
  • Perf: Use CRC32 table to improve performance of checksum code

0.1.0 (2021-01-18)

  • Minor: Use enums to make code more readable
  • Bugfix: Add manifest file to ensure headers and changes file are included in sdist builds
  • Feature: Add support for max depth and depth truncation

0.0.2 (2020-12-27)

  • Bugfix: Fix sorted dictionary arg parsing
  • Feature: Coverage report generation for C library
  • Bugfix: Fix reference counting in index method in SortedDict
  • Feature: New unit tests to improve SortedDict coverage
  • Feature: Modularize files
  • Feature: Add ability to set bids/asks to dictionaries via attributes or [ ]
  • Docs: Update README with simple usage example

0.0.1 (2020-12-26)

  • Initial Release

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

order_book-0.7.0.tar.gz (36.0 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

order_book-0.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (69.2 kB view details)

Uploaded CPython 3.14manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

order_book-0.7.0-cp314-cp314-macosx_26_0_arm64.whl (30.6 kB view details)

Uploaded CPython 3.14macOS 26.0+ ARM64

order_book-0.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (69.1 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

order_book-0.7.0-cp313-cp313-macosx_11_0_arm64.whl (30.5 kB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

order_book-0.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (69.1 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

order_book-0.7.0-cp312-cp312-macosx_11_0_arm64.whl (30.6 kB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

File details

Details for the file order_book-0.7.0.tar.gz.

File metadata

  • Download URL: order_book-0.7.0.tar.gz
  • Upload date:
  • Size: 36.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.6

File hashes

Hashes for order_book-0.7.0.tar.gz
Algorithm Hash digest
SHA256 9a1d594fb43b6712ebfb9569915634e817f8dc718c4617ff74743057ed5f9965
MD5 b9ad60f982efa27709f593d453edbb07
BLAKE2b-256 e5e22055d99db270b5bc1a0a7d001f02d1eaeb8432f9a9a33d2c4ba7d8674b3b

See more details on using hashes here.

File details

Details for the file order_book-0.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for order_book-0.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 b2981582c237f084e12fa4130b97688c879e28876cb779d40e2c2ca496bb2db2
MD5 416d62b320b89a4679e65610123d9aa3
BLAKE2b-256 9b14ed9203e9ad81de2fe010e0cec24a55a77ef51e85891f18967561dc137901

See more details on using hashes here.

File details

Details for the file order_book-0.7.0-cp314-cp314-macosx_26_0_arm64.whl.

File metadata

File hashes

Hashes for order_book-0.7.0-cp314-cp314-macosx_26_0_arm64.whl
Algorithm Hash digest
SHA256 d644d0c10442e5496e58133b16936bb2be45eb61328f8e3b2bb60ec456a7dce6
MD5 dba57049a7887de6156cd8b3272e0a64
BLAKE2b-256 da48d5d35412d39de22b3b621add9b39f77e64560c5ce8ed9c41fcb2b86468a1

See more details on using hashes here.

File details

Details for the file order_book-0.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for order_book-0.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 86bc3d563c10b55518dce3c82600431287ee66dd8d2d145d4862af4fe4cb46bd
MD5 92543dcf228746999155dde02aa444d6
BLAKE2b-256 007ff4cbd0226b84bac899d919dc09bfae59949a9176f67af48d2b0c69167331

See more details on using hashes here.

File details

Details for the file order_book-0.7.0-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for order_book-0.7.0-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 b10fd16813c9fb21202d39d95c157137b7d92e99d605171dadf1e42423239e52
MD5 2bbc717bd4f2066338486df93685ab80
BLAKE2b-256 963357c34b0c72198aa49c0f1d1a3dcb090ceac8b4d90a8c6e38b9359acf4731

See more details on using hashes here.

File details

Details for the file order_book-0.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for order_book-0.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 f98b4f4e38e2633b30b3a82e02fa23ef327875c0467d93f7c0bf9ff588b3efdd
MD5 9b5a104869d0cc43771b1b19533a0677
BLAKE2b-256 23e25dcbc8b3735f9a2ca75c2b358a12c724318f00ebb0b0f2e94bfcc4c01e78

See more details on using hashes here.

File details

Details for the file order_book-0.7.0-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for order_book-0.7.0-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 ae5b38c8205979c8ac01c8aa621031eeb833607ed2d8949f612ebb57dbd789cb
MD5 5fdcdad6f14f05c2b3258f08f16abcea
BLAKE2b-256 b94207581acb11bf2aab81909e1f4718da69ce1570d5fe7411208690bdd84627

See more details on using hashes here.

Release history Release notifications | RSS feed

1.0.2

8 files

1.0.1

7 files

1.0.0

6 files

This release

0.7.0 This release

7 files

0.6.1

5 files

0.6.0

7 files

0.5.0

7 files

0.4.3

7 files

0.4.2

7 files

0.4.1

7 files

0.4.0

7 files

0.3.2

7 files

0.3.1

7 files

0.3.0

7 files

0.2.1

10 files

0.2.0

9 files

0.1.1

10 files

0.1.0

10 files

0.0.2

10 files

0.0.1

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page