82 lines
3.2 KiB
Python
82 lines
3.2 KiB
Python
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 |