first commit
This commit is contained in:
125
app/services/market_data.py
Normal file
125
app/services/market_data.py
Normal file
@@ -0,0 +1,125 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import httpx
|
||||
|
||||
from app.core.auth import token_manager
|
||||
from app.core.config import get_base_url, settings
|
||||
from app.core.logger import setup_logger
|
||||
from app.core.rate_limiter import RateLimiter
|
||||
|
||||
logger = setup_logger("market_data")
|
||||
|
||||
_PRICE_URL = "/uapi/domestic-stock/v1/quotations/inquire-price"
|
||||
_CHART_URL = "/uapi/domestic-stock/v1/quotations/inquire-daily-itemchartprice"
|
||||
_ORDERBOOK_URL = "/uapi/domestic-stock/v1/quotations/inquire-asking-price"
|
||||
|
||||
_FID_INPUT_ISCD = "FID_INPUT_ISCD"
|
||||
_FID_COND_MRKT_DIV_CODE = "FID_COND_MRKT_DIV_CODE"
|
||||
|
||||
|
||||
class MarketDataService:
|
||||
def __init__(self) -> None:
|
||||
self._client = httpx.AsyncClient(timeout=10.0)
|
||||
self._rate_limiter = RateLimiter(settings.rate_limit.requests_per_second)
|
||||
|
||||
async def get_current_price(self, stock_code: str) -> dict:
|
||||
await self._rate_limiter.acquire()
|
||||
token = await token_manager.get_access_token()
|
||||
headers = token_manager.get_auth_headers("FHKST01010100")
|
||||
params = {_FID_COND_MRKT_DIV_CODE: "J", _FID_INPUT_ISCD: stock_code}
|
||||
url = f"{get_base_url()}{_PRICE_URL}"
|
||||
|
||||
resp = await self._client.get(url, headers=headers, params=params)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
|
||||
if data.get("rt_cd") != "0":
|
||||
logger.warning("현재가 조회 실패: %s - %s", stock_code, data.get("msg1"))
|
||||
return {}
|
||||
|
||||
output = data.get("output", {})
|
||||
return {
|
||||
"stock_code": stock_code,
|
||||
"current_price": int(output.get("stck_prpr", 0)),
|
||||
"change_price": int(output.get("prdy_vrss", 0)),
|
||||
"change_rate": float(output.get("prdy_ctrt", 0)),
|
||||
"open_price": int(output.get("stck_oprc", 0)),
|
||||
"high_price": int(output.get("stck_hgpr", 0)),
|
||||
"low_price": int(output.get("stck_lwpr", 0)),
|
||||
"volume": int(output.get("acml_vol", 0)),
|
||||
"trade_amount": int(output.get("acml_tr_pbmn", 0)),
|
||||
"stock_name": output.get("hts_kor_isnm", ""),
|
||||
}
|
||||
|
||||
async def get_daily_chart(
|
||||
self, stock_code: str, start_date: str, end_date: str, count: int = 30
|
||||
) -> list[dict]:
|
||||
await self._rate_limiter.acquire()
|
||||
token = await token_manager.get_access_token()
|
||||
headers = token_manager.get_auth_headers("FHKST03010200")
|
||||
params = {
|
||||
_FID_COND_MRKT_DIV_CODE: "J",
|
||||
_FID_INPUT_ISCD: stock_code,
|
||||
"FID_INPUT_DATE_1": start_date,
|
||||
"FID_INPUT_DATE_2": end_date,
|
||||
"FID_PERIOD_DIV_CODE": "D",
|
||||
"FID_ADJ_PRC": "1",
|
||||
}
|
||||
url = f"{get_base_url()}{_CHART_URL}"
|
||||
|
||||
resp = await self._client.get(url, headers=headers, params=params)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
|
||||
if data.get("rt_cd") != "0":
|
||||
logger.warning("차트 조회 실패: %s - %s", stock_code, data.get("msg1"))
|
||||
return []
|
||||
|
||||
result = []
|
||||
for item in data.get("output2", [])[:count]:
|
||||
result.append({
|
||||
"date": item.get("stck_bsop_date", ""),
|
||||
"open": int(item.get("stck_oprc", 0)),
|
||||
"high": int(item.get("stck_hgpr", 0)),
|
||||
"low": int(item.get("stck_lwpr", 0)),
|
||||
"close": int(item.get("stck_clpr", 0)),
|
||||
"volume": int(item.get("acml_vol", 0)),
|
||||
})
|
||||
return result
|
||||
|
||||
async def get_orderbook(self, stock_code: str) -> dict:
|
||||
await self._rate_limiter.acquire()
|
||||
token = await token_manager.get_access_token()
|
||||
headers = token_manager.get_auth_headers("FHKST01010200")
|
||||
params = {_FID_COND_MRKT_DIV_CODE: "J", _FID_INPUT_ISCD: stock_code}
|
||||
url = f"{get_base_url()}{_ORDERBOOK_URL}"
|
||||
|
||||
resp = await self._client.get(url, headers=headers, params=params)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
|
||||
if data.get("rt_cd") != "0":
|
||||
return {}
|
||||
|
||||
output = data.get("output", [{}])[0] if data.get("output") else {}
|
||||
return {
|
||||
"stock_code": stock_code,
|
||||
"bid_prices": [
|
||||
int(output.get(f"phsc_kprc_{i}", 0)) for i in range(1, 6)
|
||||
],
|
||||
"ask_prices": [
|
||||
int(output.get(f"sats_kprc_{i}", 0)) for i in range(1, 6)
|
||||
],
|
||||
"bid_volumes": [
|
||||
int(output.get(f"phsc_vola_{i}", 0)) for i in range(1, 6)
|
||||
],
|
||||
"ask_volumes": [
|
||||
int(output.get(f"sats_ac_vola_{i}", 0)) for i in range(1, 6)
|
||||
],
|
||||
}
|
||||
|
||||
async def close(self) -> None:
|
||||
await self._client.aclose()
|
||||
|
||||
|
||||
market_data_service = MarketDataService()
|
||||
Reference in New Issue
Block a user