diff --git a/prac.py b/prac.py new file mode 100644 index 0000000..d94dc50 --- /dev/null +++ b/prac.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python3 +import argparse +from collections import deque +from enum import Enum +import time +import socket +import json + +# ~~~~~============== CONFIGURATION ==============~~~~~ +team_name = "HanyangFloorFunction" + +# --- 유틸리티 및 상태 관리 클래스 --- +class Dir(str, Enum): + BUY = "BUY" + SELL = "SELL" + +class StateManager: + def __init__(self): + self.positions = {} + self.bid_prices = {} + self.ask_prices = {} + + def update_position(self, symbol, delta): + self.positions[symbol] = self.positions.get(symbol, 0) + delta + + def get_position(self, symbol): + return self.positions.get(symbol, 0) + + def update_bid_ask_price(self, symbol, bid, ask): + self.bid_prices[symbol] = bid + self.ask_prices[symbol] = ask + +class OrderManager: + def __init__(self): + self.order_id_counter = 0 + def next_order(self): + self.order_id_counter += 1 + return self.order_id_counter + +# --- 메인 봇 로직 --- +def main(): + args = parse_arguments() + exchange = ExchangeConnection(args=args) + + hello_message = exchange.read_message() + state = StateManager() + om = OrderManager() + + # 재연결 시 기존 포지션 복원 + for sym_info in hello_message.get("symbols", []): + state.update_position(sym_info["symbol"], sym_info["position"]) + + # ==================== 상수 및 설정 ==================== + BOND_FAIR = 1000 # 고정 가치 + XLF_FEE = 100 # 변환 수수료 + VALE_FEE = 10 # 변환 수수료 + + # 수익 안정화를 위한 문턱 값 (기존보다 높게 설정) + MIN_ARB_PROFIT = 30 + + market_open = False + last_refresh = time.time() + active_orders = set() + + def next_id(): return om.next_order() + + # ==================== 거래 함수 ==================== + + def trade_bond(): + """BOND는 항상 1000원이므로 999 이하 매수, 1001 이상 매도""" + if not market_open: return + pos = state.get_position("BOND") + + # 매수 주문 (100개 제한 준수) + if pos < 100: + exchange.send_add_message(next_id(), "BOND", Dir.BUY, 999, 100 - pos) + # 매도 주문 + if pos > -100: + exchange.send_add_message(next_id(), "BOND", Dir.SELL, 1001, 100 + pos) + + def trade_xlf_arb(): + """XLF 차익거래: 바스켓 가치와 XLF 가격 비교""" + prices = {s: (state.bid_prices.get(s), state.ask_prices.get(s)) for s in ["BOND", "GS", "MS", "WFC", "XLF"]} + if any(None in p for p in prices.values()): return + + # 바스켓 매수 가치 (최악의 경우 내가 사야하는 가격) + basket_buy_cost = prices["BOND"][1]*3 + prices["GS"][1]*2 + prices["MS"][1]*3 + prices["WFC"][1]*2 + xlf_sell_price = prices["XLF"][0] * 10 + + # 수익 계산 (수수료 포함) + profit = xlf_sell_price - basket_buy_cost - XLF_FEE + if profit > MIN_ARB_PROFIT: + print(f"[XLF ARB] 기회 포착! 수익: {profit}") + # 단순화를 위해 즉시 시장가(IOC)로 실행하거나 변환 로직 수행 + # (여기에 기존의 State Machine 기반 복잡한 로직을 넣되 Profit 문턱을 꼭 지킬 것) + + # ==================== 메인 루프 ==================== + while True: + message = exchange.read_message() + + if message["type"] == "open": + market_open = True + trade_bond() # 개장하자마자 BOND 주문 + + elif message["type"] == "book": + sym = message["symbol"] + state.update_bid_ask_price( + sym, + message["buy"][0][0] if message["buy"] else None, + message["sell"][0][0] if message["sell"] else None + ) + + if sym == "BOND": + trade_bond() + elif sym in ["XLF", "GS", "MS", "WFC"]: + trade_xlf_arb() + + elif message["type"] == "fill": + sym, qty, direction = message["symbol"], message["size"], message["dir"] + state.update_position(sym, qty if direction == Dir.BUY else -qty) + print(f"체결: {sym} {direction} {qty} | 현재 포지션: {state.positions.get(sym)}") + + if sym == "BOND": trade_bond() # BOND 체결 시 즉시 다시 주문 보충 + + elif message["type"] == "close": + market_open = False + +# --- 제공된 연결 코드 (수정 불필요) --- +class ExchangeConnection: + def __init__(self, args): + self.exchange_hostname = args.exchange_hostname + self.port = args.port + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect((self.exchange_hostname, self.port)) + self.reader = s.makefile("r", 1) + self.writer = s + self._write_message({"type": "hello", "team": team_name.upper()}) + + def read_message(self): + return json.loads(self.reader.readline()) + + def _write_message(self, message): + self.writer.send((json.dumps(message) + "\n").encode("utf-8")) + + def send_add_message(self, order_id, symbol, dir, price, size): + self._write_message({"type": "add", "order_id": order_id, "symbol": symbol, "dir": dir, "price": price, "size": size, "tif": "DAY"}) + +def parse_arguments(): + parser = argparse.ArgumentParser() + parser.add_argument("--test", type=str, choices=["prod-like", "slower", "empty"]) + args = parser.parse_args() + args.exchange_hostname = f"test-exch-{team_name}" + args.port = 22000 + (0 if args.test == "prod-like" else 1 if args.test == "slower" else 2) + return args + +if __name__ == "__main__": + main() \ No newline at end of file