eddmpython commited on
Commit
50938cb
ยท
verified ยท
1 Parent(s): 378721c

fix: HF data instead of OpenDART realtime

Browse files
Files changed (1) hide show
  1. src/dartlab/server/api/dart.py +129 -54
src/dartlab/server/api/dart.py CHANGED
@@ -1,7 +1,7 @@
1
- """DART API ํ”„๋ก์‹œ โ€” ํ‚ค ์—†๋Š” ์‚ฌ์šฉ์ž๋ฅผ ์œ„ํ•œ ์„œ๋ฒ„ ์ธก OpenDART ํ˜ธ์ถœ.
2
 
3
- ์„œ๋ฒ„์— DART_API_KEY๋ฅผ ๋‘๊ณ , ์‚ฌ์šฉ์ž๋Š” ํ‚ค ์—†์ด ์‹ค์‹œ๊ฐ„ ๊ณต์‹œ ์กฐํšŒ.
4
- Rate limit์œผ๋กœ ๋‚จ์šฉ ๋ฐฉ์ง€, crtfc_key ํ•„๋“œ ์ œ๊ฑฐ๋กœ ํ‚ค ๋…ธ์ถœ ๋ฐฉ์ง€.
5
  """
6
 
7
  from __future__ import annotations
@@ -13,72 +13,93 @@ from fastapi import APIRouter, HTTPException, Query
13
  router = APIRouter(prefix="/api/dart", tags=["dart"])
14
  _log = logging.getLogger(__name__)
15
 
16
- _MAX_ROWS = 100
17
 
18
 
19
- def _get_client():
20
- """์„œ๋ฒ„ ์ธก DartClient ์‹ฑ๊ธ€ํ†ค."""
21
- from dartlab.providers.dart.openapi.client import DartClient
22
-
23
- return DartClient()
24
-
25
-
26
- def _sanitize(data: dict | list) -> dict | list:
27
- """์‘๋‹ต์—์„œ crtfc_key ํ•„๋“œ๋ฅผ ์ œ๊ฑฐ."""
28
- if isinstance(data, dict):
29
- return {k: _sanitize(v) for k, v in data.items() if k != "crtfc_key"}
30
- if isinstance(data, list):
31
- return [_sanitize(item) if isinstance(item, (dict, list)) else item for item in data]
32
- return data
33
 
34
 
35
  @router.get("/filings")
36
  def dart_filings(
37
- corp: str | None = Query(None, description="์ข…๋ชฉ์ฝ”๋“œ ๋˜๋Š” corp_code"),
38
- start: str | None = Query(None, description="์‹œ์ž‘์ผ (YYYYMMDD)"),
39
- end: str | None = Query(None, description="์ข…๋ฃŒ์ผ (YYYYMMDD)"),
40
- type: str | None = Query(None, description="๊ณต์‹œ์œ ํ˜• ํ•„ํ„ฐ"),
41
  ):
42
- """๊ณต์‹œ ๋ชฉ๋ก ์กฐํšŒ."""
43
  try:
44
- from dartlab.providers.dart.openapi.dart import Dart
45
-
46
- dart = Dart()
47
- df = dart.filings(corp=corp, start=start or "20250101", end=end, type=type)
48
- rows = df.head(_MAX_ROWS).to_dicts()
49
- return _sanitize({"count": len(rows), "total": df.height, "rows": rows})
50
- except (ValueError, KeyError, RuntimeError) as exc:
 
 
51
  raise HTTPException(status_code=400, detail=str(exc)) from exc
52
 
53
 
54
  @router.get("/company/{corp}")
55
  def dart_company_info(corp: str):
56
- """๊ธฐ์—… ๊ธฐ๋ณธ ์ •๋ณด."""
57
  try:
58
- from dartlab.providers.dart.openapi.dart import Dart
59
 
60
- dart = Dart()
61
- info = dart.company(corp)
62
- return _sanitize(info)
63
- except (ValueError, KeyError, RuntimeError) as exc:
64
- raise HTTPException(status_code=400, detail=str(exc)) from exc
 
 
 
 
65
 
66
 
67
  @router.get("/finance/{corp}")
68
  def dart_finance(
69
  corp: str,
70
- year: int | None = Query(None, description="์‚ฌ์—…์—ฐ๋„"),
71
- quarter: int | None = Query(None, description="๋ถ„๊ธฐ (0=์—ฐ๊ฐ„, 1~3=๋ถ„๊ธฐ)"),
72
  ):
73
- """์žฌ๋ฌด์ œํ‘œ ์กฐํšŒ."""
74
  try:
75
- from dartlab.providers.dart.openapi.dart import Dart
 
 
 
 
 
 
 
 
 
76
 
77
- dart = Dart()
78
- df = dart.finstate(corp, start=year, q=quarter)
79
- rows = df.head(_MAX_ROWS).to_dicts()
80
- return _sanitize({"count": len(rows), "total": df.height, "rows": rows})
81
- except (ValueError, KeyError, RuntimeError) as exc:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  raise HTTPException(status_code=400, detail=str(exc)) from exc
83
 
