fix(P1): MDD 축소 수량 반영·ActiveTrade 전략명 필수화·스윙 매도에서 DT 보유분 제외
- mdd_manager: 강제 축소가 TradeHistory/remaining_pct 만 갱신하고 실제 수량은 어디도 줄이지 않아 다음 청산이 축소 전 수량으로 매도했음 → _reflect_reduction()으로 Portfolio·DT 인메모리 반영, ActiveTrade.quantity 도 잔량으로 갱신 (두 경로 모두) - swing/exit_service: close/upsert 에 strategy_name 미전달 시 db_worker 가 최신 active 행(=DT)을 닫거나 트레일링 고점을 덮어씀 → 항상 전략명 명시 - trade_executor._execute_sell: Portfolio 행(스윙+DT 합계)을 그대로 매도 대상으로 삼아 DT 보유분까지 팔았음 → 엔진 보유 수량을 제외한 자기 전략 보유분만 대상, upsert 에 전략명 - portfolio.sync: DT 태깅이 해외 루프에만 적용돼 국내 DT 매수분이 swing 으로 저장되면 스윙 엔진이 손절 → 국내에도 적용. _dt_qty 정의를 output1 블록 밖으로 올려 NameError 방지 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -329,8 +329,13 @@ class MDDManager:
|
||||
await enqueue_active_trade_upsert({
|
||||
"stock_code": stock_code,
|
||||
"remaining_pct_multiply": sell_ratio,
|
||||
"quantity": remaining_qty, # 잔량 갱신(미갱신 시 다음 청산이 과다매도)
|
||||
})
|
||||
|
||||
# [2026-09-23] 축소 체결분을 Portfolio·DT 인메모리에도 반영.
|
||||
# 누락 시 다음 청산이 축소 전 수량으로 매도한다(과다매도).
|
||||
await _reflect_reduction(stock_code, sell_qty, current_price, remaining_qty)
|
||||
|
||||
results.append({
|
||||
"stock_code": stock_code,
|
||||
"stock_name": stock_name,
|
||||
@@ -490,8 +495,13 @@ class MDDManager:
|
||||
"user_id": user_id,
|
||||
"stock_code": stock_code,
|
||||
"remaining_pct_multiply": sell_ratio,
|
||||
"quantity": remaining_qty,
|
||||
})
|
||||
|
||||
# [2026-09-23] 축소 체결분을 Portfolio·DT 인메모리에도 반영.
|
||||
# 누락 시 다음 청산이 축소 전 수량으로 매도한다(과다매도).
|
||||
await _reflect_reduction(stock_code, sell_qty, current_price, remaining_qty)
|
||||
|
||||
results.append({
|
||||
"stock_code": stock_code,
|
||||
"stock_name": stock_name,
|
||||
@@ -530,3 +540,32 @@ class MDDManager:
|
||||
|
||||
# 싱글톤 인스턴스
|
||||
mdd_manager = MDDManager()
|
||||
|
||||
|
||||
async def _reflect_reduction(
|
||||
stock_code: str, sell_qty: int, price: float, remaining_qty: int
|
||||
) -> None:
|
||||
"""MDD 축소 체결을 Portfolio 캐시/DB 와 데이트레이딩 인메모리 포지션에 반영.
|
||||
|
||||
ActiveTrade 잔량은 호출측에서 upsert 하므로 여기서는 Portfolio 와 인메모리만 다룬다.
|
||||
"""
|
||||
try:
|
||||
from app.services.portfolio_cache_bridge import reflect_sell
|
||||
await reflect_sell(stock_code, sell_qty, price, remaining_qty=remaining_qty)
|
||||
except Exception as e:
|
||||
logger.warning(f"[MDD] Portfolio 반영 실패 ({stock_code}): {e}")
|
||||
|
||||
try:
|
||||
from app.services.daytrading.position_manager import day_trading_state
|
||||
async with day_trading_state.trade_lock:
|
||||
pos = day_trading_state.positions.get(stock_code)
|
||||
if pos:
|
||||
_left = max(0, int(pos.get("quantity", 0) or 0) - sell_qty)
|
||||
if _left <= 0:
|
||||
day_trading_state.remove_position(stock_code)
|
||||
logger.warning(f"[MDD] DT 포지션 전량 축소: {stock_code}")
|
||||
else:
|
||||
pos["quantity"] = _left
|
||||
logger.warning(f"[MDD] DT 포지션 잔량 갱신: {stock_code} → {_left}주")
|
||||
except Exception as e:
|
||||
logger.warning(f"[MDD] DT 인메모리 반영 실패 ({stock_code}): {e}")
|
||||
|
||||
Reference in New Issue
Block a user