날짜 수정

This commit is contained in:
2026-02-23 08:37:24 +09:00
parent 1814f5e55e
commit d2829a47a7
7 changed files with 163 additions and 32 deletions

View File

@@ -181,34 +181,35 @@ class KoreaInvestmentAPI:
logger.info("🔑 새로운 토큰 발급 요청...")
self._last_token_request_time = datetime.now()
async with httpx.AsyncClient() as client:
# HTTP 요청도 Lock 안에서 실행 — 중복 발급 방지
try:
response = await client.post(
f"{self.base_url}/oauth2/tokenP",
json={
"grant_type": "client_credentials",
"appkey": self.app_key,
"appsecret": self.app_secret
},
timeout=30.0
)
async with httpx.AsyncClient() as client:
response = await client.post(
f"{self.base_url}/oauth2/tokenP",
json={
"grant_type": "client_credentials",
"appkey": self.app_key,
"appsecret": self.app_secret
},
timeout=30.0
)
if response.status_code == 200:
data = response.json()
self.access_token = data["access_token"]
# 토큰 만료 시간 설정 (일반적으로 24시간)
self.token_expires_at = datetime.now() + timedelta(hours=23)
# 파일에 저장 (비동기)
# 파일에 저장 (비동기) — Lock 안에서 호출하되 내부는 to_thread이므로 OK
await self._save_token_to_cache()
logger.info(f"✅ 새 토큰 발급 완료 (만료: {self.token_expires_at.strftime('%Y-%m-%d %H:%M')})")
return self.access_token
else:
error_msg = response.text
logger.error(f"❌ 토큰 발급 실패: {response.status_code} - {error_msg}")
# 토큰 발급 실패 시에도 기존 토큰이 있다면 계속 사용 시도
if self.access_token:
logger.warning(f"⚠️ 토큰 발급 실패했지만 기존 토큰 재사용 시도")

View File

@@ -71,7 +71,7 @@ class ConsecutiveLossTracker:
else:
record.consecutive_losses = entry["consecutive_losses"]
record.ban_until = entry["ban_until"]
record.updated_at = datetime.utcnow()
record.updated_at = datetime.now()
await db.commit()
logger.debug(f"loss_tracker DB 저장 완료: {stock_code}")

View File

@@ -7,7 +7,7 @@ MDD(Maximum Drawdown) 관리 - CLAUDE.md 섹션 6.3
"""
import asyncio
import logging
from typing import Dict
from typing import Dict, Optional
logger = logging.getLogger(__name__)
@@ -18,7 +18,7 @@ class MDDManager:
def __init__(self):
self.peak_balance: float = 0.0
self.is_paused: bool = False
self.reduction_applied: str = None # None, "10pct", "20pct"
self.reduction_applied: Optional[str] = None # None, "10pct", "20pct"
self._lock = asyncio.Lock() # 스케줄러-sync 동시 접근 방지
async def update(self, current_balance: float) -> Dict:

View File

@@ -56,6 +56,9 @@ class StockLSTM:
self.dropout = dropout
self.model = None
self.device = None
# 정규화 파라미터 — train_model() 또는 load() 전까지 None
self.seq_mean: Optional[np.ndarray] = None
self.seq_std: Optional[np.ndarray] = None
self._build()
def _build(self):
@@ -148,7 +151,7 @@ class StockLSTM:
def predict_proba(self, X_seq: np.ndarray) -> np.ndarray:
"""예측 확률 반환 (N, 3)"""
torch, nn = _get_torch()
if torch is None or self.model is None:
if torch is None or self.model is None or self.seq_mean is None or self.seq_std is None:
return np.full((len(X_seq), 3), 1.0 / 3)
self.model.eval()

View File

@@ -12,6 +12,7 @@
from datetime import datetime, date
from typing import Optional
import logging
import pytz
from sqlalchemy import select, update
from sqlalchemy.ext.asyncio import AsyncSession
@@ -53,7 +54,7 @@ async def initialize_watchlist(
Returns: 추가된 종목 수
"""
scanner_tickers = scanner_tickers or set()
now = datetime.now()
now = datetime.now(pytz.timezone("Asia/Seoul")).replace(tzinfo=None)
# 1. 기존 활성 종목 전부 비활성화
await session.execute(