diff --git a/prac.py b/prac.py index f5b1361..15acdee 100644 --- a/prac.py +++ b/prac.py @@ -2,7 +2,6 @@ import argparse from enum import Enum -import time import socket import json from state import StateManager @@ -10,65 +9,38 @@ from state import StateManager team_name = "HanyangFloorFunction" # ==================== 설정 ==================== -BOND_FAIR_VALUE = 1000 -STOCK_SPREAD = 6 -STOCK_ORDER_SIZE = 5 -REFRESH_INTERVAL = 1.0 -MIN_PROFIT_BUFFER = 10 -MAX_POSITION_SOFT = 50 +STOCK_SPREAD = 5 +ORDER_SIZE = 5 +MIN_PROFIT = 5 +MAX_POS = 50 -XLF_CONVERSION_FEE = 100 -VALE_CONVERSION_FEE = 10 - -# ==================== 방향 ==================== +# ==================== class Dir(str, Enum): BUY = "BUY" SELL = "SELL" -# ==================== 메인 ==================== +# ==================== def main(): args = parse_arguments() - exchange = ExchangeConnection(args=args) + exchange = ExchangeConnection(args) - hello = exchange.read_message() state = StateManager() + hello = exchange.read_message() - # 🔥 자체 order id + # 🔥 포지션 직접 관리 (핵심) + positions = {} + for sym in hello["symbols"]: + positions[sym["symbol"]] = sym["position"] + + # 🔥 order id order_id = 0 def next_id(): nonlocal order_id order_id += 1 return order_id - implied_fairs = {"GS": None, "MS": None, "WFC": None} + # ==================== 전략 ==================== - for sym_info in hello.get("symbols", []): - state.update_position(sym_info["symbol"], sym_info["position"]) - - # ==================== FAIR VALUE ==================== - def update_fair(): - try: - xlf_mid = (state.bid_prices["XLF"] + state.ask_prices["XLF"]) / 2 - gs_mid = (state.bid_prices["GS"] + state.ask_prices["GS"]) / 2 - ms_mid = (state.bid_prices["MS"] + state.ask_prices["MS"]) / 2 - wfc_mid = (state.bid_prices["WFC"] + state.ask_prices["WFC"]) / 2 - except: - return - - residual = xlf_mid * 10 - 3000 - total = gs_mid*2 + ms_mid*3 + wfc_mid*2 - if total <= 0: - return - - raw_gs = residual * (gs_mid*2 / total) / 2 - raw_ms = residual * (ms_mid*3 / total) / 3 - raw_wfc = residual * (wfc_mid*2 / total) / 2 - - implied_fairs["GS"] = int(0.7 * gs_mid + 0.3 * raw_gs) - implied_fairs["MS"] = int(0.7 * ms_mid + 0.3 * raw_ms) - implied_fairs["WFC"] = int(0.7 * wfc_mid + 0.3 * raw_wfc) - - # ==================== BOND ==================== def bond_arb(): bid = state.bid_prices.get("BOND") ask = state.ask_prices.get("BOND") @@ -79,78 +51,60 @@ def main(): if bid and bid > 1000: exchange.send_add_message_ioc(next_id(), "BOND", Dir.SELL, bid, 10) - # ==================== MARKET MAKING ==================== - def market_make(sym): - bid = state.bid_prices.get(sym) - ask = state.ask_prices.get(sym) - fair = implied_fairs.get(sym) - - if None in [bid, ask] or fair is None: - return - - pos = state.get_position(sym) - - buy_price = min(fair - STOCK_SPREAD, bid + 1) - sell_price = max(fair + STOCK_SPREAD, ask - 1) - - if pos > 0: - buy_size, sell_size = 1, STOCK_ORDER_SIZE + 3 - elif pos < 0: - buy_size, sell_size = STOCK_ORDER_SIZE + 3, 1 - else: - buy_size = sell_size = STOCK_ORDER_SIZE - - exchange.send_add_message(next_id(), sym, Dir.BUY, int(buy_price), buy_size) - exchange.send_add_message(next_id(), sym, Dir.SELL, int(sell_price), sell_size) - - # ==================== XLF ==================== - def xlf_arb(): - try: - basket_ask = ( - state.ask_prices["BOND"]*3 + - state.ask_prices["GS"]*2 + - state.ask_prices["MS"]*3 + - state.ask_prices["WFC"]*2 - ) - xlf_bid = state.bid_prices["XLF"] - except: - return - - profit = xlf_bid*10 - basket_ask - XLF_CONVERSION_FEE - - if profit > MIN_PROFIT_BUFFER: - exchange.send_add_message_ioc(next_id(), "XLF", Dir.SELL, xlf_bid, 10) - - # ==================== VALE ==================== def vale_arb(): - vale_bid = state.bid_prices.get("VALE") + vale_bid = state.bid_prices.get("VALE") valbz_ask = state.ask_prices.get("VALBZ") - if None in [vale_bid, valbz_ask]: + if vale_bid and valbz_ask: + if vale_bid - valbz_ask > MIN_PROFIT: + exchange.send_add_message_ioc(next_id(), "VALBZ", Dir.BUY, valbz_ask, 1) + + def xlf_arb(): + fair = state.get_fair_value("XLF") + bid = state.bid_prices.get("XLF") + + if fair and bid: + if bid - fair > MIN_PROFIT: + exchange.send_add_message_ioc(next_id(), "XLF", Dir.SELL, bid, 10) + + def market_make(sym): + mid = state.get_mid_price(sym) + if mid is None: return - if vale_bid - valbz_ask - VALE_CONVERSION_FEE > 5: - exchange.send_add_message_ioc(next_id(), "VALBZ", Dir.BUY, valbz_ask, 1) + pos = positions.get(sym, 0) + + buy_price = mid - STOCK_SPREAD + sell_price = mid + STOCK_SPREAD + + if pos > 0: + buy_size = 1 + sell_size = ORDER_SIZE + 3 + elif pos < 0: + buy_size = ORDER_SIZE + 3 + sell_size = 1 + else: + buy_size = sell_size = ORDER_SIZE + + exchange.send_add_message(next_id(), sym, Dir.BUY, buy_price, buy_size) + exchange.send_add_message(next_id(), sym, Dir.SELL, sell_price, sell_size) - # ==================== 리스크 ==================== def risk(): for sym in ["GS", "MS", "WFC"]: - pos = state.get_position(sym) + pos = positions.get(sym, 0) - if abs(pos) > MAX_POSITION_SOFT: + if abs(pos) > MAX_POS: if pos > 0: exchange.send_add_message_ioc( next_id(), sym, Dir.SELL, - state.bid_prices.get(sym, 1), abs(pos) + state.bid_prices.get(sym), abs(pos) ) else: exchange.send_add_message_ioc( next_id(), sym, Dir.BUY, - state.ask_prices.get(sym, 99999), abs(pos) + state.ask_prices.get(sym), abs(pos) ) - last_refresh = time.time() - # ==================== LOOP ==================== while True: msg = exchange.read_message() @@ -161,36 +115,32 @@ def main(): elif msg["type"] == "book": sym = msg["symbol"] - state.update_bid_ask_price( - sym, - msg["buy"][0][0] if msg["buy"] else None, - msg["sell"][0][0] if msg["sell"] else None - ) + bid = msg["buy"][0][0] if msg["buy"] else None + ask = msg["sell"][0][0] if msg["sell"] else None + + state.update_bid_ask_price(sym, bid, ask) + state.update_predicted_price(sym) + state.update_fair_value() - update_fair() bond_arb() - xlf_arb() vale_arb() - risk() + xlf_arb() if sym in ["GS", "MS", "WFC"]: market_make(sym) - if time.time() - last_refresh > REFRESH_INTERVAL: - last_refresh = time.time() - for s in ["GS", "MS", "WFC"]: - market_make(s) + risk() elif msg["type"] == "fill": qty = msg["size"] sym = msg["symbol"] if msg["dir"] == Dir.BUY: - state.update_position(sym, qty) + positions[sym] = positions.get(sym, 0) + qty else: - state.update_position(sym, -qty) + positions[sym] = positions.get(sym, 0) - qty -# ==================== 연결 ==================== +# ==================== class ExchangeConnection: def __init__(self, args): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) @@ -214,10 +164,10 @@ class ExchangeConnection: def _write(self, msg): self.writer.send((json.dumps(msg)+"\n").encode()) -# ==================== args ==================== +# ==================== def parse_arguments(): parser = argparse.ArgumentParser() - parser.add_argument("--test", type=str, default="prod-like") + parser.add_argument("--test", default="prod-like") args = parser.parse_args() args.exchange_hostname = "test-exch-" + team_name