fix(P0): 인증 누락·레거시 DT 엔진 병존·혼재 종목 재태깅·자정 후 스테일 청산 무력화

감사 CRITICAL 4건:
- /daytrading/{watchlist,execute,close-all,cycle} 에 require_api_key 누락 → 인증 없이
  전량 청산 가능했음 (CLAUDE.md §4 위반)
- 레거시 day_trading._positions 가 엔진 day_trading_state 와 별도로 기동 시 복원돼
  같은 포지션이 두 엔진에 존재 → /close-all 시 이중 매도. 복원 제거 + close-all/cycle 을
  엔진에 위임, /execute 실매수는 410(엔진 미관리 포지션 방지)
- reflect_buy 가 기존 행을 daytrading 으로 재태깅 → 같은 종목 스윙 보유분이
  get_swing_holdings 에서 빠져 손절·익절 정지. 신규 행일 때만 태깅
- close_stale_positions 가 인메모리만 순회 → 자정 reset 후 대상이 사라져 무력.
  ActiveTrade(day_trade/active/전일) 조회 기반으로 변경하고 인메모리가 비어도 실행

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-23 10:49:30 +09:00
parent 6207d13487
commit 70def8fe0b
6 changed files with 214 additions and 30 deletions

View File

@@ -42,8 +42,10 @@ async def reflect_buy(
existing.quantity = new_qty
existing.average_price = Decimal(str(round(new_avg, 2)))
existing.current_price = Decimal(str(price))
if strategy_type == "daytrading":
existing.strategy_type = "daytrading"
# [2026-09-23] 기존 행의 strategy_type 은 변경하지 않는다.
# 스윙 8주 보유 중 DT 4주를 사면 행(12주)이 daytrading 이 되어
# get_swing_holdings 에서 빠지고 스윙 8주의 손절·익절이 완전히 멈춘다.
# 화면의 DT 그룹은 엔진 포지션(/daytrading/positions)으로 별도 표시한다.
avg_for_db, qty_for_db = new_avg, new_qty
else:
cache.holdings.append(Portfolio(
@@ -55,15 +57,18 @@ async def reflect_buy(
))
avg_for_db, qty_for_db = price, quantity
await enqueue_portfolio_upsert({
_upsert = {
"stock_code": stock_code,
"stock_name": stock_name,
"quantity": qty_for_db,
"average_price": round(avg_for_db, 2),
"current_price": price,
"market": market,
"strategy_type": strategy_type,
})
}
if existing is None:
# 신규 행일 때만 전략 태깅 (기존 행은 다른 전략 보유분이 섞여 있을 수 있음)
_upsert["strategy_type"] = strategy_type
await enqueue_portfolio_upsert(_upsert)
await cache.apply_buy(stock_code, stock_name, quantity, price)
except Exception as e:
logger.warning(f"[cache_bridge] reflect_buy 실패 ({stock_code}): {e}")