59 lines
1.6 KiB
Python
59 lines
1.6 KiB
Python
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)
|