import math from enum import Enum, auto from typing import override from internal_types.types import OHLC, BidAsk, Instrument, Position from strategy.strategy import Strategy from utils.utils import Portfolio class State(Enum): POS_0 = auto() POS_1 = auto() class BuyAndHold(Strategy): def __init__(self, init_balance, instr: Instrument): self.state = State.POS_0 self.balance = init_balance self.instr = instr self.portfolio = Portfolio() self.desired_portfolio = Portfolio() def __process(self, at_price: float): match self.state: case State.POS_0: quantity = math.floor(self.balance / at_price / self.instr.multiplier) self.desired_portfolio.add_position(Position(self.instr, quantity, at_price)) self.state = State.POS_1 case State.POS_1: pass case _: raise RuntimeError('invalid state') @override def unfilled_positions(self, instr: Instrument) -> Position: x = self.desired_portfolio.outstanding_shares(instr) - self.portfolio.outstanding_shares(instr) return self.desired_portfolio.consolidate_last_x_shares(instr, x) @override def order_filled(self, new_pos: Position): self.portfolio.add_position(new_pos) @override def process_bid_ask(self, bid_ask: BidAsk): if bid_ask.instr != self.instr: return self.__process(bid_ask.ask) @override def process_ohlc(self, ohlc: OHLC): if ohlc.instr != self.instr: return self.__process(ohlc.close) @override def net_liquid_value(self, at_price: float) -> float: return self.balance + self.portfolio.total_gains(self.instr, at_price)