Skip to content

Core API

The lumina_lob.core package implements the matching engine and order book.

lumina_lob.core.order

Order data model.

Classes

Order dataclass

Single order in the book.

Source code in lumina_lob/core/order.py
@dataclass
class Order:
    """Single order in the book."""

    order_id: int
    side: Side
    price: float | None
    qty: int
    order_type: OrderType = OrderType.LIMIT
    filled_qty: int = field(default=0, init=False)
    next: Order | None = field(default=None, repr=False)
    prev: Order | None = field(default=None, repr=False)

    def __post_init__(self) -> None:
        if self.price is not None and self.price <= 0:
            raise ValueError("price must be positive")
        if self.qty <= 0:
            raise ValueError("qty must be positive")
        if self.order_type == OrderType.MARKET and self.price is not None:
            raise ValueError("market order cannot have price")
        if self.order_type == OrderType.LIMIT and self.price is None:
            raise ValueError("limit order must have price")

    @property
    def remaining_qty(self) -> int:
        return self.qty - self.filled_qty

    @property
    def is_filled(self) -> bool:
        return self.remaining_qty == 0

    def fill(self, amount: int) -> None:
        if amount <= 0:
            raise ValueError("fill amount must be positive")
        if amount > self.remaining_qty:
            raise ValueError("fill amount exceeds remaining qty")
        self.filled_qty += amount

    def reduce_qty(self, new_qty: int) -> int:
        """Reduce total desired qty. Return amount removed."""
        if new_qty < self.filled_qty:
            raise ValueError("new qty cannot be below filled qty")
        if new_qty > self.qty:
            raise ValueError("cannot increase qty via reduce")
        removed = self.qty - new_qty
        self.qty = new_qty
        return removed
Methods:
reduce_qty(new_qty)

Reduce total desired qty. Return amount removed.

Source code in lumina_lob/core/order.py
def reduce_qty(self, new_qty: int) -> int:
    """Reduce total desired qty. Return amount removed."""
    if new_qty < self.filled_qty:
        raise ValueError("new qty cannot be below filled qty")
    if new_qty > self.qty:
        raise ValueError("cannot increase qty via reduce")
    removed = self.qty - new_qty
    self.qty = new_qty
    return removed

lumina_lob.core.price_level

Price level: queue of orders at same price using doubly-linked list.

Classes

PriceLevel dataclass

Orders at one price level. FIFO queue via linked list.

Source code in lumina_lob/core/price_level.py
@dataclass
class PriceLevel:
    """Orders at one price level. FIFO queue via linked list."""

    price: float
    head: Order | None = field(default=None, init=False)
    tail: Order | None = field(default=None, init=False)
    total_qty: int = field(default=0, init=False)
    order_count: int = field(default=0, init=False)

    def append(self, order: Order) -> None:
        """Add order to tail."""
        if self.tail is None:
            self.head = self.tail = order
            order.prev = order.next = None
        else:
            order.prev = self.tail
            order.next = None
            self.tail.next = order
            self.tail = order
        self.total_qty += order.remaining_qty
        self.order_count += 1

    def remove(self, order: Order) -> bool:
        """Remove order from queue. Return True if found."""
        if order.prev is None and order.next is None and self.head is not order:
            return False
        if order.prev:
            order.prev.next = order.next
        else:
            self.head = order.next
        if order.next:
            order.next.prev = order.prev
        else:
            self.tail = order.prev
        self.total_qty -= order.remaining_qty
        self.order_count -= 1
        order.prev = order.next = None
        return True

    def fill(self, amount: int) -> int:
        """Fill from front of queue. Return filled amount."""
        remaining = amount
        while remaining > 0 and self.head:
            front = self.head
            can_fill = min(front.remaining_qty, remaining)
            front.fill(can_fill)
            self.total_qty -= can_fill
            remaining -= can_fill
            if front.is_filled:
                self.remove(front)
        return amount - remaining

    def reduce(self, order: Order, new_qty: int) -> int:
        """Reduce order qty at this level. Return removed amount."""
        removed = order.reduce_qty(new_qty)
        self.total_qty -= removed
        return removed

    def is_empty(self) -> bool:
        return self.head is None

    def __iter__(self) -> Iterator[Order]:
        node = self.head
        while node:
            yield node
            node = node.next

    def __len__(self) -> int:
        return self.order_count
Methods:
append(order)

Add order to tail.

Source code in lumina_lob/core/price_level.py
def append(self, order: Order) -> None:
    """Add order to tail."""
    if self.tail is None:
        self.head = self.tail = order
        order.prev = order.next = None
    else:
        order.prev = self.tail
        order.next = None
        self.tail.next = order
        self.tail = order
    self.total_qty += order.remaining_qty
    self.order_count += 1
