commit
This commit is contained in:
185
prac.py
185
prac.py
@@ -1,7 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import argparse
|
import argparse
|
||||||
from collections import deque
|
|
||||||
from enum import Enum
|
|
||||||
import time
|
import time
|
||||||
import socket
|
import socket
|
||||||
import json
|
import json
|
||||||
@@ -9,8 +7,8 @@ import json
|
|||||||
# ~~~~~============== CONFIGURATION ==============~~~~~
|
# ~~~~~============== CONFIGURATION ==============~~~~~
|
||||||
team_name = "HanyangFloorFunction"
|
team_name = "HanyangFloorFunction"
|
||||||
|
|
||||||
# --- 유틸리티 및 상태 관리 클래스 ---
|
# --- 상태 관리 및 유틸리티 ---
|
||||||
class Dir(str, Enum):
|
class Dir:
|
||||||
BUY = "BUY"
|
BUY = "BUY"
|
||||||
SELL = "SELL"
|
SELL = "SELL"
|
||||||
|
|
||||||
@@ -37,119 +35,100 @@ class OrderManager:
|
|||||||
self.order_id_counter += 1
|
self.order_id_counter += 1
|
||||||
return self.order_id_counter
|
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:
|
class ExchangeConnection:
|
||||||
def __init__(self, args):
|
def __init__(self, args):
|
||||||
self.exchange_hostname = args.exchange_hostname
|
self.exchange_hostname = args.exchange_hostname
|
||||||
self.port = args.port
|
self.port = args.port
|
||||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
# 타임아웃 설정으로 무한 대기 방지
|
||||||
s.connect((self.exchange_hostname, self.port))
|
self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
self.reader = s.makefile("r", 1)
|
self.s.settimeout(5)
|
||||||
self.writer = s
|
self.s.connect((self.exchange_hostname, self.port))
|
||||||
|
self.reader = self.s.makefile("r", 1)
|
||||||
|
# 초기 Handshake
|
||||||
self._write_message({"type": "hello", "team": team_name.upper()})
|
self._write_message({"type": "hello", "team": team_name.upper()})
|
||||||
|
|
||||||
def read_message(self):
|
def read_message(self):
|
||||||
return json.loads(self.reader.readline())
|
line = self.reader.readline()
|
||||||
|
if not line: return None
|
||||||
|
return json.loads(line)
|
||||||
|
|
||||||
def _write_message(self, message):
|
def _write_message(self, message):
|
||||||
self.writer.send((json.dumps(message) + "\n").encode("utf-8"))
|
# 모든 메시지 끝에는 줄바꿈(\n)이 필수입니다
|
||||||
|
self.s.send((json.dumps(message) + "\n").encode("utf-8"))
|
||||||
|
|
||||||
def send_add_message(self, order_id, symbol, dir, price, size):
|
def send_add_message(self, order_id, symbol, direction, price, size):
|
||||||
self._write_message({"type": "add", "order_id": order_id, "symbol": symbol, "dir": dir, "price": price, "size": size, "tif": "DAY"})
|
self._write_message({
|
||||||
|
"type": "add", "order_id": order_id, "symbol": symbol,
|
||||||
|
"dir": direction, "price": price, "size": size, "tif": "DAY"
|
||||||
|
})
|
||||||
|
|
||||||
|
# --- 메인 봇 로직 ---
|
||||||
|
def main():
|
||||||
|
args = parse_arguments()
|
||||||
|
try:
|
||||||
|
exchange = ExchangeConnection(args)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"연결 실패: {e}")
|
||||||
|
return
|
||||||
|
|
||||||
|
state = StateManager()
|
||||||
|
om = OrderManager()
|
||||||
|
market_open = False
|
||||||
|
|
||||||
|
# 첫 메시지 처리 (Handshake Hello)
|
||||||
|
hello_msg = exchange.read_message()
|
||||||
|
if hello_msg and "symbols" in hello_msg:
|
||||||
|
for sym_info in hello_msg["symbols"]:
|
||||||
|
state.update_position(sym_info["symbol"], sym_info["position"])
|
||||||
|
|
||||||
|
print("봇이 시작되었습니다. P/L 복구 모드 가동.")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
message = exchange.read_message()
|
||||||
|
if not message: continue
|
||||||
|
|
||||||
|
msg_type = message.get("type")
|
||||||
|
|
||||||
|
if msg_type == "open":
|
||||||
|
market_open = True
|
||||||
|
print("시장이 열렸습니다.")
|
||||||
|
|
||||||
|
elif msg_type == "book":
|
||||||
|
sym = message["symbol"]
|
||||||
|
# 최우선 호가 업데이트
|
||||||
|
bid = message["buy"][0][0] if message["buy"] else None
|
||||||
|
ask = message["sell"][0][0] if message["sell"] else None
|
||||||
|
state.update_bid_ask_price(sym, bid, ask)
|
||||||
|
|
||||||
|
# BOND 로직: 1000원 고정 가치를 활용한 안전 수익
|
||||||
|
if sym == "BOND" and market_open:
|
||||||
|
pos = state.get_position("BOND")
|
||||||
|
if pos < 100:
|
||||||
|
exchange.send_add_message(om.next_order(), "BOND", Dir.BUY, 999, 100 - pos)
|
||||||
|
if pos > -100:
|
||||||
|
exchange.send_add_message(om.next_order(), "BOND", Dir.SELL, 1001, 100 + pos)
|
||||||
|
|
||||||
|
elif msg_type == "fill":
|
||||||
|
# 체결 시 포지션 동기화
|
||||||
|
sym, qty, direction = message["symbol"], message["size"], message["dir"]
|
||||||
|
delta = qty if direction == Dir.BUY else -qty
|
||||||
|
state.update_position(sym, delta)
|
||||||
|
print(f"[FILL] {sym} {direction} {qty} | 포지션: {state.positions.get(sym)}")
|
||||||
|
|
||||||
|
elif msg_type == "close":
|
||||||
|
market_open = False
|
||||||
|
print("시장이 닫혔습니다.")
|
||||||
|
|
||||||
|
elif msg_type == "error":
|
||||||
|
print(f"서버 에러: {message.get('error')}")
|
||||||
|
|
||||||
def parse_arguments():
|
def parse_arguments():
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("--test", type=str, choices=["prod-like", "slower", "empty"])
|
parser.add_argument("--test", type=str, default="prod-like", choices=["prod-like", "slower", "empty"])
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
args.exchange_hostname = f"test-exch-{team_name}"
|
# 팀 이름에 맞게 호스트네임 설정
|
||||||
|
args.exchange_hostname = f"test-exch-{team_name.lower()}"
|
||||||
args.port = 22000 + (0 if args.test == "prod-like" else 1 if args.test == "slower" else 2)
|
args.port = 22000 + (0 if args.test == "prod-like" else 1 if args.test == "slower" else 2)
|
||||||
return args
|
return args
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user