[2026-05-18] KIS API rate limit 수정 — 모의투자 1건/초, 진입체크 불필요 호출 제거
This commit is contained in:
@@ -49,9 +49,10 @@ class KISClient:
|
|||||||
self._access_token : Optional[str] = None
|
self._access_token : Optional[str] = None
|
||||||
self._token_expires_at: Optional[datetime] = None
|
self._token_expires_at: Optional[datetime] = None
|
||||||
|
|
||||||
# rate limit: 초당 20건
|
# rate limit: 모의투자 1건/초, 실거래 5건/초
|
||||||
self._semaphore = asyncio.Semaphore(20)
|
self._rate_limit = 1 if self.is_mock else 5
|
||||||
self._req_times : list = []
|
self._semaphore = asyncio.Semaphore(1)
|
||||||
|
self._req_times : list = []
|
||||||
|
|
||||||
mode = "모의투자" if self.is_mock else "실거래"
|
mode = "모의투자" if self.is_mock else "실거래"
|
||||||
logger.info(f"KISClient 초기화 완료 [{mode}] 계좌: {self.account_no}")
|
logger.info(f"KISClient 초기화 완료 [{mode}] 계좌: {self.account_no}")
|
||||||
@@ -127,10 +128,9 @@ class KISClient:
|
|||||||
}
|
}
|
||||||
|
|
||||||
async with self._semaphore:
|
async with self._semaphore:
|
||||||
# 초당 20건 rate limit
|
|
||||||
now = time.monotonic()
|
now = time.monotonic()
|
||||||
self._req_times = [t for t in self._req_times if now - t < 1.0]
|
self._req_times = [t for t in self._req_times if now - t < 1.0]
|
||||||
if len(self._req_times) >= 20:
|
if len(self._req_times) >= self._rate_limit:
|
||||||
wait = 1.0 - (now - self._req_times[0])
|
wait = 1.0 - (now - self._req_times[0])
|
||||||
if wait > 0:
|
if wait > 0:
|
||||||
await asyncio.sleep(wait)
|
await asyncio.sleep(wait)
|
||||||
|
|||||||
+8
-13
@@ -239,13 +239,15 @@ class StockBot:
|
|||||||
continue
|
continue
|
||||||
if len(self.positions) >= MAX_POSITIONS:
|
if len(self.positions) >= MAX_POSITIONS:
|
||||||
break
|
break
|
||||||
|
# 목표가 미계산 종목 스킵 (불필요한 API 호출 방지)
|
||||||
|
if self.strategy.get_target(ticker) <= 0:
|
||||||
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
price_info = await self.kis.get_price(ticker)
|
price_info = await self.kis.get_price(ticker) # rate limiter가 자동 throttle
|
||||||
current = price_info["current"]
|
current = price_info["current"]
|
||||||
name = price_info.get("name", ticker)
|
name = price_info.get("name", ticker)
|
||||||
|
|
||||||
# 전략 신호 체크
|
|
||||||
signal = self.strategy.check_entry(
|
signal = self.strategy.check_entry(
|
||||||
ticker=ticker,
|
ticker=ticker,
|
||||||
name=name,
|
name=name,
|
||||||
@@ -255,15 +257,11 @@ class StockBot:
|
|||||||
if not signal["signal"]:
|
if not signal["signal"]:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 포지션 사이즈 계산
|
balance = await self.kis.get_balance()
|
||||||
balance = await self.kis.get_balance()
|
cash = balance["cash"]
|
||||||
cash = balance["cash"]
|
invest = self.risk.get_pos_size(cash, signal.get("multiplier", 1.0))
|
||||||
invest = self.risk.get_pos_size(
|
qty = max(1, int(invest // current))
|
||||||
cash, signal.get("multiplier", 1.0)
|
|
||||||
)
|
|
||||||
qty = max(1, int(invest // current))
|
|
||||||
|
|
||||||
# 매수 실행
|
|
||||||
result = await self.executor.buy(
|
result = await self.executor.buy(
|
||||||
ticker=ticker, name=name,
|
ticker=ticker, name=name,
|
||||||
qty=qty, reason=signal["reason"],
|
qty=qty, reason=signal["reason"],
|
||||||
@@ -273,7 +271,6 @@ class StockBot:
|
|||||||
if result["success"]:
|
if result["success"]:
|
||||||
entry_price = result["price"] or current
|
entry_price = result["price"] or current
|
||||||
sl_price = entry_price * (1 - self.risk.get_sl_pct())
|
sl_price = entry_price * (1 - self.risk.get_sl_pct())
|
||||||
tp1_price = entry_price * (1 + 0.02)
|
|
||||||
|
|
||||||
self.positions[ticker] = {
|
self.positions[ticker] = {
|
||||||
"name" : name,
|
"name" : name,
|
||||||
@@ -293,8 +290,6 @@ class StockBot:
|
|||||||
boosted=signal.get("boosted", False),
|
boosted=signal.get("boosted", False),
|
||||||
)
|
)
|
||||||
|
|
||||||
await asyncio.sleep(1.1)
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"진입 체크 오류 {ticker}: {e}")
|
logger.error(f"진입 체크 오류 {ticker}: {e}")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user