fill(amount)

Fill from front of queue. Return filled amount.

Source code in lumina_lob/core/price_level.py
def fill(self, amount: int) -> int:
    """Fill from front of queue. Return filled amount."""
    remaining = amount
    while remaining > 0 and self.head:
        front = self.head
        can_fill = min(front.remaining_qty, remaining)
        front.fill(can_fill)
        self.total_qty -= can_fill
        remaining -= can_fill
        if front.is_filled:
            self.remove(front)
    return amount - remaining
reduce(order, new_qty)

Reduce order qty at this level. Return removed amount.

Source code in lumina_lob/core/price_level.py
def reduce(self, order: Order, new_qty: int) -> int:
    """Reduce order qty at this level. Return removed amount."""
    removed = order.reduce_qty(new_qty)
    self.total_qty -= removed
    return removed
remove(order)

Remove order from queue. Return True if found.

Source code in lumina_lob/core/price_level.py
def remove(self, order: Order) -> bool:
    """Remove order from queue. Return True if found."""
    if order.prev is None and order.next is None and self.head is not order:
        return False
    if order.prev:
        order.prev.next = order.next
    else:
        self.head = order.next
    if order.next:
        order.next.prev = order.prev
    else:
        self.tail = order.prev
    self.total_qty -= order.remaining_qty
    self.order_count -= 1
    order.prev = order.next = None
    return True

lumina_lob.core.book

Order book with bid/ask price levels.

Classes

OrderBook

Price-time priority order book.

Source code in lumina_lob/core/book.py
class OrderBook:
    """Price-time priority order book."""

    def __init__(self, event_log: EventLog | None = None) -> None:
        self.bids: dict[float, PriceLevel] = {}
        self.asks: dict[float, PriceLevel] = {}
        self.orders: dict[int, Order] = {}
        self.trades: list[tuple[int, int, int]] = []  # (buy_id, sell_id, qty)
        self.event_log = event_log if event_log is not None else EventLog()

    # ---------- helpers ----------
    @property
    def best_bid(self) -> float | None:
        return max(self.bids) if self.bids else None

    @property
    def best_ask(self) -> float | None:
        return min(self.asks) if self.asks else None

    @property
    def spread(self) -> float | None:
        bb, ba = self.best_bid, self.best_ask
        if bb is None or ba is None:
            return None
        return ba - bb

    @property
    def mid_price(self) -> float | None:
        bb, ba = self.best_bid, self.best_ask
        if bb is None or ba is None:
            return None
        return (bb + ba) / 2.0

    def _side_levels(self, side: Side) -> dict[float, PriceLevel]:
        return self.bids if side == Side.BID else self.asks

    # ---------- public ----------
    def add(self, order: Order) -> None:
        """Insert order into book. Market orders are not stored."""
        if order.order_id in self.orders:
            raise ValueError(f"duplicate order_id {order.order_id}")
        self.orders[order.order_id] = order
        price = order.price
        if price is None:
            raise ValueError("resting order must have a price")
        if order.side == Side.BID:
            level = self.bids.setdefault(price, PriceLevel(price))
        else:
            level = self.asks.setdefault(price, PriceLevel(price))
        level.append(order)
        self.event_log.log_add(
            order_id=order.order_id,
            side=order.side.name,
            price=order.price,
            qty=order.qty,
            best_bid=self.best_bid,
            best_ask=self.best_ask,
        )

    def cancel(self, order_id: int) -> bool:
        """Cancel resting order. Return True if cancelled."""
        order = self.orders.pop(order_id, None)
        if order is None:
            return False
        price = order.price
        if price is None:
            return False
        levels = self._side_levels(order.side)
        level = levels.get(price)
        if level is None:
            return False
        level.remove(order)
        if level.is_empty():
            del levels[price]
        self.event_log.log_cancel(
            order_id=order_id,
            best_bid=self.best_bid,
            best_ask=self.best_ask,
        )
        return True

    def modify(self, order_id: int, new_qty: int) -> bool:
        """Reduce resting order to new total qty. Remove if fully filled."""
        order = self.orders.get(order_id)
        if order is None:
            return False
        if new_qty <= 0:
            raise ValueError("new qty must be positive")
        price = order.price
        if price is None:
            return False
        levels = self._side_levels(order.side)
        level = levels.get(price)
        if level is None:
            return False
        level.reduce(order, new_qty)
        if order.is_filled:
            level.remove(order)
            self.orders.pop(order_id, None)
        if level.is_empty():
            del levels[price]
        self.event_log.log_modify(
            order_id=order_id,
            new_qty=new_qty,
            best_bid=self.best_bid,
            best_ask=self.best_ask,
        )
        return True

    def full_depth(self, side: Side) -> dict[float, int]:
        """Return all price levels and total qty for a side."""
        levels = self._side_levels(side)
        return {p: levels[p].total_qty for p in sorted(levels.keys(), reverse=(side == Side.BID))}

    def depth(self, side: Side, n: int = 5) -> dict[float, int]:
        """Return top N price levels and total qty."""
        levels = self._side_levels(side)
        prices = sorted(levels.keys(), reverse=(side == Side.BID))
        return {p: levels[p].total_qty for p in prices[:n]}

    def snapshot(self) -> dict[str, dict[float, int]]:
        return {"bids": self.depth(Side.BID), "asks": self.depth(Side.ASK)}

    def full_snapshot(self) -> dict[str, dict[float, int]]:
        return {"bids": self.full_depth(Side.BID), "asks": self.full_depth(Side.ASK)}

    def to_pandas(self) -> Any:
        """Return book depth as pandas DataFrame with columns [side, price, qty, order_count]."""
        import pandas as pd

        rows: list[dict[str, object]] = []
        for side in (Side.BID, Side.ASK):
            for price, level in self._side_levels(side).items():
                rows.append({
                    "side": side.name,
                    "price": price,
                    "qty": level.total_qty,
                    "order_count": len(level),
                })
        return pd.DataFrame(rows)

    def __len__(self) -> int:
        return len(self.orders)
