fix: DB 타임스탬프 KST 통일 + KIS 토큰 캐시 영속화

시간대 혼재: 모델 기본값은 datetime.utcnow(UTC), 수동 매매 등 일부 경로는 _get_kst_now(KST)로
저장해 같은 테이블에 9시간 차가 섞였다(화면 매매시각 02:14 등). 조회 구간(utcnow - days)도
KST 저장분과 비교돼 어긋났고, 추가매수 쿨다운은 (UTC now - KST created_at)이 음수라 상시
발동했다. app.core.timeutil.now_kst() 도입 후 모델 기본값 27곳과 app/ 전역 utcnow() 호출을
모두 KST 기준으로 통일(잔존 0건, 테스트로 고정).

KIS 토큰 캐시(~/.cache/auto-stock)가 컨테이너에만 있어 배포마다 사라지고 토큰을 새로
발급받다 KIS가 EGW00103(유효하지 않은 AppKey)로 거부 → 502·잔고 0 유발. 볼륨 마운트로
재배포 후에도 재사용.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-23 11:24:14 +09:00
parent e2d4b607cb
commit ec2fa2b423
22 changed files with 177 additions and 69 deletions

View File

@@ -20,6 +20,7 @@ from sqlalchemy import select, and_, func
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.models import TradeHistory, TradeJournal
from app.core.timeutil import now_kst
logger = logging.getLogger(__name__)
@@ -380,7 +381,7 @@ async def analyze_performance(db: AsyncSession, days: int = 30) -> Dict[str, Any
"generated_at": ISO 문자열
}
"""
since = datetime.utcnow() - timedelta(days=days)
since = now_kst() - timedelta(days=days)
stmt = select(TradeHistory).where(
and_(
@@ -408,7 +409,7 @@ async def analyze_performance(db: AsyncSession, days: int = 30) -> Dict[str, Any
"drift_alerts": [],
"period_days": days,
"total_trades": 0,
"generated_at": datetime.utcnow().isoformat(),
"generated_at": now_kst().isoformat(),
}
# ── 전체 통계 ──────────────────────────────────────────────────────────
@@ -526,7 +527,7 @@ async def analyze_performance(db: AsyncSession, days: int = 30) -> Dict[str, Any
"drift_alerts": drift_alerts,
"period_days": days,
"total_trades": len(trades),
"generated_at": datetime.utcnow().isoformat(),
"generated_at": now_kst().isoformat(),
}
@@ -637,7 +638,7 @@ def build_insights(perf: Dict[str, Any]) -> List[Dict[str, str]]:
async def get_uncovered_trades(db: AsyncSession, days: int = 7) -> List[TradeHistory]:
"""일지가 아직 없는 완료 SELL 거래 목록을 반환."""
since = datetime.utcnow() - timedelta(days=days)
since = now_kst() - timedelta(days=days)
# 이미 일지가 있는 trade_id 목록
covered_stmt = select(TradeJournal.trade_id).where(TradeJournal.trade_id.is_not(None))
@@ -753,7 +754,7 @@ async def create_journal_record(
trade_id=trade.id,
stock_code=trade.stock_code,
stock_name=trade.stock_name,
journal_date=trade.executed_at or trade.created_at or datetime.utcnow(),
journal_date=trade.executed_at or trade.created_at or now_kst(),
content=content,
strategy_name=strategy,
mood=mood,