배치 실계좌에서 종목조회로 변경

This commit is contained in:
2026-03-03 13:34:31 +09:00
parent 0531952edb
commit d6841a06f7
5 changed files with 163 additions and 76 deletions

View File

@@ -0,0 +1,18 @@
"""공통 로깅 설정 — KST(Asia/Seoul) 타임존 포매터 제공."""
import logging
import datetime
import pytz
KST = pytz.timezone("Asia/Seoul")
class KSTFormatter(logging.Formatter):
"""%(asctime)s 를 KST(UTC+9) 기준으로 출력하는 포매터."""
def formatTime(self, record: logging.LogRecord, datefmt: str | None = None) -> str:
ct = datetime.datetime.fromtimestamp(record.created, tz=KST)
if datefmt:
return ct.strftime(datefmt)
t = ct.strftime(self.default_time_format)
return self.default_msec_format % (t, record.msecs)

View File

@@ -27,22 +27,24 @@ from app.core.market_schedule import is_market_open
# 로깅 설정
import os
import logging.handlers
from app.core.logging_config import KSTFormatter
_log_level = getattr(logging, settings.LOG_LEVEL.upper(), logging.INFO)
_log_format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
_log_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "logs")
os.makedirs(_log_dir, exist_ok=True)
_log_file = os.path.join(_log_dir, "backend.log")
logging.basicConfig(
level=_log_level,
format=_log_format,
handlers=[
logging.StreamHandler(),
logging.handlers.RotatingFileHandler(
_log_file, maxBytes=10 * 1024 * 1024, backupCount=5, encoding="utf-8"
),
]
)
_formatter = KSTFormatter(_log_format)
_handlers: list[logging.Handler] = [
logging.StreamHandler(),
logging.handlers.RotatingFileHandler(
_log_file, maxBytes=10 * 1024 * 1024, backupCount=5, encoding="utf-8"
),
]
for _h in _handlers:
_h.setFormatter(_formatter)
logging.basicConfig(level=_log_level, handlers=_handlers)
logger = logging.getLogger(__name__)

View File

@@ -97,7 +97,41 @@ class KoreaInvestmentAPI:
# 토큰 캐시 파일 경로 — 사용자 홈 디렉토리 하위에 저장 (/tmp는 보안 취약)
TOKEN_CACHE_FILE = Path.home() / ".cache" / "auto-stock" / "kis_token_cache.json"
@classmethod
def for_real_market_data(cls) -> "KoreaInvestmentAPI":
"""실계좌 자격증명으로 시세조회 전용 인스턴스 생성.
모의투자 모드에서도 실계좌 API로 시세를 조회할 때 사용한다.
(모의투자 서버는 KOSDAQ 거래량 순위 등 일부 조회 API를 지원하지 않음)
주문 API는 절대 호출하지 말 것.
"""
if not settings.KIS_REAL_APP_KEY:
raise ValueError("KIS_REAL_APP_KEY가 설정되지 않아 실계좌 시세조회를 사용할 수 없습니다.")
instance = cls.__new__(cls)
instance.base_url = settings.KIS_REAL_URL
instance.app_key = settings.KIS_REAL_APP_KEY
instance.app_secret = settings.KIS_REAL_APP_SECRET
instance.account_number = settings.KIS_REAL_ACCOUNT_NO
instance.access_token = None
instance.token_expires_at = None
instance.last_request_time = None
instance.TOKEN_CACHE_FILE = Path.home() / ".cache" / "auto-stock" / "kis_token_cache_real.json"
instance.min_request_interval = settings.API_MIN_REQUEST_INTERVAL
instance.max_retries = settings.API_MAX_RETRIES
instance.retry_delay = settings.API_RETRY_DELAY
instance.retry_backoff = settings.API_RETRY_BACKOFF
instance.timeout = settings.API_TIMEOUT
instance.semaphore = asyncio.Semaphore(settings.API_MAX_CONCURRENT_REQUESTS)
instance._token_lock = asyncio.Lock()
instance.monitor = APICallMonitor(window_size=settings.API_MONITORING_WINDOW)
logger.info(
"🔧 KIS 실계좌 시세조회 API 초기화: base_url=%s, app_key=%s...",
instance.base_url, instance.app_key[:6]
)
instance._load_token_from_cache()
return instance
def __init__(self):
self.base_url = settings.KOREA_INVESTMENT_API_BASE_URL
self.app_key = settings.KOREA_INVESTMENT_APP_KEY
@@ -106,20 +140,20 @@ class KoreaInvestmentAPI:
self.access_token: Optional[str] = None
self.token_expires_at: Optional[datetime] = None
self.last_request_time: Optional[float] = None
# 설정 기반 쓰로틀링 파라미터
self.min_request_interval = settings.API_MIN_REQUEST_INTERVAL # 기본 0.5초 (초당 2회)
self.max_retries = settings.API_MAX_RETRIES # 기본 3회
self.retry_delay = settings.API_RETRY_DELAY # 기본 1.5초
self.retry_backoff = settings.API_RETRY_BACKOFF # 기본 2.0배
self.timeout = settings.API_TIMEOUT # 기본 30초
# 동시 요청 제한 (세마포어)
self.semaphore = asyncio.Semaphore(settings.API_MAX_CONCURRENT_REQUESTS)
# 토큰 발급 Lock (중복 발급 방지)
self._token_lock = asyncio.Lock()
# 모니터링
self.monitor = APICallMonitor(window_size=settings.API_MONITORING_WINDOW)