Skip to main content

No project description provided

Project description

Polars Order Book

Polars Order Book provides plugins for the Polars library that efficiently calculate summary information (price and quantity) for the top N levels of an order book.

Features

  • Top N Levels: Compute the price and quantity for the top N price levels of both bid and ask sides of the order book.
  • High Performance: Designed with performance in mind.
  • Multiple Input Formats: Supports various types of order book updates:
    • Price level updates: (side, price, new_quantity)
    • Order mutations: (side, price, quantity_change)
    • Order mutations with modifications: (side, price, quantity, prev_price, prev_quantity)

Usage

Here are examples of how to use the plugin:

Example 1: Price Level Updates

import polars as pl
from polars_order_book import top_n_levels_from_price_updates

df = pl.DataFrame(
    {
        "is_bid": [True, True, False, False, True, True],
        "price": [1, 2, 4, 5, 2, 2],
        "qty": [100, 200, 400, 500, 250, 0],
    }
)
expr = top_n_levels_from_price_updates(
    price=df["price"], qty=df["qty"], is_bid=df["is_bid"], n=2
)
result = df.with_columns(expr.alias("top_levels")).unnest("top_levels")
print(result)

# Output
shape: (6, 11)
┌────────┬───────┬─────┬─────────────┬─────────────┬───────────┬───────────┬─────────────┬─────────────┬───────────┬───────────┐
 is_bid  price  qty  bid_price_1  bid_price_2  bid_qty_1  bid_qty_2  ask_price_1  ask_price_2  ask_qty_1  ask_qty_2 
 ---     ---    ---  ---          ---          ---        ---        ---          ---          ---        ---       
 bool    i64    i64  i64          i64          i64        i64        i64          i64          i64        i64       
╞════════╪═══════╪═════╪═════════════╪═════════════╪═══════════╪═══════════╪═════════════╪═════════════╪═══════════╪═══════════╡
 true    1      100  1            null         100        null       null         null         null       null      
 true    2      200  2            1            200        100        null         null         null       null      
 false   4      400  2            1            200        100        4            null         400        null      
 false   5      500  2            1            200        100        4            5            400        500       
 true    2      250  2            1            250        100        4            5            400        500       
 true    2      0    1            null         100        null       4            5            400        500       
└────────┴───────┴─────┴─────────────┴─────────────┴───────────┴───────────┴─────────────┴─────────────┴───────────┴───────────┘

Example 2: Order Mutations

import polars as pl
from polars_order_book import top_n_levels_from_price_mutations

df = pl.DataFrame(
    {
        "is_bid": [True, True, False, False, True, True],
        "price": [1, 2, 4, 5, 2, 2],
        "qty": [100, 200, 400, 500, 50, -250],
    }
)
expr = top_n_levels_from_price_mutations(price="price", qty="qty", is_bid="is_bid", n=2)
result = df.with_columns(expr.alias("top_levels")).unnest("top_levels")
print(result)

# Output
shape: (6, 11)
┌────────┬───────┬──────┬─────────────┬─────────────┬───────────┬───────────┬─────────────┬─────────────┬───────────┬───────────┐
 is_bid  price  qty   bid_price_1  bid_price_2  bid_qty_1  bid_qty_2  ask_price_1  ask_price_2  ask_qty_1  ask_qty_2 
 ---     ---    ---   ---          ---          ---        ---        ---          ---          ---        ---       
 bool    i64    i64   i64          i64          i64        i64        i64          i64          i64        i64       
╞════════╪═══════╪══════╪═════════════╪═════════════╪═══════════╪═══════════╪═════════════╪═════════════╪═══════════╪═══════════╡
 true    1      100   1            null         100        null       null         null         null       null      
 true    2      200   2            1            200        100        null         null         null       null      
 false   4      400   2            1            200        100        4            null         400        null      
 false   5      500   2            1            200        100        4            5            400        500       
 true    2      50    2            1            250        100        4            5            400        500       
 true    2      -250  1            null         100        null       4            5            400        500       
└────────┴───────┴──────┴─────────────┴─────────────┴───────────┴───────────┴─────────────┴─────────────┴───────────┴───────────┘

Example 3: Order Mutations with Modifications

import polars as pl
from polars_order_book import top_n_levels_from_price_mutations_with_modify

df = pl.DataFrame(
    {
        "is_bid": [True, False, True, False, True, False],
        "price": [1, 6, 2, 5, 3, 4],
        "qty": [100, 600, 200, 500, 300, 400],
        "prev_price": [None, None, 1, 6, 2, 5],
        "prev_qty": [None, None, 100, 600, 200, 500],
    }
)
expr = top_n_levels_from_price_mutations_with_modify(
    "price", "qty", "is_bid", "prev_price", "prev_qty", n=2
)
result = df.with_columns(expr.alias("top_levels")).unnest("top_levels")
print(result)

