Skip to content

order module

Order

Class representing an order in the securities exchange.

Attributes:

Name Type Description
ticker str

Ticker symbol for the order.

type OrderType

Type of the order (Market or Limit).

side MarketSide

Side of the market (Buy or Sell).

size int

Size of the order.

price float

Price of the order (optional for Market orders).

timestamp int

Timestamp of order creation.

status OrderStatus

Status of the order (UNFILLED, PARTIALLY_FILLED, FILLED).

matches deque

Queue to store order matches.

residual_size int

Remaining size to be filled.

avg_fill_price float

Average fill price of the order.

error bool

Flag indicating if there's an error in the order.

Methods

update: Updates the order status and properties after a fill.

Source code in securities_exchange/order.py
class Order:

    """
    Class representing an order in the securities exchange.

    Attributes:
        ticker (str): Ticker symbol for the order.
        type (OrderType): Type of the order (Market or Limit).
        side (MarketSide): Side of the market (Buy or Sell).
        size (int): Size of the order.
        price (float): Price of the order (optional for Market orders).
        timestamp (int): Timestamp of order creation.
        status (OrderStatus): Status of the order (UNFILLED, PARTIALLY_FILLED, FILLED).
        matches (deque): Queue to store order matches.
        residual_size (int): Remaining size to be filled.
        avg_fill_price (float): Average fill price of the order.
        error (bool): Flag indicating if there's an error in the order.

    Methods:
        update: Updates the order status and properties after a fill.
    """    

    @validate_call
    def __init__(self, ticker: str, type: OrderType, side: MarketSide, size: int, price: float = None):

        """
        Initialize an order with the provided parameters.

        Args:
            ticker (str): Ticker symbol for the order.
            type (OrderType): Type of the order (Market or Limit).
            side (MarketSide): Side of the market (Buy or Sell).
            size (int): Size of the order.
            price (float): Price of the order (optional for Market orders).
        """

        # Set timestamp to current time in microseconds
        self.timestamp = int(datetime.now().timestamp() * 1e6)
        self.ticker = ticker
        self.type = type
        self.side = side
        self.size = size
        self.price = price
        self.status = OrderStatus.UNFILLED
        self.matches = deque()
        self.residual_size = size
        self.avg_fill_price = 0
        self.error = False

        # Handle special cases for market orders
        if self.price is not None and self.type == OrderType.MARKET:
            logger.warning("Market orders will ignore 'price'. Price attribute set to None")
            self.price = None        

        # Generate a unique order ID based on order attributes
        keys = [self.timestamp, self.ticker, self.type.name, self.side.name, self.size]
        if self.price is not None and self.type == OrderType.LIMIT:
            keys.append(f"@{self.price}")
        self.id = ''.join(map(str, keys))


    def __repr__(self) -> str:
        d = {k: v for k, v in self.__dict__.items()}
        d["type"] = d["type"].name
        d["side"] = d["side"].name
        d["status"] = d["status"].name
        d["matches"] = len(d["matches"])
        if d["price"] is None:
            del d["price"] 
        _repr = ",\n      ".join(map(lambda x: f"{x[0]} = {x[1]}", d.items()))
        return f"Order({_repr})"


    def update(self, filled_quantity: int, at_price: float, matched_order_id: str):

        """
        Update the order after a fill.

        Args:
            filled_quantity (int): Quantity filled in the latest match.
            at_price (float): Price at which the latest match occurred.
            matched_order_id (str): ID of the order that was matched.
        """

        # Ensure filled_quantity does not exceed the remaining size
        filled_quantity = min(filled_quantity, self.residual_size)

        # Update average fill price
        self.avg_fill_price *= (self.size - self.residual_size)
        self.avg_fill_price += filled_quantity * at_price
        self.avg_fill_price /= (self.size - self.residual_size + filled_quantity)

        # Update residual size
        self.residual_size -= filled_quantity

        # Update order status based on residual size
        if self.residual_size == 0:
            self.status = OrderStatus.FILLED
            logger.info(f"Order {self.id} filled {filled_quantity} units at price {at_price} with order {matched_order_id}")
        elif self.residual_size > 0:
            self.status = OrderStatus.PARTIALLY_FILLED
            logger.info(f"Order {self.id} partially filled {filled_quantity} units at price {at_price} with order {matched_order_id}")

        # Record the match in the matches queue
        self.matches.append((filled_quantity, at_price, matched_order_id))

__init__(self, ticker, type, side, size, price=None) special

Initialize an order with the provided parameters.

Parameters:

Name Type Description Default
ticker str

Ticker symbol for the order.

required
type OrderType

Type of the order (Market or Limit).

required
side MarketSide

Side of the market (Buy or Sell).

required
size int

Size of the order.

required
price float

Price of the order (optional for Market orders).

None
Source code in securities_exchange/order.py
@validate_call
def __init__(self, ticker: str, type: OrderType, side: MarketSide, size: int, price: float = None):

    """
    Initialize an order with the provided parameters.

    Args:
        ticker (str): Ticker symbol for the order.
        type (OrderType): Type of the order (Market or Limit).
        side (MarketSide): Side of the market (Buy or Sell).
        size (int): Size of the order.
        price (float): Price of the order (optional for Market orders).
    """

    # Set timestamp to current time in microseconds
    self.timestamp = int(datetime.now().timestamp() * 1e6)
    self.ticker = ticker
    self.type = type
    self.side = side
    self.size = size
    self.price = price
    self.status = OrderStatus.UNFILLED
    self.matches = deque()
    self.residual_size = size
    self.avg_fill_price = 0
    self.error = False

    # Handle special cases for market orders
    if self.price is not None and self.type == OrderType.MARKET:
        logger.warning("Market orders will ignore 'price'. Price attribute set to None")
        self.price = None        

    # Generate a unique order ID based on order attributes
    keys = [self.timestamp, self.ticker, self.type.name, self.side.name, self.size]
    if self.price is not None and self.type == OrderType.LIMIT:
        keys.append(f"@{self.price}")
    self.id = ''.join(map(str, keys))

update(self, filled_quantity, at_price, matched_order_id)

Update the order after a fill.

Parameters:

Name Type Description Default
filled_quantity int

Quantity filled in the latest match.

required
at_price float

Price at which the latest match occurred.

required
matched_order_id str

ID of the order that was matched.

required
Source code in securities_exchange/order.py
def update(self, filled_quantity: int, at_price: float, matched_order_id: str):

    """
    Update the order after a fill.

    Args:
        filled_quantity (int): Quantity filled in the latest match.
        at_price (float): Price at which the latest match occurred.
        matched_order_id (str): ID of the order that was matched.
    """

    # Ensure filled_quantity does not exceed the remaining size
    filled_quantity = min(filled_quantity, self.residual_size)

    # Update average fill price
    self.avg_fill_price *= (self.size - self.residual_size)
    self.avg_fill_price += filled_quantity * at_price
    self.avg_fill_price /= (self.size - self.residual_size + filled_quantity)

    # Update residual size
    self.residual_size -= filled_quantity

    # Update order status based on residual size
    if self.residual_size == 0:
        self.status = OrderStatus.FILLED
        logger.info(f"Order {self.id} filled {filled_quantity} units at price {at_price} with order {matched_order_id}")
    elif self.residual_size > 0:
        self.status = OrderStatus.PARTIALLY_FILLED
        logger.info(f"Order {self.id} partially filled {filled_quantity} units at price {at_price} with order {matched_order_id}")

    # Record the match in the matches queue
    self.matches.append((filled_quantity, at_price, matched_order_id))