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:
@@ -172,10 +172,16 @@ async def get_trade_stats(
|
||||
executed_trades = len([t for t in trades if t.status in ("EXECUTED", "COMPLETED")])
|
||||
failed_trades = len([t for t in trades if t.status == "FAILED"])
|
||||
|
||||
# 총 거래 금액 계산
|
||||
# 총 거래 금액 계산 — [2026-10-08] total_amount(체결 확인 시 체결가×체결수량으로 갱신) 우선.
|
||||
# executed_price 는 체결 확인 전 NULL 이므로 곱셈만 쓰면 미확인 건이 0 으로 빠진다.
|
||||
def _amount(t) -> float:
|
||||
if t.total_amount is not None:
|
||||
return float(t.total_amount)
|
||||
price = t.executed_price if t.executed_price is not None else t.order_price
|
||||
return float(price or 0) * (t.executed_quantity or 0)
|
||||
|
||||
total_amount = sum(
|
||||
float(t.executed_price or 0) * (t.executed_quantity or 0)
|
||||
for t in trades if t.status in ("EXECUTED", "COMPLETED")
|
||||
_amount(t) for t in trades if t.status in ("EXECUTED", "COMPLETED")
|
||||
)
|
||||
|
||||
return {
|
||||
@@ -192,7 +198,11 @@ async def get_trade_stats(
|
||||
|
||||
@router.delete("/pending")
|
||||
async def delete_pending_trades(db: AsyncSession = Depends(get_db)):
|
||||
"""PENDING과 FAILED 상태의 매수 시도 내역 정리 — **전일 이전 행만**.
|
||||
"""FAILED 상태의 주문 시도 내역 정리 — **전일 이전 행만, PENDING 은 보존**.
|
||||
|
||||
[R7] PENDING(지정가 미체결·결과 불명 BUY)은 원장 BUY 합계와 강등 방어의 근거라 지우지 않는다.
|
||||
쓰기는 FAILED 전용 정리(기존 동작 유지). db_worker 에 delete task 가 없어 직접 삭제를 유지한다
|
||||
(관리자 전용 등록은 security.ADMIN_ONLY_ROUTES 소관 — 별도 보고).
|
||||
|
||||
[L 2026-10-02] 기존에는 기간 무제한이라 장중 호출 시 당일 PENDING/FAILED 행까지
|
||||
지웠다. 당일 행은 `already_ordered_today`·일일 스윙 매수 한도(`_count_swing_buys_today_v2`
|
||||
@@ -204,7 +214,9 @@ async def delete_pending_trades(db: AsyncSession = Depends(get_db)):
|
||||
cutoff = get_daily_cutoff_utc()
|
||||
result = await db.execute(
|
||||
select(TradeHistory).where(
|
||||
TradeHistory.status.in_(['PENDING', 'FAILED']),
|
||||
# [R7] PENDING 은 보존한다 — 체결 확인 실패 BUY 가 PENDING 으로 남아
|
||||
# portfolio 원장 BUY 합계·유입잔고 강등(swing→external) 방어의 근거가 된다(main.py 기동 정리와 동일 정책).
|
||||
TradeHistory.status == 'FAILED',
|
||||
TradeHistory.created_at < cutoff,
|
||||
)
|
||||
)
|
||||
@@ -215,9 +227,9 @@ async def delete_pending_trades(db: AsyncSession = Depends(get_db)):
|
||||
await db.delete(trade)
|
||||
await db.commit()
|
||||
|
||||
logger.info(f"✅ 정리 완료: {count}개의 PENDING/FAILED 매수 시도 삭제 (기준 {cutoff} 이전, 당일 행 보존)")
|
||||
logger.info(f"✅ 정리 완료: {count}개의 FAILED 시도 삭제(PENDING 보존) (기준 {cutoff} 이전, 당일 행 보존)")
|
||||
|
||||
return {
|
||||
"deleted_count": count,
|
||||
"message": f"Cleaned up {count} pending/failed trades before {cutoff.isoformat()} (today's rows kept)",
|
||||
"message": f"Cleaned up {count} failed trades (PENDING kept) before {cutoff.isoformat()} (today's rows kept)",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user