fix: 자동매매 신뢰성·소액계좌 안전배치 5종

감사에서 드러난 신뢰성·조용한실패 버그 수정. 전략값(§5)·시그니처(§4) 불변.

① KIS API 신뢰성(korea_investment_api, config): 스로틀에 _throttle_lock 추가로
   thundering herd(8코루틴 동시발사) 제거, API_TIMEOUT 10→20초, circuit_breaker
   임계 15→7, 재시도 백오프 지터. → 시간당 121건 실패의 근원 완화.
② portfolio summary 보존(portfolio): 잔고 API 실패 시 summary가 영영 None으로
   남아 현금 0→매수 스킵되던 것을, 최초 실패 시 holdings 기반 부분 summary로 채움.
③ output2 빈리스트 가드: KIS가 output2=[] 반환 시 IndexError로 MDD 업데이트가
   조용히 스킵(하방보호 비활성)되던 것 방어. swing/scheduler·adaptive_strategy
   (실버그) + daytrading 2곳(정규화). trade_executor:555는 load-bearing 가드라
   의도적 미변경.
④ MIN_BUY_AMOUNT config화+소액완화(trade_executor, config): 하드코딩 50만을
   MIN_BUY_AMOUNT_KRW로. 자산 833만↓ 계좌가 전종목 차단되던 것을 자산 대비로
   완화(큰 계좌는 기존 50만 하한 불변).
⑤ adaptive 서브버그(adaptive_strategy): fallback 시 _last_update_ts 갱신(refresh
   남발 방지), force_mode_override 전체필드(crisis의 max_daily_trades=0 실제 적용),
   get_event_loop→get_running_loop.

검증: code-verifier 통과(CONFIRMED 버그/회귀 없음, 4개 위험지점 안전), pytest 442 통과.
(day_trading.py·entry_service.py는 소량 인접 선재편집 포함 — 검증 완료.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HBmzf1HDnvFHLraJ8eaaKJ
This commit is contained in:
2026-09-16 16:59:59 +09:00
parent 5b80223777
commit 408bcce62e
8 changed files with 117 additions and 43 deletions

View File

@@ -552,6 +552,9 @@ async def _execute_buy(
if available_cash <= 0:
try:
balance = await korea_investment_api.get_balance()
# NOTE: output2가 빈 리스트([])일 때 balance.get()의 default([{}])는 적용되지 않으므로
# 아래 `if output2:` 가드가 빈 리스트 케이스를 걸러낸다 (IndexError 방지 + 기존
# available_cash/total_assets 캐시값을 의도적으로 덮어쓰지 않고 보존).
output2 = balance.get("output2", [{}])
if output2:
b = output2[0] if isinstance(output2, list) else output2
@@ -777,8 +780,20 @@ async def _execute_buy(
logger.info(f"[BUY_SKIP] {stock_code}/{stock_name}: {_qz_reason} (invest={invest_amount:,.0f})")
return {"action": "SKIP", "reason": _qz_reason, "stock": stock_name}
# 최소 매수 금액 가드: 50만원 미만이면 수수료 대비 비효율
_MIN_BUY_AMOUNT = 500_000
# 최소 매수 금액 가드: 수수료 대비 비효율 방지 (기본 config.MIN_BUY_AMOUNT_KRW, 50만원)
# 큰 계좌는 기존과 동일하게 config 하한(기본 50만원)이 그대로 적용된다.
# 소액 계좌(총자산이 작아 6% 예산 자체가 50만원 미만인 경우)는 이 하한 때문에
# 전종목이 영구 차단되는 문제를 완화하기 위해, "config 하한"과
# "총자산 × 적응형 포지션상한 × 0.9" 중 더 작은 값을 실효 하한으로 사용한다.
# (총자산이 충분히 크면 항상 config값이 더 작게 선택되어 기존 동작과 동일 — 소액 계좌만 완화됨.)
_MIN_BUY_AMOUNT = settings.MIN_BUY_AMOUNT_KRW
try:
if total_assets > 0 and _adaptive_position_cap > 0:
_small_account_cap = total_assets * _adaptive_position_cap * 0.9
if _small_account_cap > 0:
_MIN_BUY_AMOUNT = min(_MIN_BUY_AMOUNT, _small_account_cap)
except Exception:
_MIN_BUY_AMOUNT = settings.MIN_BUY_AMOUNT_KRW
_actual_amount = quantity * current_price
if _actual_amount < _MIN_BUY_AMOUNT:
logger.info(f"[SKIP] {stock_code}: 매수금액 {_actual_amount:,.0f}원 < 최소 {_MIN_BUY_AMOUNT:,.0f}원")