from collections import deque CONVERSION_FEE = 5 class StateManager: def __init__(self, tick_size=20): symbols = ["BOND", "VALBZ", "VALE", "GS", "MS", "WFC", "XLF"] self.positions = {s: 0 for s in symbols} self.orders = {s: {} for s in symbols} self.bid_prices = {s: None for s in symbols} self.ask_prices = {s: None for s in symbols} self.bid_depths = {s: 0 for s in symbols} self.ask_depths = {s: 0 for s in symbols} self.tick_size = tick_size self.price_ticks = {s: deque(maxlen=tick_size) for s in symbols} self.last_prices = {s: None for s in symbols} self.fair_values = { "BOND": 1000, "VALBZ": None, "VALE": None, "GS": None, "MS": None, "WFC": None, "XLF": None, } self.last_prices = {} self.etf_components = { "XLF": {"BOND": 3, "GS": 2, "MS": 3, "WFC": 2} } self.etf_shares = {"XLF": 10} def add_price_tick(self, symbol: str, price: int): self.price_ticks[symbol].append(price) self.last_prices[symbol] = price def get_price_ticks(self, symbol: str): return list(self.price_ticks.get(symbol, [])) def get_last_price(self, symbol: str): return self.last_prices.get(symbol) def get_avg_price(self, symbol: str): ticks = self.get_price_ticks(symbol) if ticks: return sum(ticks) / len(ticks) return None def get_ma(self, symbol: str, period: int): ticks = self.get_price_ticks(symbol) if len(ticks) >= period: return sum(list(ticks)[-period:]) / period return None def update_position(self, symbol: str, quantity: int): self.positions[symbol] = self.positions.get(symbol, 0) + quantity def update_bid_price(self, symbol: str, price: int): self.bid_prices[symbol] = price def update_ask_price(self, symbol: str, price: int): self.ask_prices[symbol] = price def get_position(self, symbol: str) -> int: return self.positions.get(symbol, 0) def get_spread(self, symbol: str) -> int: bid = self.bid_prices.get(symbol) ask = self.ask_prices.get(symbol) if bid is not None and ask is not None: return ask - bid return None def update_last_price(self, symbol: str, price: int): self.last_prices[symbol] = price def get_mid_price(self, symbol: str): bid = self.bid_prices[symbol] ask = self.ask_prices[symbol] if bid is not None and ask is not None: return (bid + ask) // 2 return self.last_prices.get(symbol) def update_depth(self, symbol: str, bid_depth: int, ask_depth: int): self.bid_depths[symbol] = bid_depth self.ask_depths[symbol] = ask_depth def get_imbalance(self, symbol: str) -> int: bid_depth = self.bid_depths[symbol] ask_depth = self.ask_depths[symbol] return bid_depth - ask_depth def get_predicted_price(self, symbol: str) -> int: mid_price = self.get_mid_price(symbol) imbalance = self.get_imbalance(symbol) if mid_price is not None: return self.get_last_price(symbol) def update_fair_value(self): valbz_price = self.get_mid_price("VALBZ") if valbz_price is not None: self.fair_values["VALBZ"] = valbz_price self.fair_values["VALE"] = valbz_price - self.conversion_fee bond_price = self.get_mid_price("BOND") gs_price = self.get_mid_price("GS") ms_price = self.get_mid_price("MS") wfc_price = self.get_mid_price("WFC") if all(p is not None for p in [bond_price, gs_price, ms_price, wfc_price]): component_value = ( bond_price * self.etf_components["XLF"]["BOND"] + gs_price * self.etf_components["XLF"]["GS"] + ms_price * self.etf_components["XLF"]["MS"] + wfc_price * self.etf_components["XLF"]["WFC"] ) / self.etf_shares["XLF"] self.fair_values["XLF"] = component_value def get_fair_value(self, symbol: str) -> int: return self.fair_values.get(symbol)