Methods:
add(order)

Insert order into book. Market orders are not stored.

Source code in lumina_lob/core/book.py
def add(self, order: Order) -> None:
    """Insert order into book. Market orders are not stored."""
    if order.order_id in self.orders:
        raise ValueError(f"duplicate order_id {order.order_id}")
    self.orders[order.order_id] = order
    price = order.price
    if price is None:
        raise ValueError("resting order must have a price")
    if order.side == Side.BID:
        level = self.bids.setdefault(price, PriceLevel(price))
    else:
        level = self.asks.setdefault(price, PriceLevel(price))
    level.append(order)
    self.event_log.log_add(
        order_id=order.order_id,
        side=order.side.name,
        price=order.price,
        qty=order.qty,
        best_bid=self.best_bid,
        best_ask=self.best_ask,
    )
cancel(order_id)

Cancel resting order. Return True if cancelled.

Source code in lumina_lob/core/book.py
def cancel(self, order_id: int) -> bool:
    """Cancel resting order. Return True if cancelled."""
    order = self.orders.pop(order_id, None)
    if order is None:
        return False
    price = order.price
    if price is None:
        return False
    levels = self._side_levels(order.side)
    level = levels.get(price)
    if level is None:
        return False
    level.remove(order)
    if level.is_empty():
        del levels[price]
    self.event_log.log_cancel(
        order_id=order_id,
        best_bid=self.best_bid,
        best_ask=self.best_ask,
    )
    return True
depth(side, n=5)

Return top N price levels and total qty.

Source code in lumina_lob/core/book.py
def depth(self, side: Side, n: int = 5) -> dict[float, int]:
    """Return top N price levels and total qty."""
    levels = self._side_levels(side)
    prices = sorted(levels.keys(), reverse=(side == Side.BID))
    return {p: levels[p].total_qty for p in prices[:n]}
full_depth(side)

Return all price levels and total qty for a side.

Source code in lumina_lob/core/book.py
def full_depth(self, side: Side) -> dict[float, int]:
    """Return all price levels and total qty for a side."""
    levels = self._side_levels(side)
    return {p: levels[p].total_qty for p in sorted(levels.keys(), reverse=(side == Side.BID))}
modify(order_id, new_qty)

Reduce resting order to new total qty. Remove if fully filled.

Source code in lumina_lob/core/book.py
def modify(self, order_id: int, new_qty: int) -> bool:
    """Reduce resting order to new total qty. Remove if fully filled."""
    order = self.orders.get(order_id)
    if order is None:
        return False
    if new_qty <= 0:
        raise ValueError("new qty must be positive")
    price = order.price
    if price is None:
        return False
    levels = self._side_levels(order.side)
    level = levels.get(price)
    if level is None:
        return False
    level.reduce(order, new_qty)
    if order.is_filled:
        level.remove(order)
        self.orders.pop(order_id, None)
    if level.is_empty():
        del levels[price]
    self.event_log.log_modify(
        order_id=order_id,
        new_qty=new_qty,
        best_bid=self.best_bid,
        best_ask=self.best_ask,
    )
    return True
