from abc import ABC, abstractmethod from typing import TYPE_CHECKING if TYPE_CHECKING: from Common import TickData from datetime import datetime class GenericLimitOrder(ABC): def __init__(self, symbol: str, datetime_placed: 'datetime', quantity: float, limit_price: float, order_id: int = None): self._symbol = symbol self._datetime_placed = datetime_placed self._quantity = quantity self._limit_price = limit_price self._needed_cost = quantity * limit_price self._id = order_id # Starts as None until the BrokerageSimulator returns the order_id @property def symbol(self) -> str: return self._symbol @property def datetime_placed(self) -> 'datetime': return self._datetime_placed @property def quantity(self) -> float: return self._quantity @property def limit_price(self) -> float: return self._limit_price @property def needed_cost(self) -> float: return self._needed_cost @property def id(self) -> int: return self._id def change_id(self, new_id: int) -> None: '''Change the id after creation. Only allowed if the current id is None''' if self._id is not None: raise ValueError("Trying to change id of order that already has id") else: self._id = new_id @abstractmethod def is_fillable(self, tick_data: 'TickData') -> bool: '''Return whether or not the order should be filled based on self.limit_price and the low and high price of the tick Overload this operator''' pass class BuyOrder(GenericLimitOrder): '''Buy order''' def __init__(self, symbol: str, datetime_placed: 'datetime', quantity: float, limit_price: float, order_id: int): super().__init__(symbol, datetime_placed, quantity, limit_price, order_id) def is_fillable(self, tick_data: 'TickData') -> bool: '''Return whether or not the order should be filled based on self.limit_price and the low and high price of the tick''' if tick_data.low <= self._limit_price: return True else: return False class SellOrder(GenericLimitOrder): '''Sell order''' def __init__(self, symbol: str, datetime_placed: 'datetime', quantity: float, limit_price: float, order_id: int): super().__init__(symbol, datetime_placed, quantity, limit_price, order_id) def is_fillable(self, tick_data: 'TickData') -> bool: '''Return whether or not the order should be filled based on self.limit_price and the low and high price of the tick''' if tick_data.high >= self._limit_price: return True else: return False class OrderFilledResponse(): def __init__(self, symbol: str, matching_order_id: int, datetime_filled: 'datetime', quantity: float, price: float, commission: float): self.symbol = symbol self.matching_order_id = matching_order_id self.datetime_filled = datetime_filled self.quantity = quantity self.price = price self.commission = commission self.total_cost = quantity * price + commission self.cost_per_share = self.total_cost / self.quantity