84
 
@@ -86,15 +107,69 @@ def dart_finance(
86
  def dart_report(
87
  corp: str,
88
  category: str,
89
- year: int | None = Query(None, description="์‚ฌ์—…์—ฐ๋„"),
90
  ):
91
- """๋ณด๊ณ ์„œ API (๋ฐฐ๋‹น, ์ง์›, ์ž„์› ๋“ฑ 56๊ฐœ ์นดํ…Œ๊ณ ๋ฆฌ)."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  try:
93
- from dartlab.providers.dart.openapi.dart import Dart
 
 
 
 
 
 
 
 
94
 
95
- dart = Dart()
96
- df = dart.report(corp, category, start=year)
97
- rows = df.head(_MAX_ROWS).to_dicts()
98
- return _sanitize({"count": len(rows), "total": df.height, "rows": rows})
99
- except (ValueError, KeyError, RuntimeError) as exc:
 
 
 
 
 
 
 
 
 
 
 
100
  raise HTTPException(status_code=400, detail=str(exc)) from exc
 
1
+ """DART ๋ฐ์ดํ„ฐ API โ€” HuggingFace ์‚ฌ์ „๋นŒ๋“œ ๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜ ์ฆ‰์‹œ ์‘๋‹ต.
2
 
3
+ ์žฌ๋ฌด์ œํ‘œ, ๋ณด๊ณ ์„œ, ๊ณต์‹œ ๋ชฉ๋ก ๋“ฑ ๋Œ€๋ถ€๋ถ„์˜ ๋ฐ์ดํ„ฐ๋Š” ์ด๋ฏธ HuggingFace์— ์žˆ๋‹ค.
4
+ OpenDART API๋ฅผ ์‹ค์‹œ๊ฐ„ ํ˜ธ์ถœํ•˜์ง€ ์•Š๊ณ , dartlab Company/listing์œผ๋กœ HF ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜.
5
  """
6
 
7
  from __future__ import annotations
 
13
  router = APIRouter(prefix="/api/dart", tags=["dart"])
14
  _log = logging.getLogger(__name__)
15
 
16
+ _MAX_ROWS = 200
17
 
18
 
19
+ def _df_to_response(df, max_rows: int = _MAX_ROWS) -> dict:
20
+ """DataFrame โ†’ JSON ์‘๋‹ต."""
21
+ if df is None or (hasattr(df, "is_empty") and df.is_empty()):
22
+ return {"count": 0, "total": 0, "rows": []}
23
+ total = df.height if hasattr(df, "height") else len(df)
24
+ rows = df.head(max_rows).to_dicts() if hasattr(df, "to_dicts") else []
25
+ return {"count": len(rows), "total": total, "rows": rows}
 
 
 
 
 
 
 
26
 
27
 
28
  @router.get("/filings")
29
  def dart_filings(
30
+ corp: str | None = Query(None, description="์ข…๋ชฉ์ฝ”๋“œ"),
31
+ topK: int = Query(20, description="์ตœ๋Œ€ ๊ฑด์ˆ˜"),
 
 
32
  ):
33
+ """๊ณต์‹œ ๋ชฉ๋ก โ€” HF ๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜."""
34
  try:
35
+ import dartlab
36
+
37
+ if corp:
38
+ c = dartlab.Company(corp)
39
+ df = c.filings(topK=topK)
40
+ else:
41
+ df = dartlab.listing("filings")
42
+ return _df_to_response(df, max_rows=topK)
43
+ except (ValueError, KeyError, RuntimeError, FileNotFoundError) as exc:
44
  raise HTTPException(status_code=400, detail=str(exc)) from exc
45
 
46
 
47
  @router.get("/company/{corp}")
48
  def dart_company_info(corp: str):
49
+ """๊ธฐ์—… ๊ธฐ๋ณธ ์ •๋ณด โ€” HF ๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜."""
50
  try:
51
+ import dartlab
52
 
53
+ c = dartlab.Company(corp)
54
+ return {
55
+ "corpName": c.corpName,
56
+ "stockCode": c.stockCode,
57
+ "market": getattr(c, "market", None),
58
+ "currency": getattr(c, "currency", None),
59
+ }
60
+ except (ValueError, KeyError, RuntimeError, FileNotFoundError) as exc:
61
+ raise HTTPException(status_code=404, detail=str(exc)) from exc
62
 
63
 
64
  @router.get("/finance/{corp}")
65
  def dart_finance(
66
  corp: str,
67
+ statement: str = Query("IS", description="IS/BS/CF/CIS/SCE"),
68
+ freq: str = Query("Q", description="Q(๋ถ„๊ธฐ)/Y(์—ฐ๊ฐ„)"),
69
  ):
70
+ """์žฌ๋ฌด์ œํ‘œ โ€” HF parquet ์ฆ‰์‹œ ๋ฐ˜ํ™˜."""
71
  try:
72
+ import dartlab
73
+
74
+ c = dartlab.Company(corp)
75
+ if freq == "Y":
76
+ df = c.show(statement, freq="Y")
77
+ else:
78
+ df = c.show(statement)
79
+ return _df_to_response(df)
80
+ except (ValueError, KeyError, RuntimeError, FileNotFoundError) as exc:
81
+ raise HTTPException(status_code=400, detail=str(exc)) from exc
82
 
83
+
84
+ @router.get("/show/{corp}/{topic}")
85
+ def dart_show(
86
+ corp: str,
87
+ topic: str,
88
+ period: str | None = Query(None, description="๊ธฐ๊ฐ„ ํ•„ํ„ฐ"),
89
+ ):
90
+ """๊ณต์‹œ ํ† ํ”ฝ ๋ฐ์ดํ„ฐ โ€” HF parquet ์ฆ‰์‹œ ๋ฐ˜ํ™˜."""
91
+ try:
92
+ import dartlab
93
+
94
+ c = dartlab.Company(corp)
95
+ if period:
96
+ result = c.show(topic, period=period)
97
+ else:
98
+ result = c.show(topic)
99
+ if hasattr(result, "to_dicts"):
100
+ return _df_to_response(result)
101
+ return {"data": str(result)[:5000]}
102
+ except (ValueError, KeyError, RuntimeError, FileNotFoundError) as exc:
103
  raise HTTPException(status_code=400, detail=str(exc)) from exc
104
 
105
 
 
107
  def dart_report(
108
  corp: str,
109
  category: str,
 
110
  ):
111
+ """๋ณด๊ณ ์„œ (๋ฐฐ๋‹น, ์ง์›, ์ž„์› ๋“ฑ) โ€” HF parquet ์ฆ‰์‹œ ๋ฐ˜ํ™˜."""
112
+ try:
113
+ import dartlab
114
+
115
+ c = dartlab.Company(corp)
116
+ df = c.show(category)
117
+ if hasattr(df, "to_dicts"):
118
+ return _df_to_response(df)
119
+ return {"data": str(df)[:5000]}
120
+ except (ValueError, KeyError, RuntimeError, FileNotFoundError) as exc:
121
+ raise HTTPException(status_code=400, detail=str(exc)) from exc
122
+
123
+
124
+ @router.get("/scan/{axis}")
125
+ def dart_scan(
126
+ axis: str,
127
+ target: str | None = Query(None, description="์ถ•๋ณ„ ๋Œ€์ƒ (account: ๊ณ„์ •๋ช…, ratio: ๋น„์œจ๋ช…)"),
128
+ ):
129
+ """์ „์ข…๋ชฉ ํšก๋‹จ๋ถ„์„ โ€” ํ”„๋ฆฌ๋นŒ๋“œ parquet ์ฆ‰์‹œ ๋ฐ˜ํ™˜."""
130
+ try:
131
+ import dartlab
132
+
133
+ if target:
134
+ df = dartlab.scan(axis, target)
135
+ else:
136
+ df = dartlab.scan(axis)
137
+ return _df_to_response(df)
138
+ except (ValueError, KeyError, RuntimeError, FileNotFoundError) as exc:
139
+ raise HTTPException(status_code=400, detail=str(exc)) from exc
140
+
141
+
142
+ @router.get("/search")
143
+ def dart_search(
144
+ q: str = Query(..., description="๊ฒ€์ƒ‰์–ด"),
145
+ corp: str | None = Query(None, description="์ข…๋ชฉ์ฝ”๋“œ ํ•„ํ„ฐ"),
146
+ ):
147
+ """๊ณต์‹œ ์›๋ฌธ ๊ฒ€์ƒ‰ โ€” stemIndex ์ฆ‰์‹œ ๋ฐ˜ํ™˜."""
148
  try:
149
+ import dartlab
150
+
151
+ if corp:
152
+ df = dartlab.search(q, corp=corp)
153
+ else:
154
+ df = dartlab.search(q)
155
+ return _df_to_response(df)
156
+ except (ValueError, KeyError, RuntimeError, FileNotFoundError) as exc:
157
+ raise HTTPException(status_code=400, detail=str(exc)) from exc
158
 
159
+
160
+ @router.get("/listing")
161
+ def dart_listing(
162
+ kind: str = Query("companies", description="companies/filings/topics"),
163
+ corp: str | None = Query(None, description="filings ์‹œ ์ข…๋ชฉ์ฝ”๋“œ"),
164
+ ):
165
+ """์ƒ์žฅ ์ข…๋ชฉ/๊ณต์‹œ ๋ชฉ๋ก."""
166
+ try:
167
+ import dartlab
168
+
169
+ if kind == "filings" and corp:
170
+ df = dartlab.listing("filings", corp=corp)
171
+ else:
172
+ df = dartlab.listing(kind)
173
+ return _df_to_response(df)
174
+ except (ValueError, KeyError, RuntimeError, FileNotFoundError) as exc:
175
  raise HTTPException(status_code=400, detail=str(exc)) from exc