Stock-Algorithm-Back-Tester/Orders.py
2024-08-14 14:10:18 -05:00

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