반복적인 IPC 오류 해결, 봇 오류 해결, 인증 오류 해결, 서버 자원 할당 오류 해결, 코드 리팩토링
This commit is contained in:
@@ -484,31 +484,174 @@ class KISClient:
|
||||
self.ensure_token()
|
||||
url = f"{self.base_url}/uapi/domestic-stock/v1/quotations/inquire-investor"
|
||||
headers = self._get_headers(tr_id="FHKST01010900")
|
||||
|
||||
|
||||
params = {
|
||||
"FID_COND_MRKT_DIV_CODE": "J",
|
||||
"FID_INPUT_ISCD": ticker
|
||||
}
|
||||
|
||||
|
||||
try:
|
||||
res = requests.get(url, headers=headers, params=params)
|
||||
res.raise_for_status()
|
||||
data = res.json()
|
||||
if data['rt_cd'] != '0':
|
||||
return None
|
||||
|
||||
# output 리스트: [ {stck_bsop_date: 날짜, frgn_ntby_qty: 외인순매수, orgn_ntby_qty: 기관순매수, ...}, ... ]
|
||||
|
||||
trends = []
|
||||
for item in data['output'][:5]: # 최근 5일치만
|
||||
for item in data['output'][:5]:
|
||||
trends.append({
|
||||
"date": item['stck_bsop_date'],
|
||||
"foreigner": self._safe_int(item.get('frgn_ntby_qty')), # 외인 순매수량
|
||||
"institutional": self._safe_int(item.get('orgn_ntby_qty')), # 기관 순매수량
|
||||
"price_change": float(item['prdy_vrss']) # 전일대비 등락금액
|
||||
"foreigner": self._safe_int(item.get('frgn_ntby_qty')),
|
||||
"institutional": self._safe_int(item.get('orgn_ntby_qty')),
|
||||
"price_change": float(item['prdy_vrss'])
|
||||
})
|
||||
|
||||
# 최근일이 0번 인덱스임
|
||||
|
||||
return trends
|
||||
except Exception as e:
|
||||
print(f"❌ 투자자 동향 조회 실패({ticker}): {e}")
|
||||
print(f"[KIS] 투자자 동향 조회 실패({ticker}): {e}")
|
||||
return None
|
||||
|
||||
|
||||
class KISAsyncClient:
|
||||
"""
|
||||
비동기 KIS API 클라이언트
|
||||
- aiohttp 기반 HTTP 호출
|
||||
- 동기 KISClient의 토큰/설정을 공유
|
||||
- 다중 종목 병렬 수집용
|
||||
"""
|
||||
def __init__(self, sync_client):
|
||||
self.sync = sync_client
|
||||
self.min_interval = 0.5 # 초당 2회 제한
|
||||
|
||||
async def _async_get(self, session, url, headers, params):
|
||||
"""비동기 GET 요청"""
|
||||
try:
|
||||
async with session.get(url, headers=headers, params=params) as resp:
|
||||
return await resp.json()
|
||||
except Exception as e:
|
||||
print(f"[KIS Async] Request failed: {e}")
|
||||
return None
|
||||
|
||||
async def get_daily_price_async(self, ticker):
|
||||
"""비동기 일별 시세 조회"""
|
||||
import aiohttp
|
||||
import asyncio
|
||||
|
||||
self.sync.ensure_token()
|
||||
url = f"{self.sync.base_url}/uapi/domestic-stock/v1/quotations/inquire-daily-price"
|
||||
headers = self.sync._get_headers(tr_id="FHKST01010400")
|
||||
params = {
|
||||
"FID_COND_MRKT_DIV_CODE": "J",
|
||||
"FID_INPUT_ISCD": ticker,
|
||||
"FID_PERIOD_DIV_CODE": "D",
|
||||
"FID_ORG_ADJ_PRC": "1"
|
||||
}
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
data = await self._async_get(session, url, headers, params)
|
||||
if data and data.get('rt_cd') == '0':
|
||||
prices = [int(item['stck_clpr']) for item in data['output']]
|
||||
prices.reverse()
|
||||
return prices
|
||||
return []
|
||||
|
||||
async def get_investor_trend_async(self, ticker):
|
||||
"""비동기 투자자 동향 조회"""
|
||||
import aiohttp
|
||||
|
||||
self.sync.ensure_token()
|
||||
url = f"{self.sync.base_url}/uapi/domestic-stock/v1/quotations/inquire-investor"
|
||||
headers = self.sync._get_headers(tr_id="FHKST01010900")
|
||||
params = {
|
||||
"FID_COND_MRKT_DIV_CODE": "J",
|
||||
"FID_INPUT_ISCD": ticker
|
||||
}
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
data = await self._async_get(session, url, headers, params)
|
||||
if data and data.get('rt_cd') == '0':
|
||||
trends = []
|
||||
for item in data['output'][:5]:
|
||||
trends.append({
|
||||
"date": item['stck_bsop_date'],
|
||||
"foreigner": self.sync._safe_int(item.get('frgn_ntby_qty')),
|
||||
"institutional": self.sync._safe_int(item.get('orgn_ntby_qty')),
|
||||
"price_change": float(item['prdy_vrss'])
|
||||
})
|
||||
return trends
|
||||
return None
|
||||
|
||||
async def get_daily_prices_batch(self, tickers):
|
||||
"""여러 종목의 일별 시세를 병렬로 조회"""
|
||||
import aiohttp
|
||||
import asyncio
|
||||
|
||||
self.sync.ensure_token()
|
||||
results = {}
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
tasks = []
|
||||
for i, ticker in enumerate(tickers):
|
||||
# rate limit: 0.5초 간격으로 요청 생성
|
||||
if i > 0:
|
||||
await asyncio.sleep(self.min_interval)
|
||||
|
||||
url = f"{self.sync.base_url}/uapi/domestic-stock/v1/quotations/inquire-daily-price"
|
||||
headers = self.sync._get_headers(tr_id="FHKST01010400")
|
||||
params = {
|
||||
"FID_COND_MRKT_DIV_CODE": "J",
|
||||
"FID_INPUT_ISCD": ticker,
|
||||
"FID_PERIOD_DIV_CODE": "D",
|
||||
"FID_ORG_ADJ_PRC": "1"
|
||||
}
|
||||
tasks.append((ticker, self._async_get(session, url, headers, params)))
|
||||
|
||||
for ticker, task in tasks:
|
||||
data = await task
|
||||
if data and data.get('rt_cd') == '0':
|
||||
prices = [int(item['stck_clpr']) for item in data['output']]
|
||||
prices.reverse()
|
||||
results[ticker] = prices
|
||||
else:
|
||||
results[ticker] = []
|
||||
|
||||
return results
|
||||
|
||||
async def get_investor_trends_batch(self, tickers):
|
||||
"""여러 종목의 투자자 동향을 병렬로 조회"""
|
||||
import aiohttp
|
||||
import asyncio
|
||||
|
||||
self.sync.ensure_token()
|
||||
results = {}
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
tasks = []
|
||||
for i, ticker in enumerate(tickers):
|
||||
if i > 0:
|
||||
await asyncio.sleep(self.min_interval)
|
||||
|
||||
url = f"{self.sync.base_url}/uapi/domestic-stock/v1/quotations/inquire-investor"
|
||||
headers = self.sync._get_headers(tr_id="FHKST01010900")
|
||||
params = {
|
||||
"FID_COND_MRKT_DIV_CODE": "J",
|
||||
"FID_INPUT_ISCD": ticker
|
||||
}
|
||||
tasks.append((ticker, self._async_get(session, url, headers, params)))
|
||||
|
||||
for ticker, task in tasks:
|
||||
data = await task
|
||||
if data and data.get('rt_cd') == '0':
|
||||
trends = []
|
||||
for item in data['output'][:5]:
|
||||
trends.append({
|
||||
"date": item['stck_bsop_date'],
|
||||
"foreigner": self.sync._safe_int(item.get('frgn_ntby_qty')),
|
||||
"institutional": self.sync._safe_int(item.get('orgn_ntby_qty')),
|
||||
"price_change": float(item['prdy_vrss'])
|
||||
})
|
||||
results[ticker] = trends
|
||||
else:
|
||||
results[ticker] = None
|
||||
|
||||
return results
|
||||
|
||||
Reference in New Issue
Block a user