diff --git a/bot.py b/bot.py index 6af5ee7..4653f2c 100644 --- a/bot.py +++ b/bot.py @@ -11,6 +11,8 @@ import time import socket import json +from state import StateManager + # ~~~~~============== CONFIGURATION ==============~~~~~ # Replace "REPLACEME" with your team name! team_name = "HanyangFloorFunction" @@ -29,6 +31,7 @@ team_name = "HanyangFloorFunction" def main(): args = parse_arguments() + state = StateManager() exchange = ExchangeConnection(args=args) diff --git a/state.py b/state.py new file mode 100644 index 0000000..9b41d1d --- /dev/null +++ b/state.py @@ -0,0 +1,120 @@ +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)