feat: 데이트레이딩 ORB 복구 + 스윙:DT 자본 50:50 + 시간대 제한 완화

- ORB: 09:00에 당일 분석이 없으면 최근 분석일 결과로 watchlist 선채움 → ORB 수집 가능.
  자동 목록은 30분마다 갱신(당일 분석 완료 시 교체), 수동 설정 목록은 덮어쓰지 않음.
- 자본 배분: DAY_TRADE_MAX_RATIO_DT 0.25→0.50, 종목당 4%→6%, 동시 3→8종목.
  SWING_MAX_CAPITAL_RATIO=0.50 신설 — 스윙 합산 투자액 초과 시 스윙 신규 매수 차단(trade_executor).
- 시간대: 점심(11:30~13:00) VWAP/RSI_BB 제한 제거, 하드코딩 14:30 마감 → 설정값 15:00.
- 회귀 테스트 3건, CLAUDE.md §6/§7.3/§8.2 갱신.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-22 13:29:02 +09:00
parent 62c324e346
commit e0178aaf0d
6 changed files with 196 additions and 28 deletions

View File

@@ -829,6 +829,30 @@ async def _execute_buy(
if invest_amount <= 0:
return {"action": "SKIP", "reason": "global_capital_guard", "stock": stock_name}
# [스윙:DT 자본 배분 2026-09-22] 스윙 합산 투자액이 총자산 × SWING_MAX_CAPITAL_RATIO 를
# 넘지 않도록 상한. 스윙 투자액 = 주식 평가액 − 데이트레이딩 포지션 투자액.
# (스윙이 자본 91%를 점유해 DT 예산이 남지 않던 문제 → 50:50)
_swing_cap_ratio = float(getattr(settings, "SWING_MAX_CAPITAL_RATIO", 1.0) or 1.0)
if 0 < _swing_cap_ratio < 1 and total_assets > 0:
try:
from app.services.daytrading.position_manager import day_trading_state as _dts
_dt_amount = sum(
float(p.get("entry_price", 0) or 0) * int(p.get("quantity", 0) or 0)
for p in _dts.positions.values()
)
except Exception:
_dt_amount = 0.0
_swing_invested = max(0.0, total_assets - available_cash - _dt_amount)
_swing_room = total_assets * _swing_cap_ratio - _swing_invested
if invest_amount > _swing_room:
invest_amount = max(0.0, _swing_room)
if invest_amount <= 0:
logger.info(
f"[TRADE] {stock_name}: 스윙 자본 상한 도달 "
f"({_swing_invested/total_assets:.1%} ≥ {_swing_cap_ratio:.0%}) — 신규 매수 차단"
)
return {"action": "SKIP", "reason": "swing_capital_cap", "stock": stock_name}
# ── 7.5. 패턴 메모리 기반 포지션 조정 ──────────────────────────────────
# 종목별 과거 행동 패턴(지지/저항선, RSI 반등, 승률 등)을 기반으로
# 투자금액을 -30% ~ +30% 범위에서 동적 조정.