# Output
shape: (6, 13)
┌────────┬───────┬─────┬────────────┬──────────┬─────────────┬─────────────┬───────────┬───────────┬─────────────┬─────────────┬───────────┬───────────┐
 is_bid  price  qty  prev_price  prev_qty  bid_price_1  bid_price_2  bid_qty_1  bid_qty_2  ask_price_1  ask_price_2  ask_qty_1  ask_qty_2 
 ---     ---    ---  ---         ---       ---          ---          ---        ---        ---          ---          ---        ---       
 bool    i64    i64  i64         i64       i64          i64          i64        i64        i64          i64          i64        i64       
╞════════╪═══════╪═════╪════════════╪══════════╪═════════════╪═════════════╪═══════════╪═══════════╪═════════════╪═════════════╪═══════════╪═══════════╡
 true    1      100  null        null      1            null         100        null       null         null         null       null      
 false   6      600  null        null      1            null         100        null       6            null         600        null      
 true    2      200  1           100       2            null         200        null       6            null         600        null      
 false   5      500  6           600       2            null         200        null       5            null         500        null      
 true    3      300  2           200       3            null         300        null       5            null         500        null      
 false   4      400  5           500       3            null         300        null       4            null         400        null      
└────────┴───────┴─────┴────────────┴──────────┴─────────────┴─────────────┴───────────┴───────────┴─────────────┴─────────────┴───────────┴───────────┘

Practical Considerations and Tips

Converting Exchange Messages to Mutations

In practice, you may need to modify the order book data you have to get it into one of the supported input formats. The following example demonstrates several common modifications:

  • Convert side column to an is_bid boolean
  • Convert float price column to integers for internal processing, and convert output prices back to float
  • Represent deletes and trades as negative quantity mutations
import polars as pl
from polars_order_book import top_n_levels_from_price_mutations

messages = pl.DataFrame(
    {
        "message_type": ["add", "add", "add", "add", "trade", "delete"],
        "side": ["bid", "bid", "ask", "ask", "bid", "bid"],
        "price": [0.01, 0.02, 0.04, 0.05, 0.02, 0.02],
        "qty": [100, 200, 400, 500, 50, 150],
    }
)
PRICE_FACTOR = 100
mutations = messages.lazy().select(
    is_bid=pl.col("side") == "bid",
    price=(pl.col("price") * PRICE_FACTOR).round().cast(pl.Int64),
    qty=pl.when(pl.col("message_type").is_in(["delete", "trade"]))
    .then(-pl.col("qty"))
    .otherwise(pl.col("qty")),
)
expr = top_n_levels_from_price_mutations(price="price", qty="qty", is_bid="is_bid", n=2)
top_levels = (
    mutations.with_columns(top_levels=expr)
    .select("top_levels")
    .unnest("top_levels")
    .with_columns(pl.selectors.matches(r"^(bid|ask)_price_\d+$") / PRICE_FACTOR)  # Cast prices back to floats
    .collect()
)
result = pl.concat([messages, top_levels], how="horizontal")
print(result)

# Output
shape: (6, 12)
┌──────────────┬──────┬───────┬─────┬─────────────┬─────────────┬───────────┬───────────┬─────────────┬─────────────┬───────────┬───────────┐
 message_type  side  price  qty  bid_price_1  bid_price_2  bid_qty_1  bid_qty_2  ask_price_1  ask_price_2  ask_qty_1  ask_qty_2 
 ---           ---   ---    ---  ---          ---          ---        ---        ---          ---          ---        ---       
 str           str   f64    i64  f64          f64          i64        i64        f64          f64          i64        i64       
╞══════════════╪══════╪═══════╪═════╪═════════════╪═════════════╪═══════════╪═══════════╪═════════════╪═════════════╪═══════════╪═══════════╡
 add           bid   0.01   100  0.01         null         100        null       null         null         null       null      
 add           bid   0.02   200  0.02         0.01         200        100        null         null         null       null      
 add           ask   0.04   400  0.02         0.01         200        100        0.04         null         400        null      
 add           ask   0.05   500  0.02         0.01         200        100        0.04         0.05         400        500       
 trade         bid   0.02   50   0.02         0.01         150        100        0.04         0.05         400        500       
 delete        bid   0.02   150  0.01         null         100        null       0.04         0.05         400        500       
└──────────────┴──────┴───────┴─────┴─────────────┴─────────────┴───────────┴───────────┴─────────────┴─────────────┴───────────┴───────────┘

