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.1.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.1-cp38-abi3-win_amd64.whl (4.3 MB view details)

Uploaded CPython 3.8+Windows x86-64

polars_order_book-0.4.1-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.1-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.1-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.1-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.1.tar.gz.

File metadata

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

File hashes

Hashes for polars_order_book-0.4.1.tar.gz
Algorithm Hash digest
SHA256 1966d67c51807056f5bdd487b65f2e1fcefa05f3552ba7b53b7eb83cca0fa8e3
MD5 35f4f194b691c507dcf16d4385fdebc5
BLAKE2b-256 5e0e86186b4f46fca37b717c2539fadcc57a69bcf19049483e4671ddc0b25a6e

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for polars_order_book-0.4.1-cp38-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 8443f582a9790b15f0ad232be221862bee4c51677b2780e632c2bc9136984931
MD5 e25b834f4128109cd14535fec65ed835
BLAKE2b-256 649297a6e2e3f8410f6c850fe16955fa3c4d4a30b4afdf39dc8868995633c561

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for polars_order_book-0.4.1-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 bd4b315adb44bab9051dad948170b70e82751dcf0ca571f8e3063b31e52c9dfa
MD5 1f3977d038aedf1d94d327664963d361
BLAKE2b-256 4165f3b1905680f5c1cf54e5d385bd4c87e351dc21ac5234c5d1a582040ef115

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for polars_order_book-0.4.1-cp38-abi3-manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm Hash digest
SHA256 ec17fc1322343cb8e0dfe0aca0a7c4bd66ab8f20a25f1d7d0a6b644bf0ba2775
MD5 f0ac5062898d276145315832c8126d53
BLAKE2b-256 a9a0c774e8e18c0be7aea85b8550f408b8fa7aa81b2e5c3166f0118348ac0911

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for polars_order_book-0.4.1-cp38-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 ead6c625c3008556a22eb62b3bf4cdce6d9fbe4fb65d4e9fa06edb937dd70745
MD5 6d15aa3e4aa55b2033b9458f56806121
BLAKE2b-256 f2b1aa2014945cb4b92fe916ef2866347273417bcb4bee8b7ee282862a9df1d1

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for polars_order_book-0.4.1-cp38-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 8311b25b013b9d38f05764dee65ec1945b55a3577f81d2f5557ebcd139ab763a
MD5 2ac60b708d32a68c0ef8014193b93484
BLAKE2b-256 3df7757758e4bba1d75924bce4663cb334a71b15ae6e049ed1ebae265830da69

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