to_pandas()

Return book depth as pandas DataFrame with columns [side, price, qty, order_count].

Source code in lumina_lob/core/book.py
def to_pandas(self) -> Any:
    """Return book depth as pandas DataFrame with columns [side, price, qty, order_count]."""
    import pandas as pd

    rows: list[dict[str, object]] = []
    for side in (Side.BID, Side.ASK):
        for price, level in self._side_levels(side).items():
            rows.append({
                "side": side.name,
                "price": price,
                "qty": level.total_qty,
                "order_count": len(level),
            })
    return pd.DataFrame(rows)

lumina_lob.core.matching

Matching engine: price-time priority fills.

Classes

MatchingEngine

Match incoming orders against resting liquidity.

Source code in lumina_lob/core/matching.py
class MatchingEngine:
    """Match incoming orders against resting liquidity."""

    def __init__(self, book: OrderBook) -> None:
        self.book = book

    def process(self, order: Order) -> None:
        """Route order: match market/limit aggressive, rest passive remainder."""
        if order.order_type == OrderType.MARKET:
            self._match_market(order)
            return
        if order.order_type == OrderType.IOC:
            self._match_ioc(order)
            return
        if order.order_type == OrderType.FOK:
            self._match_fok(order)
            return
        # Limit order
        price = order.price
        if order.side == Side.BID:
            if price is not None and self.book.best_ask is not None and price >= self.book.best_ask:
                self._match_buy(order)
        else:
            if price is not None and self.book.best_bid is not None and price <= self.book.best_bid:
                self._match_sell(order)
        if not order.is_filled:
            self.book.add(order)

    def _match_ioc(self, order: Order) -> None:
        """Immediate or Cancel: match aggressively, cancel remainder, leave nothing in book."""
        opposite = Side.ASK if order.side == Side.BID else Side.BID
        levels = self.book._side_levels(opposite)
        prices = sorted(levels.keys(), reverse=(opposite == Side.BID))
        for p in prices:
            if order.is_filled:
                break
            # IOC can have a price limit; if set, enforce it
            if order.price is not None:
                if order.side == Side.BID and p > order.price:
                    break
                if order.side == Side.ASK and p < order.price:
                    break
            level = levels[p]
            self._fill_at_price(order, level)
            if level.is_empty():
                del levels[p]

    def _match_fok(self, order: Order) -> None:
        """Fill or Kill: all or nothing."""
        opposite = Side.ASK if order.side == Side.BID else Side.BID
        levels = self.book._side_levels(opposite)
        prices = sorted(levels.keys(), reverse=(opposite == Side.BID))
        available = 0
        for p in prices:
            if order.price is not None:
                if order.side == Side.BID and p > order.price:
                    break
                if order.side == Side.ASK and p < order.price:
                    break
            available += levels[p].total_qty
            if available >= order.qty:
                break
        if available >= order.qty:
            self._match_ioc(order)

    def _match_market(self, order: Order) -> None:
        opposite = Side.ASK if order.side == Side.BID else Side.BID
        levels = self.book._side_levels(opposite)
        prices = sorted(levels.keys(), reverse=(opposite == Side.BID))
        for p in prices:
            if order.is_filled:
                break
            level = levels[p]
            self._fill_at_price(order, level)
            if level.is_empty():
                del levels[p]

    def _match_buy(self, order: Order) -> None:
        price = order.price
        if price is None:
            return
        while not order.is_filled:
            best_ask = self.book.best_ask
            if best_ask is None or best_ask > price:
                break
            level = self.book.asks[best_ask]
            self._fill_at_price(order, level)
            if level.is_empty():
                del self.book.asks[best_ask]

    def _match_sell(self, order: Order) -> None:
        price = order.price
        if price is None:
            return
        while not order.is_filled:
            best_bid = self.book.best_bid
            if best_bid is None or best_bid < price:
                break
            level = self.book.bids[best_bid]
            self._fill_at_price(order, level)
            if level.is_empty():
                del self.book.bids[best_bid]

    def _fill_at_price(self, order: Order, level: PriceLevel) -> None:
        for resting in list(level):
            if order.is_filled:
                break
            amount = min(order.remaining_qty, resting.remaining_qty)
            order.fill(amount)
            resting.fill(amount)
            level.total_qty -= amount
            self.book.trades.append((order.order_id, resting.order_id, amount))
            self.book.event_log.log_fill(
                order_id=order.order_id,
                counterparty_id=resting.order_id,
                trade_qty=amount,
                price=level.price,
                side=order.side.name,
                filled_qty=order.filled_qty,
                best_bid=self.book.best_bid,
                best_ask=self.book.best_ask,
            )
            if resting.is_filled:
                level.remove(resting)
                self.book.orders.pop(resting.order_id, None)
