Skip to content

[Bug Report] get_volume_at_price crashes on every populated level (calls a non-existent OrderTree.get_price) #22

Description

@OPTIONPOOL

Found while running OrderBook through an open-source matching-engine benchmark, the Matching Engine Performance Challenge — it cross-checks engines against the byte-identical consensus of other open source engines. The matching itself is correct, but auditing the book through the benchmark needs a way to read resting depth, and the engine's public depth accessor crashes the moment it's pointed at a real level — querying it took this one-line fix.

OrderBook.get_volume_at_price(side, price) raises AttributeError for any price that actually has volume. After the price_exists guard passes, both branches call get_price on the side's OrderTree (orderbook/orderbook.py:184 for bids, :189 for asks):

        if side == 'bid':
            volume = 0
            if self.bids.price_exists(price):
                volume = self.bids.get_price(price).volume   # <-- no such method
            return volume

Cause. OrderTree (orderbook/ordertree.py) has no get_price method. The accessor for a price level is get_price_list (ordertree.py:23), which returns the OrderList at that price; get_price simply doesn't exist on the class. So self.bids.get_price(price) blows up with AttributeError: 'OrderTree' object has no attribute 'get_price' before .volume is ever read.

This only fires on a populated level. get_volume_at_price reaches the bad call solely through the price_exists(price) guard — when no order rests at that price the guard is false, the function returns the pre-set volume = 0, and the typo is never exercised. That's why the function looks fine until you query a price that has resting size, which is exactly what a depth read does.

Repro. Rest a single bid, then ask for its volume:

from orderbook import OrderBook
ob = OrderBook()
ob.process_order({'type':'limit','side':'bid','quantity':5,'price':100,'trade_id':1}, False, False)
ob.get_volume_at_price('bid', 100)
# AttributeError: 'OrderTree' object has no attribute 'get_price'

(get_volume_at_price('bid', 100) against an empty book returns 0 and never crashes, which is what hides the bug.)

Fix. Call the method that exists at both sites (orderbook.py:184 and :189):

                volume = self.bids.get_price_list(price).volume   # was get_price(...)

get_price_list(price) returns the OrderList resting at that price (ordertree.py:23-24), and OrderList.volume (orderbook/orderlist.py:15) is the per-level share volume kept in sync on every append/remove — i.e. exactly the number get_volume_at_price is meant to return. With this change the call returns the resting depth (e.g. 8 after two bids of 5 and 3 at the same price) instead of raising; it only fixes which method name is invoked, not what the book holds.

Happy to share the failing workload.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions