Skip to content

bookside module

BookSide

Class representing a side (Buy or Sell) of the order book.

Attributes:

Name Type Description
side MarketSide

The side of the market (Buy or Sell).

_allow_market_queue bool

Flag indicating whether market orders are allowed to be queued.

_sign int

Sign used for heap comparisons based on market side.

market_orders deque

Queue to store market orders.

limit_orders defaultdict

Dictionary to store limit orders as dequeues.

_bestHeap list

Heap structure to efficiently find the best price.

bestP float

Best price on the order book.

bestV int

Best volume at the best price.

volumes Counter

Counter to track volumes at different prices.

Methods

fill: Match and fill two orders. match: Match and fill an order with the best available order. add: Add an order to the order book. liquid: Check if the order book is liquid. has_market: Check if there are market orders in the queue.

Source code in securities_exchange/bookside.py
class BookSide:

    """
    Class representing a side (Buy or Sell) of the order book.

    Attributes:
        side (MarketSide): The side of the market (Buy or Sell).
        _allow_market_queue (bool): Flag indicating whether market orders are allowed to be queued.
        _sign (int): Sign used for heap comparisons based on market side.
        market_orders (deque): Queue to store market orders.
        limit_orders (defaultdict): Dictionary to store limit orders as dequeues.
        _bestHeap (list): Heap structure to efficiently find the best price.
        bestP (float): Best price on the order book.
        bestV (int): Best volume at the best price.
        volumes (Counter): Counter to track volumes at different prices.

    Methods:
        fill: Match and fill two orders.
        match: Match and fill an order with the best available order.
        add: Add an order to the order book.
        liquid: Check if the order book is liquid.
        has_market: Check if there are market orders in the queue.
    """

    def __init__(self, side: MarketSide = MarketSide.BUY, allow_market_queue: bool = False):

        """
        Initialize a BookSide instance with the given parameters.

        Args:
            side (MarketSide): The side of the market (Buy or Sell).
            allow_market_queue (bool): Flag indicating whether market orders are allowed to be queued.
        """

        self.side = side
        self._allow_market_queue = allow_market_queue
        self._sign = -1 if self.side == MarketSide.BUY else 1
        self.market_orders = deque()
        self.limit_orders = defaultdict(deque)
        self._bestHeap = []
        heapify(self._bestHeap)
        self.bestP = None
        self.bestV = 0  
        self.volumes = Counter()


    def fill(self, orderA: Order, orderB: Order):

        """
        FIll two orders with each other.

        Args:
            orderA (Order): First order to match.
            orderB (Order): Second order to match.
        """

        if orderA.type == OrderType.MARKET:
            at_price = orderB.price
        elif orderB.type == OrderType.MARKET:
            at_price = orderA.price
        else:
            # Determine price based on market side and order sides
            if self.side == MarketSide.BUY:
                if orderA.side == MarketSide.BUY:
                    at_price = orderA.price
                else:
                    at_price = orderB.price
            else:
                if orderA.side == MarketSide.SELL:
                    at_price = orderA.price
                else:
                    at_price = orderB.price

        resA = orderA.residual_size
        resB = orderB.residual_size
        orderA.update(resB, at_price, orderB.id)
        orderB.update(resA, at_price, orderA.id)


    def match(self, order: Order, orders: OrderedDict[str, Order]):

        """
        Match an order with the best available order and update book.

        Args:
            order (Order): The order to match.
            orders (OrderedDict): Dictionary containing all existing orders.
        """

        if (order.type == OrderType.LIMIT and self.has_market()) and self._allow_market_queue:

            # If order is LIMIT and there are market orders in the queue, match with the first market order in the queue
            queued_mo = orders[self.market_orders[0]]
            self.fill(order, queued_mo)
            if queued_mo.status == OrderStatus.FILLED:
                self.market_orders.popleft()

        else:

            # Match with the best limit order
            queued_lo = orders[self.limit_orders[self.bestP][0]]
            self.fill(order, queued_lo)

            self.volumes[self.bestP] -= queued_lo.matches[-1][0]
            self.bestV -= queued_lo.matches[-1][0]
            if queued_lo.status == OrderStatus.FILLED:
                # If the matched limit order is fully filled remove it from the queue and update order book
                self.limit_orders[self.bestP].popleft()
                if len(self.limit_orders[self.bestP]) == 0:
                    del self.limit_orders[self.bestP]
                    del self.volumes[self.bestP]
                    if len(self._bestHeap):
                        self.bestP = self._sign * heappop(self._bestHeap)
                        self.bestV = self.volumes[self.bestP]
                    else:
                        self.bestP = None
                        self.bestV = 0                


    def add(self, order: Order):

        """
        Add an order to the side of the order book.

        Args:
            order (Order): The order to be added.
        """

        if (order.type == OrderType.MARKET) and self._allow_market_queue:
            # If the order is MARKET and market queue is allowed, add to market queue
            self.market_orders.append(order.id)

        elif order.type == OrderType.LIMIT:
            # If the order is LIMIT, add to limit orders and update order book
            if self.bestP is None:
                self.bestP = order.price
                self.bestV = order.residual_size                
            elif (self.bestP < order.price and self.side == MarketSide.BUY) or \
                 (self.bestP > order.price and self.side == MarketSide.SELL):
                heappush(self._bestHeap, self._sign * self.bestP)
                self.bestP = order.price
                self.bestV = order.residual_size
            elif self.bestP == order.price:
                self.bestV += order.residual_size
            elif order.price not in self.limit_orders:
                heappush(self._bestHeap, self._sign * order.price)

            self.limit_orders[order.price].append(order.id)
            self.volumes[order.price] += order.residual_size


    def liquid(self) -> bool:
        """
        Check if the side of the order book has liquidity.

        Returns:
            bool: True if the side of the order book has liquidity, False otherwise.
        """
        return self.bestV > 0


    def is_be(self, price) -> bool:
        """
        Compare price with the current bestP, Better or Equal.

        Args:
            price (float): price to compare to bestP

        Returns:
            bool: True if the price is better or equal to bestP, False otherwise.
        """
        if self.side == MarketSide.BUY:
            return self.bestP >= price 
        else:
            return self.bestP <= price 


    def has_market(self) -> bool:
        """
        Check if there are market orders in the queue.

        Returns:
            bool: True if there are market orders, False otherwise.
        """
        return len(self.market_orders) and self._allow_market_queue

__init__(self, side=<MarketSide.BUY: 1>, allow_market_queue=False) special

Initialize a BookSide instance with the given parameters.

Parameters:

Name Type Description Default
side MarketSide

The side of the market (Buy or Sell).

<MarketSide.BUY: 1>
allow_market_queue bool

Flag indicating whether market orders are allowed to be queued.

False
Source code in securities_exchange/bookside.py
def __init__(self, side: MarketSide = MarketSide.BUY, allow_market_queue: bool = False):

    """
    Initialize a BookSide instance with the given parameters.

    Args:
        side (MarketSide): The side of the market (Buy or Sell).
        allow_market_queue (bool): Flag indicating whether market orders are allowed to be queued.
    """

    self.side = side
    self._allow_market_queue = allow_market_queue
    self._sign = -1 if self.side == MarketSide.BUY else 1
    self.market_orders = deque()
    self.limit_orders = defaultdict(deque)
    self._bestHeap = []
    heapify(self._bestHeap)
    self.bestP = None
    self.bestV = 0  
    self.volumes = Counter()

add(self, order)

Add an order to the side of the order book.

Parameters:

Name Type Description Default
order Order

The order to be added.

required
Source code in securities_exchange/bookside.py
def add(self, order: Order):

    """
    Add an order to the side of the order book.

    Args:
        order (Order): The order to be added.
    """

    if (order.type == OrderType.MARKET) and self._allow_market_queue:
        # If the order is MARKET and market queue is allowed, add to market queue
        self.market_orders.append(order.id)

    elif order.type == OrderType.LIMIT:
        # If the order is LIMIT, add to limit orders and update order book
        if self.bestP is None:
            self.bestP = order.price
            self.bestV = order.residual_size                
        elif (self.bestP < order.price and self.side == MarketSide.BUY) or \
             (self.bestP > order.price and self.side == MarketSide.SELL):
            heappush(self._bestHeap, self._sign * self.bestP)
            self.bestP = order.price
            self.bestV = order.residual_size
        elif self.bestP == order.price:
            self.bestV += order.residual_size
        elif order.price not in self.limit_orders:
            heappush(self._bestHeap, self._sign * order.price)

        self.limit_orders[order.price].append(order.id)
        self.volumes[order.price] += order.residual_size

fill(self, orderA, orderB)

FIll two orders with each other.

Parameters:

Name Type Description Default
orderA Order

First order to match.

required
orderB Order

Second order to match.

required
Source code in securities_exchange/bookside.py
def fill(self, orderA: Order, orderB: Order):

    """
    FIll two orders with each other.

    Args:
        orderA (Order): First order to match.
        orderB (Order): Second order to match.
    """

    if orderA.type == OrderType.MARKET:
        at_price = orderB.price
    elif orderB.type == OrderType.MARKET:
        at_price = orderA.price
    else:
        # Determine price based on market side and order sides
        if self.side == MarketSide.BUY:
            if orderA.side == MarketSide.BUY:
                at_price = orderA.price
            else:
                at_price = orderB.price
        else:
            if orderA.side == MarketSide.SELL:
                at_price = orderA.price
            else:
                at_price = orderB.price

    resA = orderA.residual_size
    resB = orderB.residual_size
    orderA.update(resB, at_price, orderB.id)
    orderB.update(resA, at_price, orderA.id)

has_market(self)

Check if there are market orders in the queue.

Returns:

Type Description
bool

True if there are market orders, False otherwise.

Source code in securities_exchange/bookside.py
def has_market(self) -> bool:
    """
    Check if there are market orders in the queue.

    Returns:
        bool: True if there are market orders, False otherwise.
    """
    return len(self.market_orders) and self._allow_market_queue

is_be(self, price)

Compare price with the current bestP, Better or Equal.

Parameters:

Name Type Description Default
price float

price to compare to bestP

required

Returns:

Type Description
bool

True if the price is better or equal to bestP, False otherwise.

Source code in securities_exchange/bookside.py
def is_be(self, price) -> bool:
    """
    Compare price with the current bestP, Better or Equal.

    Args:
        price (float): price to compare to bestP

    Returns:
        bool: True if the price is better or equal to bestP, False otherwise.
    """
    if self.side == MarketSide.BUY:
        return self.bestP >= price 
    else:
        return self.bestP <= price 

liquid(self)

Check if the side of the order book has liquidity.

Returns:

Type Description
bool

True if the side of the order book has liquidity, False otherwise.

Source code in securities_exchange/bookside.py
def liquid(self) -> bool:
    """
    Check if the side of the order book has liquidity.

    Returns:
        bool: True if the side of the order book has liquidity, False otherwise.
    """
    return self.bestV > 0

match(self, order, orders)

Match an order with the best available order and update book.

Parameters:

Name Type Description Default
order Order

The order to match.

required
orders OrderedDict

Dictionary containing all existing orders.

required
Source code in securities_exchange/bookside.py
def match(self, order: Order, orders: OrderedDict[str, Order]):

    """
    Match an order with the best available order and update book.

    Args:
        order (Order): The order to match.
        orders (OrderedDict): Dictionary containing all existing orders.
    """

    if (order.type == OrderType.LIMIT and self.has_market()) and self._allow_market_queue:

        # If order is LIMIT and there are market orders in the queue, match with the first market order in the queue
        queued_mo = orders[self.market_orders[0]]
        self.fill(order, queued_mo)
        if queued_mo.status == OrderStatus.FILLED:
            self.market_orders.popleft()

    else:

        # Match with the best limit order
        queued_lo = orders[self.limit_orders[self.bestP][0]]
        self.fill(order, queued_lo)

        self.volumes[self.bestP] -= queued_lo.matches[-1][0]
        self.bestV -= queued_lo.matches[-1][0]
        if queued_lo.status == OrderStatus.FILLED:
            # If the matched limit order is fully filled remove it from the queue and update order book
            self.limit_orders[self.bestP].popleft()
            if len(self.limit_orders[self.bestP]) == 0:
                del self.limit_orders[self.bestP]
                del self.volumes[self.bestP]
                if len(self._bestHeap):
                    self.bestP = self._sign * heappop(self._bestHeap)
                    self.bestV = self.volumes[self.bestP]
                else:
                    self.bestP = None
                    self.bestV = 0