날짜 수정
This commit is contained in:
@@ -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"⚠️ 토큰 발급 실패했지만 기존 토큰 재사용 시도")
|
||||
|
||||
@@ -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}")
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user