Files
JSETC/prac.py
2026-05-09 13:55:48 +09:00

157 lines
5.5 KiB
Python

#!/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()