fix: 하드닝 루프 8회차 — 체결 보정·고아 감지·청산 경보·실시간/차트·리스크 가드 일괄 수정

- 스윙 체결가 확인 전 executed_price NULL + fill_sweep, 진입가 resolve(ActiveTrade↔Portfolio), 0체결/부분체결 매수 장부 원복
- DT 고아 감지(근거 한정)·0체결 매도 복원·stale 점검 확대, 시간손절 -0.3% 원복
- 폴백 손절 ATR 캐시, 거래량부족-차단 신호 매수 차단, 현금 5% 유보 재적용, MDD peak/reset 수정
- KIS 서킷브레이커 청산 우회·경보, db_worker 재큐잉 순서, heartbeat·health-watch 사이드카
- 실시간 WS 재구독·41건 우선순위·다건 프레임 분할, 차트 샘플 제거·당일봉 합성, WS 서브프로토콜 인증
- 프론트: 미관리 그룹·확인 모달·차트 실시간 반영·예수금 표시 정정
- 전문가 에이전트 6종 추가(.claude/agents)
- 유닛 스위트 2667 passed (소켓 차단 하), tsc OK

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y83Za98XsNw9zuA4FTUytN
This commit is contained in:
2026-10-10 12:17:49 +09:00
parent 5c4803b339
commit a264561576
230 changed files with 41160 additions and 2334 deletions

View File

@@ -34,6 +34,7 @@ from app.services.us_stock_strategy import (
from app.services.korea_investment_api import korea_investment_api
from app.services.exchange_detector import detect_exchange
from app.services.mdd_manager import mdd_manager
from app.services.loss_tracker import loss_tracker
from app.services.db_worker import (
enqueue_trade_history,
enqueue_active_trade_upsert,
@@ -436,6 +437,27 @@ async def _monitor_us_exits(exchange_rate: float = settings.US_DEFAULT_FX_RATE)
# ── 단일 매매 사이클 ────────────────────────────────────────────────────────────
async def _us_entry_blocked_by_ban_v2(ticker: str) -> Optional[str]:
"""연속손실 밴(§5.1) 확인. 차단 사유 문자열 또는 None(진입 가능).
판정 불가(예외)는 fail-closed — try 에는 조회만 두고 판정은 밖에서 한다.
섹터 3종목·상관 0.7 필터는 국내 데이터(섹터맵/KR 일봉) 전용이라 미국 종목에
적용할 수 없어 미구현 (US_TRADING_ENABLED 켜기 전 별도 작업).
"""
banned = True
err: Optional[str] = None
try:
banned = await loss_tracker.is_banned_safe(ticker)
except Exception as e:
err = f"{type(e).__name__}: {e}"
if err is not None:
logger.warning("[US Buy] %s 밴 판정 불가 — 신규 진입 차단(fail-closed): %s", ticker, err)
return "ban_check_unavailable"
if banned:
return "loss_ban"
return None
async def us_trade_cycle() -> dict:
"""단일 매매 사이클: 스캔 → 신호 체크 → 주문 실행 → 포지션 청산 모니터링
@@ -512,6 +534,11 @@ async def us_trade_cycle() -> dict:
current_price_usd = float(sig["price"])
exchange = detect_exchange(ticker)
_ban_reason = await _us_entry_blocked_by_ban_v2(ticker)
if _ban_reason is not None:
logger.info("[US Buy] %s — %s, 건너뜀", ticker, _ban_reason)
continue
try:
# 7-1. 포지션 사이징
buy_budget_krw = calculate_us_position_size(