Potential Pitfalls

  1. Unsorted Data: Messages must be processed in the correct order. Always sort your data by timestamp or sequence number before applying order book calculations.

  2. Multiple Products: For datasets containing multiple products, apply the top_n_level_* expression in a group-by context. For example:

    ...
    result = (
        mutations.group_by("product_id")
        .agg(
            top_levels=top_n_levels_from_price_mutations(
                price="price", qty="qty", is_bid="is_bid", n=2
            )
        )
        .unnest("top_levels")
    )
    
  3. Repeated Information: Ensure each mutation is applied only once. For instance, if you have both trade (one per passive order executed) and trade_summary (one per aggressive order) messages, discard the trade_summary to avoid double-counting.

  4. Order Book Resets: Your dataset may includes periods where the order book is cleared without explicit delete messages for all orders. To handle this:

    • Add a reset_count column to your data, incrementing it each time the book is reset.
    • Apply the top_n_level_* expression with a group-by on this column:
    ...
    result = (
        mutations.group_by(["product_id", "reset_count"])
        .agg(
            top_levels=top_n_levels_from_price_mutations(
                price="price", qty="qty", is_bid="is_bid", n=2
            )
        )
        .unnest("top_levels")
    )
    

Polar Order Bear

Project details


Download files

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

Source Distribution

polars_order_book-0.4.2.tar.gz (1.8 MB view details)

Uploaded Source

Built Distributions

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

polars_order_book-0.4.2-cp38-abi3-win_amd64.whl (4.3 MB view details)

Uploaded CPython 3.8+Windows x86-64

polars_order_book-0.4.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (4.9 MB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ x86-64

polars_order_book-0.4.2-cp38-abi3-manylinux_2_17_i686.manylinux2014_i686.whl (5.3 MB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ i686

polars_order_book-0.4.2-cp38-abi3-macosx_11_0_arm64.whl (4.2 MB view details)

Uploaded CPython 3.8+macOS 11.0+ ARM64

polars_order_book-0.4.2-cp38-abi3-macosx_10_12_x86_64.whl (4.5 MB view details)

Uploaded CPython 3.8+macOS 10.12+ x86-64

File details

Details for the file polars_order_book-0.4.2.tar.gz.

File metadata

  • Download URL: polars_order_book-0.4.2.tar.gz
  • Upload date:
  • Size: 1.8 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: maturin/1.8.3

File hashes

Hashes for polars_order_book-0.4.2.tar.gz
Algorithm Hash digest
SHA256 68b705176e87fbaccfd0e80d90bd1e137ff61763b73ed6935586a0717743546e
MD5 42f9a267bade1b68e7f5a75ad760dab5
BLAKE2b-256 3ad77672667b6540cf54c4f9bf125312b87227f2d7c1443b2167f948f3bebc2d

See more details on using hashes here.

File details

Details for the file polars_order_book-0.4.2-cp38-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for polars_order_book-0.4.2-cp38-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 1bf4127175f01755b65e5d60885337f99a78b32ff20441a88b2c458cd166c582
MD5 1681d5a5857dd6e6b76c954d3680a39f
BLAKE2b-256 e8cdc775bcd33b04106985d433e4848c29e21e13b111c03ef8cdc64a3a4afa6e

See more details on using hashes here.

File details

Details for the file polars_order_book-0.4.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for polars_order_book-0.4.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 bcc16349ab1ca63bb877ce494fcad7ef6b3682407a55fcfea61c90aeadba2a82
MD5 fcc1b47678eaddedef277a1d176ef0c2
BLAKE2b-256 4b67dfc6726749e20ae2563ddd1c7b7b6d73dcb68b88de0611ad06deef46bab2

See more details on using hashes here.

File details

Details for the file polars_order_book-0.4.2-cp38-abi3-manylinux_2_17_i686.manylinux2014_i686.whl.

File metadata

File hashes

Hashes for polars_order_book-0.4.2-cp38-abi3-manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm Hash digest
SHA256 4926cc7c19708b083d63745dc426a13d5e8da5272c0cfc3c0e82076d719bd5b6
MD5 2ba10f58bae0e55f9df0caef22ec8a0f
BLAKE2b-256 b6e4fd269b8d9b3ea4c60147255d5edcaa2438657503c481a0cbd14986470dc7

See more details on using hashes here.

File details

Details for the file polars_order_book-0.4.2-cp38-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for polars_order_book-0.4.2-cp38-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 d50ab3348498df35cfc134225f30f22276661d2647a7543905c89ae9f0ed0377
MD5 52a678dca62ee1f1d044cc09daf8e82f
BLAKE2b-256 2af5e53a35070d7ace726d9bd1c767516933256ba6145af65ba450f6a0fb743d

See more details on using hashes here.

File details

Details for the file polars_order_book-0.4.2-cp38-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for polars_order_book-0.4.2-cp38-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 cf3c97197900a5d1e0ecec244b3bac88b38f07c797ac55ad285919695ffb5ff7
MD5 495869c906ea804589100430568fb233
BLAKE2b-256 ff73105710fd784a6929c1d887b81c80f94769b5f9ab76f35f3165d23246440c

See more details on using hashes here.

Supported by

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