feat(execution): AccountOrderWorker로 매수·매도 주문 직렬화

전략별 tick/scan 매도 락 대신 계좌 단일 PriorityQueue로 place를 B-full 직렬화한다.
틱매도 only_code 필터와 inflight 중복 enqueue 방지로 REST 폭주를 줄인다.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Your Name
2026-08-28 16:45:26 +09:00
parent ffe84a9375
commit bc2b1b642c
15 changed files with 541 additions and 207 deletions

View File

@@ -70,6 +70,7 @@ from .database.db_manager import get_db
from .execution.kis_client import KISClient
from .execution.account_cash import AccountCashLedger
from .execution.order_manager import OrderManager
from .execution.order_worker import AccountOrderWorker
from .network.condition_manager import ConditionSearchManager
from .network.kiwoom_condition_manager import KiwoomConditionSearchManager
from .network.ls_condition_manager import (
@@ -189,6 +190,7 @@ class TradingOrchestrator:
self.order_mgr = OrderManager(
client=self.client, db=self.db, cash_ledger=self.cash_ledger,
)
self.order_worker = AccountOrderWorker(self.order_mgr)
self.ws = WSManager(db=self.db, kis_client=self.market_client)
# 해외(US) 실시간(지연)체결가 수신기 — 국내 WS와 분리된 별도 인스턴스(야간 가동).
# permanent_subscriptions 테이블의 US 종목을 HDFSCNT0 로 구독. start()에서 기동.
@@ -380,7 +382,7 @@ class TradingOrchestrator:
if buy_price > 0 and px > 0:
profit_pct = (px - buy_price) / buy_price * 100.0
try:
strat._submit_sell({
strat._enqueue_sell({
"code": code,
"name": (row or {}).get("name") or h.get("name") or code,
"qty": qty,
@@ -389,7 +391,7 @@ class TradingOrchestrator:
"buy_price": buy_price,
"profit_pct": profit_pct,
"reason": reason,
})
}, source="halt")
except Exception as ex:
logger.warning(
"⚠️ [리스크버짓] %s %s 청산 실패: %s", sid_u, code, ex,
@@ -723,6 +725,10 @@ class TradingOrchestrator:
self._register_strategies()
# 해외 WS 는 등록 전에 기동됨 → 여기서 US_MOMENTUM 에 핸들 주입
self._attach_overseas_ws_to_strategies()
for strat in self.strategies:
self.order_worker.register_strategy(strat)
strat.order_worker = self.order_worker
self.order_worker.start()
for strat in self.strategies:
strat.start() # threading.Thread.start()
logger.info("▶ 쓰레드 기동: %s", strat.name)
@@ -2666,6 +2672,8 @@ class TradingOrchestrator:
)
if getattr(ns, "strategy_id", "") == "US_MOMENTUM" and self.overseas_ws:
ns.overseas_ws = self.overseas_ws
ns.order_worker = self.order_worker
self.order_worker.register_strategy(ns)
ns.start()
new_list.append(ns)
logger.info("🔁 전략 재기동: %s", ns.name)
@@ -2734,6 +2742,10 @@ class TradingOrchestrator:
def stop(self) -> None:
for s in self.strategies:
s.stop_loop()
try:
self.order_worker.stop()
except Exception:
pass
for s in self.strategies:
try:
s.join(timeout=5)
@@ -2802,6 +2814,13 @@ class TradingOrchestrator:
def main() -> None:
import faulthandler
import signal
faulthandler.enable()
try:
faulthandler.register(signal.SIGUSR1, all_threads=True, chain=False)
except:
pass
orch = TradingOrchestrator()
try:
orch.start()