Methods:
process(order)

Route order: match market/limit aggressive, rest passive remainder.

Source code in lumina_lob/core/matching.py
def process(self, order: Order) -> None:
    """Route order: match market/limit aggressive, rest passive remainder."""
    if order.order_type == OrderType.MARKET:
        self._match_market(order)
        return
    if order.order_type == OrderType.IOC:
        self._match_ioc(order)
        return
    if order.order_type == OrderType.FOK:
        self._match_fok(order)
        return
    # Limit order
    price = order.price
    if order.side == Side.BID:
        if price is not None and self.book.best_ask is not None and price >= self.book.best_ask:
            self._match_buy(order)
    else:
        if price is not None and self.book.best_bid is not None and price <= self.book.best_bid:
            self._match_sell(order)
    if not order.is_filled:
        self.book.add(order)

lumina_lob.core.event_log

Nanosecond-precision event journal for order book lifecycle.

Classes

Event dataclass

Single order book event.

Source code in lumina_lob/core/event_log.py
@dataclass
class Event:
    """Single order book event."""

    event_id: int
    timestamp_ns: int
    event_type: EventType
    order_id: int
    side: str | None = None
    price: float | None = None
    qty: int | None = None
    filled_qty: int | None = None
    counterparty_id: int | None = None
    trade_qty: int | None = None
    best_bid: float | None = None
    best_ask: float | None = None

EventLog

Append-only journal of all book events.

Source code in lumina_lob/core/event_log.py
class EventLog:
    """Append-only journal of all book events."""

    def __init__(self) -> None:
        self.events: list[Event] = []
        self._counter: int = 0

    def _next_id(self) -> int:
        self._counter += 1
        return self._counter

    def _now_ns(self) -> int:
        return perf_counter_ns()

    def log_add(self, order_id: int, side: str, price: float | None, qty: int, best_bid: float | None, best_ask: float | None) -> Event:
        ev = Event(
            event_id=self._next_id(),
            timestamp_ns=self._now_ns(),
            event_type=EventType.ADD,
            order_id=order_id,
            side=side,
            price=price,
            qty=qty,
            best_bid=best_bid,
            best_ask=best_ask,
        )
        self.events.append(ev)
        return ev

    def log_cancel(self, order_id: int, best_bid: float | None, best_ask: float | None) -> Event:
        ev = Event(
            event_id=self._next_id(),
            timestamp_ns=self._now_ns(),
            event_type=EventType.CANCEL,
            order_id=order_id,
            best_bid=best_bid,
            best_ask=best_ask,
        )
        self.events.append(ev)
        return ev

    def log_modify(self, order_id: int, new_qty: int, best_bid: float | None, best_ask: float | None) -> Event:
        ev = Event(
            event_id=self._next_id(),
            timestamp_ns=self._now_ns(),
            event_type=EventType.MODIFY,
            order_id=order_id,
            qty=new_qty,
            best_bid=best_bid,
            best_ask=best_ask,
        )
        self.events.append(ev)
        return ev

    def log_fill(self, order_id: int, counterparty_id: int, trade_qty: int, price: float, side: str, filled_qty: int, best_bid: float | None, best_ask: float | None) -> Event:
        ev = Event(
            event_id=self._next_id(),
            timestamp_ns=self._now_ns(),
            event_type=EventType.FILL,
            order_id=order_id,
            side=side,
            price=price,
            counterparty_id=counterparty_id,
            trade_qty=trade_qty,
            filled_qty=filled_qty,
            best_bid=best_bid,
            best_ask=best_ask,
        )
        self.events.append(ev)
        return ev

    def to_dicts(self) -> list[dict[str, object]]:
        return [
            {
                "event_id": e.event_id,
                "timestamp_ns": e.timestamp_ns,
                "event_type": e.event_type.name,
                "order_id": e.order_id,
                "side": e.side,
                "price": e.price,
                "qty": e.qty,
                "filled_qty": e.filled_qty,
                "counterparty_id": e.counterparty_id,
                "trade_qty": e.trade_qty,
                "best_bid": e.best_bid,
                "best_ask": e.best_ask,
            }
            for e in self.events
        ]

    def __len__(self) -> int:
        return len(self.events)