commit
This commit is contained in:
107
prac.py
107
prac.py
@@ -46,36 +46,40 @@ def main():
|
||||
# pick? Also, you will need to send more orders over time.
|
||||
# --- 설정 ---
|
||||
BOND_FAIR_VALUE = 1000 # BOND fair value (고정)
|
||||
BOND_ORDER_SIZE = 50 # BOND 주문당 수량
|
||||
BOND_ORDER_SIZE = 60 # BOND 주문당 수량
|
||||
XLF_CONVERSION_FEE = 100 # XLF 변환 비용
|
||||
XLF_MIN_PROFIT = 10 # XLF 차익거래 최소 수익 (불확실한 거래 방지)
|
||||
XLF_MIN_PROFIT = 5 # XLF 차익거래 최소 수익
|
||||
XLF_COOLDOWN = 2.0 # XLF 실패 후 재시도 대기 시간 (초)
|
||||
VALE_CONVERSION_FEE = 10 # VALE 변환 비용
|
||||
VALE_MIN_PROFIT = 3 # VALE 차익거래 최소 수익
|
||||
VALE_MIN_PROFIT = 1 # VALE 차익거래 최소 수익
|
||||
VALE_ARB_SIZE = 10 # VALE 차익거래 단위
|
||||
REFRESH_INTERVAL = 5.0 # 주문 갱신 주기 (초)
|
||||
|
||||
# XLF state machine
|
||||
# IDLE → BUYING_BASKET → CONVERTING → SELLING_XLF → IDLE
|
||||
# IDLE → BUYING_XLF → CONVERTING → SELLING_BASKET → IDLE
|
||||
xlf_state = "IDLE"
|
||||
xlf_pending = {}
|
||||
xlf_direction = None
|
||||
xlf_arb_size = 0
|
||||
xlf_state = "IDLE"
|
||||
xlf_pending = {}
|
||||
xlf_direction = None
|
||||
xlf_arb_size = 0
|
||||
xlf_convert_oid = None
|
||||
xlf_last_fail = 0.0 # 마지막 XLF 실패 시간
|
||||
|
||||
# VALE state machine
|
||||
# VALE state machine (XLF와 독립적으로 동시 실행 가능)
|
||||
# IDLE → BUYING_VALBZ → CONVERTING → SELLING_VALE → IDLE
|
||||
# IDLE → BUYING_VALE → CONVERTING → SELLING_VALBZ → IDLE
|
||||
vale_state = "IDLE"
|
||||
vale_pending = {}
|
||||
vale_direction = None
|
||||
vale_arb_size = 0
|
||||
vale_state = "IDLE"
|
||||
vale_pending = {}
|
||||
vale_direction = None
|
||||
vale_arb_size = 0
|
||||
vale_convert_oid = None
|
||||
|
||||
state = StateManager()
|
||||
om = OrderManager(exchange)
|
||||
market_open = False
|
||||
active_orders = {} # BOND 전용 {order_id: {"dir": ..., "price": ..., "size": ...}}
|
||||
|
||||
last_refresh = time.time()
|
||||
last_refresh = time.time()
|
||||
|
||||
# hello 메시지에서 기존 포지션 로드 (재접속 시 필수)
|
||||
for sym_info in hello_message["symbols"]:
|
||||
@@ -89,6 +93,16 @@ def main():
|
||||
def next_id():
|
||||
return om.next_order()
|
||||
|
||||
def cancel_bond_sell_orders():
|
||||
"""BOND 매도 주문만 취소 (basket→XLF 시 매수 주문은 유지)"""
|
||||
for oid in list(active_orders.keys()):
|
||||
order_info = active_orders[oid]
|
||||
if order_info["dir"] == Dir.SELL:
|
||||
size = order_info.get("size", 0)
|
||||
om.future_positions["BOND"] += size
|
||||
om.cancel(oid)
|
||||
active_orders.pop(oid, None)
|
||||
|
||||
def cancel_all_bond_orders():
|
||||
"""활성 BOND 주문 전부 취소 + future_positions 롤백"""
|
||||
for oid in list(active_orders.keys()):
|
||||
@@ -151,12 +165,16 @@ def main():
|
||||
print(f" BOND 주문 → 매수:{buy_price} x{buy_size}, 매도:{sell_price} x{sell_size}, 포지션:{position}")
|
||||
|
||||
def try_xlf_arb():
|
||||
"""XLF 차익거래 시도 - IDLE 상태일 때만"""
|
||||
nonlocal xlf_state, xlf_direction, xlf_pending, xlf_arb_size
|
||||
"""XLF 차익거래 시도 - IDLE + 쿨다운 통과 시에만"""
|
||||
nonlocal xlf_state, xlf_direction, xlf_pending, xlf_arb_size, xlf_convert_oid
|
||||
|
||||
if not market_open or xlf_state != "IDLE":
|
||||
return
|
||||
|
||||
# 실패 후 쿨다운 체크
|
||||
if time.time() - xlf_last_fail < XLF_COOLDOWN:
|
||||
return
|
||||
|
||||
bond_ask = state.ask_prices["BOND"]
|
||||
gs_ask = state.ask_prices["GS"]
|
||||
ms_ask = state.ask_prices["MS"]
|
||||
@@ -175,12 +193,20 @@ def main():
|
||||
basket_ask = bond_ask*3 + gs_ask*2 + ms_ask*3 + wfc_ask*2
|
||||
basket_bid = bond_bid*3 + gs_bid*2 + ms_bid*3 + wfc_bid*2
|
||||
|
||||
# 바스켓 심볼 포지션 한도 체크
|
||||
def basket_has_room():
|
||||
return (
|
||||
om.positions["BOND"] + 3 <= 100 and
|
||||
om.positions["GS"] + 2 <= 100 and
|
||||
om.positions["MS"] + 3 <= 100 and
|
||||
om.positions["WFC"] + 2 <= 100
|
||||
)
|
||||
|
||||
# 케이스 1: 바스켓 매수 → XLF 변환 → XLF 매도
|
||||
profit1 = xlf_bid * 10 - basket_ask - XLF_CONVERSION_FEE
|
||||
if profit1 > XLF_MIN_PROFIT and om.check_pos_limit("XLF"):
|
||||
if profit1 > XLF_MIN_PROFIT and om.check_pos_limit("XLF") and basket_has_room():
|
||||
print(f" XLF 차익(바스켓→XLF) 시작, 예상수익:{profit1}")
|
||||
# BOND 주문 먼저 취소 (XLF용 BOND 매수가 기존 매도에 상쇄되지 않도록)
|
||||
cancel_all_bond_orders()
|
||||
cancel_bond_sell_orders()
|
||||
xlf_state = "BUYING_BASKET"
|
||||
xlf_direction = "BASKET_TO_XLF"
|
||||
xlf_arb_size = 10
|
||||
@@ -206,7 +232,7 @@ def main():
|
||||
|
||||
def handle_xlf_fill(order_id, symbol, dir_, qty):
|
||||
"""XLF state machine 체결 처리 (부분 체결 추적)"""
|
||||
nonlocal xlf_state, xlf_pending
|
||||
nonlocal xlf_state, xlf_pending, xlf_convert_oid
|
||||
|
||||
if order_id not in xlf_pending:
|
||||
return
|
||||
@@ -218,16 +244,18 @@ def main():
|
||||
if xlf_state == "BUYING_BASKET" and not xlf_pending:
|
||||
print(" 바스켓 매수 완료 → XLF 변환 시작")
|
||||
xlf_state = "CONVERTING"
|
||||
exchange.send_convert_message(next_id(), "XLF", Dir.BUY, xlf_arb_size)
|
||||
xlf_convert_oid = next_id()
|
||||
exchange.send_convert_message(xlf_convert_oid, "XLF", Dir.BUY, xlf_arb_size)
|
||||
|
||||
elif xlf_state == "BUYING_XLF" and not xlf_pending:
|
||||
print(" XLF 매수 완료 → 바스켓 변환 시작")
|
||||
xlf_state = "CONVERTING"
|
||||
exchange.send_convert_message(next_id(), "XLF", Dir.SELL, xlf_arb_size)
|
||||
xlf_convert_oid = next_id()
|
||||
exchange.send_convert_message(xlf_convert_oid, "XLF", Dir.SELL, xlf_arb_size)
|
||||
|
||||
def try_vale_arb():
|
||||
"""VALE/VALBZ 차익거래 시도 - IDLE 상태일 때만"""
|
||||
nonlocal vale_state, vale_direction, vale_pending, vale_arb_size
|
||||
"""VALE/VALBZ 차익거래 시도 - XLF와 독립적으로 동시 실행"""
|
||||
nonlocal vale_state, vale_direction, vale_pending, vale_arb_size, vale_convert_oid
|
||||
|
||||
if not market_open or vale_state != "IDLE":
|
||||
return
|
||||
@@ -272,7 +300,7 @@ def main():
|
||||
|
||||
def handle_vale_fill(order_id, symbol, dir_, qty):
|
||||
"""VALE state machine 체결 처리 (부분 체결 추적)"""
|
||||
nonlocal vale_state, vale_pending
|
||||
nonlocal vale_state, vale_pending, vale_convert_oid
|
||||
|
||||
if order_id not in vale_pending:
|
||||
return
|
||||
@@ -284,12 +312,14 @@ def main():
|
||||
if vale_state == "BUYING_VALBZ" and not vale_pending:
|
||||
print(" VALBZ 매수 완료 → VALE 변환 시작")
|
||||
vale_state = "CONVERTING"
|
||||
exchange.send_convert_message(next_id(), "VALE", Dir.BUY, vale_arb_size)
|
||||
vale_convert_oid = next_id()
|
||||
exchange.send_convert_message(vale_convert_oid, "VALE", Dir.BUY, vale_arb_size)
|
||||
|
||||
elif vale_state == "BUYING_VALE" and not vale_pending:
|
||||
print(" VALE 매수 완료 → VALBZ 변환 시작")
|
||||
vale_state = "CONVERTING"
|
||||
exchange.send_convert_message(next_id(), "VALE", Dir.SELL, vale_arb_size)
|
||||
vale_convert_oid = next_id()
|
||||
exchange.send_convert_message(vale_convert_oid, "VALE", Dir.SELL, vale_arb_size)
|
||||
|
||||
# Set up some variables to track the bid and ask price of a symbol. Right
|
||||
# now this doesn't track much information, but it's enough to get a sense
|
||||
@@ -336,19 +366,22 @@ def main():
|
||||
active_orders.pop(oid, None)
|
||||
if oid in xlf_pending:
|
||||
print(" XLF 주문 reject → IDLE 복귀")
|
||||
xlf_state = "IDLE"
|
||||
xlf_state = "IDLE"
|
||||
xlf_pending.clear()
|
||||
xlf_direction = None
|
||||
place_bond_orders()
|
||||
xlf_last_fail = time.time() # 쿨다운 시작
|
||||
place_bond_orders() # try_xlf_arb() 호출 제거
|
||||
if oid in vale_pending:
|
||||
print(" VALE 주문 reject → IDLE 복귀")
|
||||
vale_state = "IDLE"
|
||||
vale_state = "IDLE"
|
||||
vale_pending.clear()
|
||||
vale_direction = None
|
||||
|
||||
elif message["type"] == "ack":
|
||||
# XLF 변환 ack 처리
|
||||
if xlf_state == "CONVERTING":
|
||||
ack_oid = message.get("order_id")
|
||||
|
||||
# XLF 변환 ack 처리 (order_id로 구분)
|
||||
if xlf_state == "CONVERTING" and ack_oid == xlf_convert_oid:
|
||||
print(" XLF 변환 완료 → 매도 시작")
|
||||
if xlf_direction == "BASKET_TO_XLF":
|
||||
# 변환: BOND -3, GS -2, MS -3, WFC -2, XLF +10
|
||||
@@ -388,8 +421,8 @@ def main():
|
||||
)
|
||||
xlf_pending[oid] = qty
|
||||
|
||||
# VALE 변환 ack 처리
|
||||
elif vale_state == "CONVERTING":
|
||||
# VALE 변환 ack 처리 (order_id로 구분)
|
||||
elif vale_state == "CONVERTING" and ack_oid == vale_convert_oid:
|
||||
print(" VALE 변환 완료 → 매도 시작")
|
||||
if vale_direction == "VALBZ_TO_VALE":
|
||||
# 변환: VALBZ -size, VALE +size
|
||||
@@ -423,6 +456,7 @@ def main():
|
||||
dir_ = message["dir"]
|
||||
oid = message["order_id"]
|
||||
|
||||
# 포지션 업데이트
|
||||
if dir_ == Dir.BUY:
|
||||
om.update_position(sym, oid, qty)
|
||||
else:
|
||||
@@ -430,22 +464,27 @@ def main():
|
||||
|
||||
print(f" 포지션 → {om.positions}")
|
||||
|
||||
# BOND 체결 시 무조건 재주문
|
||||
if sym == "BOND" and oid in active_orders:
|
||||
active_orders.pop(oid, None)
|
||||
place_bond_orders()
|
||||
|
||||
# XLF state machine 체결 처리
|
||||
handle_xlf_fill(oid, sym, dir_, qty)
|
||||
if xlf_state in ("SELLING_XLF", "SELLING_BASKET") and not xlf_pending:
|
||||
print(" XLF 차익거래 완료 → IDLE 복귀")
|
||||
xlf_state = "IDLE"
|
||||
xlf_direction = None
|
||||
place_bond_orders()
|
||||
# try_xlf_arb() 제거 → book 메시지에서 자동 시도
|
||||
|
||||
# VALE state machine 체결 처리
|
||||
handle_vale_fill(oid, sym, dir_, qty)
|
||||
if vale_state in ("SELLING_VALE", "SELLING_VALBZ") and not vale_pending:
|
||||
print(" VALE 차익거래 완료 → IDLE 복귀")
|
||||
vale_state = "IDLE"
|
||||
vale_direction = None
|
||||
# try_vale_arb() 제거 → book 메시지에서 자동 시도
|
||||
|
||||
elif message["type"] == "book":
|
||||
sym = message["symbol"]
|
||||
@@ -621,4 +660,4 @@ if __name__ == "__main__":
|
||||
"Please put your team name in the variable [team_name]."
|
||||
)
|
||||
|
||||
main()
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user