Skip to content

securities_exchange module

SecuritiesExchange

Class representing a securities exchange.

Attributes:

Name Type Description
orders OrderedDict

Dictionary to store all submitted orders.

rejected_orders OrderedDict

Dictionary to store rejected orders.

markets dict

Dictionary to store order books for different tickers.

_allow_market_queue bool

Flag indicating whether market orders can be queued.

Methods

_init_market: Initializes an order book for a new ticker. _validate_order: Validates the properties of an order and logs errors if any. submit_order: Submits an order to the exchange for processing. get_order: Retrieves an order based on its ID.

Source code in securities_exchange/securities_exchange.py
class SecuritiesExchange:

    """
    Class representing a securities exchange.

    Attributes:
        orders (OrderedDict): Dictionary to store all submitted orders.
        rejected_orders (OrderedDict): Dictionary to store rejected orders.
        markets (dict): Dictionary to store order books for different tickers.
        _allow_market_queue (bool): Flag indicating whether market orders can be queued.

    Methods:
        _init_market: Initializes an order book for a new ticker.
        _validate_order: Validates the properties of an order and logs errors if any.
        submit_order: Submits an order to the exchange for processing.
        get_order: Retrieves an order based on its ID.
    """

    def __init__(self, allow_market_queue: bool = False):

        """
        Initialize a SecuritiesExchange instance.

        Args:
            allow_market_queue (bool): Flag indicating whether market orders can be queued.
        """

        self.orders = OrderedDict()
        self.rejected_orders = OrderedDict()
        self.markets = {}
        self._allow_market_queue = allow_market_queue


    def _init_market(self, ticker: str):

        """
        Initialize an order book for a new ticker.

        Args:
            ticker (str): Ticker symbol for the market.
        """

        self.markets[ticker] = OrderBook(allow_market_queue=self._allow_market_queue)


    def _validate_order(self, order: Order):

        """
        Validate the properties of an order and log errors if any.

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

        msg = f"Order {order.id} has been REJECTED."
        if (order.price is None or order.price <= 0) and (order.type == OrderType.LIMIT):
            order.error = True
            msg += "\n\t- LIMIT orders require a non-null positive PRICE."

        if (order.size <= 0):
            order.error = True
            msg += "\n\t- Orders require a non-null positive SIZE."

        if order.error:
            logger.error(msg)


    def submit_order(self, order: Order) -> bool:

        """
        Submits an order to the exchange for processing.

        Args:
            order (Order): The order to be submitted.

        Returns:
            bool: True if the order is successfully submitted, False otherwise.
        """

        # Validate the order before processing
        self._validate_order(order)

        if order.error:
            # If the order is invalid, store it in the rejected_orders dictionary and return False
            self.rejected_orders[order.id] = order
            return False

        if order.ticker not in self.markets:
            # If the ticker is not in markets, initialize a new market
            self._init_market(order.ticker)

        # Store the order in the orders dictionary
        self.orders[order.id] = order

        logger.info(f"Order {order.id} submitted for {order.ticker}")

        # Process the order in the corresponding market
        self.markets[order.ticker].process_order(order, self.orders)

        # Log information about the order based on the market queue settings
        if self._allow_market_queue:
            if (order.type == OrderType.MARKET) and (order.status == OrderStatus.UNFILLED):             
                logger.info(f"Order {order.id} has been added to the Market Orders queue for the full amount.")
            elif (order.type == OrderType.MARKET) and (order.status == OrderStatus.PARTIALLY_FILLED):
                logger.info(f"Order {order.id} has been added to the Market Orders queue for the residual amount of {order.residual_size} units.")
        else:
            if (order.type == OrderType.MARKET) and (order.status == OrderStatus.UNFILLED):             
                logger.info(f"Order {order.id} couldn't be matched.")

        return True


    def get_order(self, order_id: str) -> Order:

        """
        Retrieves an order based on its ID.

        Args:
            order_id (str): The ID of the order to retrieve.

        Returns:
            Order: The order corresponding to the given ID.
        """

        if order_id in self.orders:
            return self.orders.get(order_id)

        return self.rejected_orders.get(order_id)

__init__(self, allow_market_queue=False) special

Initialize a SecuritiesExchange instance.

Parameters:

Name Type Description Default
allow_market_queue bool

Flag indicating whether market orders can be queued.

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

    """
    Initialize a SecuritiesExchange instance.

    Args:
        allow_market_queue (bool): Flag indicating whether market orders can be queued.
    """

    self.orders = OrderedDict()
    self.rejected_orders = OrderedDict()
    self.markets = {}
    self._allow_market_queue = allow_market_queue

get_order(self, order_id)

Retrieves an order based on its ID.

Parameters:

Name Type Description Default
order_id str

The ID of the order to retrieve.

required

Returns:

Type Description
Order

The order corresponding to the given ID.

Source code in securities_exchange/securities_exchange.py
def get_order(self, order_id: str) -> Order:

    """
    Retrieves an order based on its ID.

    Args:
        order_id (str): The ID of the order to retrieve.

    Returns:
        Order: The order corresponding to the given ID.
    """

    if order_id in self.orders:
        return self.orders.get(order_id)

    return self.rejected_orders.get(order_id)

submit_order(self, order)

Submits an order to the exchange for processing.

Parameters:

Name Type Description Default
order Order

The order to be submitted.

required

Returns:

Type Description
bool

True if the order is successfully submitted, False otherwise.

Source code in securities_exchange/securities_exchange.py
def submit_order(self, order: Order) -> bool:

    """
    Submits an order to the exchange for processing.

    Args:
        order (Order): The order to be submitted.

    Returns:
        bool: True if the order is successfully submitted, False otherwise.
    """

    # Validate the order before processing
    self._validate_order(order)

    if order.error:
        # If the order is invalid, store it in the rejected_orders dictionary and return False
        self.rejected_orders[order.id] = order
        return False

    if order.ticker not in self.markets:
        # If the ticker is not in markets, initialize a new market
        self._init_market(order.ticker)

    # Store the order in the orders dictionary
    self.orders[order.id] = order

    logger.info(f"Order {order.id} submitted for {order.ticker}")

    # Process the order in the corresponding market
    self.markets[order.ticker].process_order(order, self.orders)

    # Log information about the order based on the market queue settings
    if self._allow_market_queue:
        if (order.type == OrderType.MARKET) and (order.status == OrderStatus.UNFILLED):             
            logger.info(f"Order {order.id} has been added to the Market Orders queue for the full amount.")
        elif (order.type == OrderType.MARKET) and (order.status == OrderStatus.PARTIALLY_FILLED):
            logger.info(f"Order {order.id} has been added to the Market Orders queue for the residual amount of {order.residual_size} units.")
    else:
        if (order.type == OrderType.MARKET) and (order.status == OrderStatus.UNFILLED):             
            logger.info(f"Order {order.id} couldn't be matched.")

    return True