datagokr-reader 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- datagokr_reader-1.0.0/PKG-INFO +25 -0
- datagokr_reader-1.0.0/README.md +12 -0
- datagokr_reader-1.0.0/pyproject.toml +36 -0
- datagokr_reader-1.0.0/setup.cfg +4 -0
- datagokr_reader-1.0.0/src/datagokr_reader/__init__.py +27 -0
- datagokr_reader-1.0.0/src/datagokr_reader/base_reader.py +139 -0
- datagokr_reader-1.0.0/src/datagokr_reader/cli.py +31 -0
- datagokr_reader-1.0.0/src/datagokr_reader/getBondBasiInfo.py +107 -0
- datagokr_reader-1.0.0/src/datagokr_reader/getBondWithOptiCallRede.py +111 -0
- datagokr_reader-1.0.0/src/datagokr_reader/getEarlExerOpti.py +100 -0
- datagokr_reader-1.0.0/src/datagokr_reader/getIssuIssuItemStat.py +75 -0
- datagokr_reader-1.0.0/src/datagokr_reader/getOptiExer.py +89 -0
- datagokr_reader-1.0.0/src/datagokr_reader/getOptiExerPricAdju.py +101 -0
- datagokr_reader-1.0.0/src/datagokr_reader/workflow.py +61 -0
- datagokr_reader-1.0.0/src/datagokr_reader.egg-info/PKG-INFO +25 -0
- datagokr_reader-1.0.0/src/datagokr_reader.egg-info/SOURCES.txt +18 -0
- datagokr_reader-1.0.0/src/datagokr_reader.egg-info/dependency_links.txt +1 -0
- datagokr_reader-1.0.0/src/datagokr_reader.egg-info/entry_points.txt +2 -0
- datagokr_reader-1.0.0/src/datagokr_reader.egg-info/requires.txt +4 -0
- datagokr_reader-1.0.0/src/datagokr_reader.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: datagokr_reader
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: data.go.kr의 채권 관련 API를 사용하기 편하게 만든 라이브러리입니다.
|
|
5
|
+
Author: WONWOO LEE
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Repository, https://example.com
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Requires-Dist: httpx>=0.27
|
|
11
|
+
Provides-Extra: dev
|
|
12
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
13
|
+
|
|
14
|
+
## DatagokrReader
|
|
15
|
+
|
|
16
|
+
data.go.kr의 채권 관련 API를 사용하기 편하게 만든 라이브러리입니다.
|
|
17
|
+
|
|
18
|
+
### Example
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
from datagokr_reader import run_bond_workflow
|
|
22
|
+
|
|
23
|
+
# 띄어쓰기 및 대소문자 무관, 채권 관련 주요 데이터 모두 정리
|
|
24
|
+
result = run_bond_workflow(datagokr_api_key, "공공데이터포털1EB")
|
|
25
|
+
```
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=69", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "datagokr_reader"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = " data.go.kr의 채권 관련 API를 사용하기 편하게 만든 라이브러리입니다."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [{ name = "WONWOO LEE" }]
|
|
13
|
+
dependencies = [
|
|
14
|
+
"httpx>=0.27",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.optional-dependencies]
|
|
18
|
+
dev = [
|
|
19
|
+
"pytest>=8.0",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[project.urls]
|
|
23
|
+
Repository = "https://example.com"
|
|
24
|
+
|
|
25
|
+
[project.scripts]
|
|
26
|
+
datagokr-reader = "datagokr_reader.cli:main"
|
|
27
|
+
|
|
28
|
+
[tool.setuptools]
|
|
29
|
+
package-dir = {"" = "src"}
|
|
30
|
+
|
|
31
|
+
[tool.setuptools.packages.find]
|
|
32
|
+
where = ["src"]
|
|
33
|
+
|
|
34
|
+
[tool.pytest.ini_options]
|
|
35
|
+
testpaths = ["tests"]
|
|
36
|
+
addopts = "-q"
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# datagokr_reader 구조:
|
|
2
|
+
#
|
|
3
|
+
# src/datagokr_reader/
|
|
4
|
+
# ├── __init__.py
|
|
5
|
+
# ├── getOptiExer.py # 실제 주식 행사내역 다운로드
|
|
6
|
+
# ├── getOptiExerPricAdju.py # 주식 행사가 조정내역 다운로드
|
|
7
|
+
# ├── getEarlExerOpti.py # 옵션 행사 일정 다운로드
|
|
8
|
+
# ├── getBondBasiInfo.py # 채권 기본 정보 다운로드, Not reliable
|
|
9
|
+
# ├── getBondWithOptiCallRede.py # 옵션 행사내역 다운로드
|
|
10
|
+
# ├── getIssuIssuItemStat.py # 발행인에 따른 채권 조회
|
|
11
|
+
|
|
12
|
+
from .getOptiExer import get_OptiExer, parse_opti_exer
|
|
13
|
+
from .getOptiExerPricAdju import get_OptiExerPricAdju, parse_opti_exer_pric_adju
|
|
14
|
+
from .getEarlExerOpti import get_EarlExerOpti, parse_earl_exer_opti
|
|
15
|
+
from .getBondBasiInfo import get_BondBasiInfo, parse_bond_basi_info
|
|
16
|
+
from .getBondWithOptiCallRede import get_BondWithOptiCallRede, parse_bond_with_opti_call_rede
|
|
17
|
+
from .getIssuIssuItemStat import get_IssuIssuItemStat, parse_issu_issu_item_stat
|
|
18
|
+
from .workflow import run_bond_workflow
|
|
19
|
+
__all__ = [
|
|
20
|
+
"get_OptiExer", "parse_opti_exer",
|
|
21
|
+
"get_OptiExerPricAdju", "parse_opti_exer_pric_adju",
|
|
22
|
+
"get_EarlExerOpti", "parse_earl_exer_opti",
|
|
23
|
+
"get_BondBasiInfo", "parse_bond_basi_info",
|
|
24
|
+
"get_BondWithOptiCallRede", "parse_bond_with_opti_call_rede",
|
|
25
|
+
"get_IssuIssuItemStat", "parse_issu_issu_item_stat",
|
|
26
|
+
"run_bond_workflow",
|
|
27
|
+
]
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
import httpx
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_datagokr_data(
|
|
10
|
+
serviceUrl: str, params: dict[str, Any], timeout_seconds: float = 60.0
|
|
11
|
+
) -> Any | None:
|
|
12
|
+
""" Datagokr API를 호출해서 응답 JSON을 반환한다. """
|
|
13
|
+
baseUrl = "http://apis.data.go.kr"
|
|
14
|
+
url = f"{baseUrl}/{serviceUrl}"
|
|
15
|
+
try:
|
|
16
|
+
resp = httpx.get(url, params=params, timeout=timeout_seconds)
|
|
17
|
+
resp.raise_for_status()
|
|
18
|
+
return resp.json()
|
|
19
|
+
except Exception:
|
|
20
|
+
return None
|
|
21
|
+
|
|
22
|
+
def get_datagokr_document(
|
|
23
|
+
serviceUrl: str, params: dict[str, Any], timeout_seconds: float = 60.0,
|
|
24
|
+
numOfRows: int = 100, resultType: str = "json",
|
|
25
|
+
) -> Any | None:
|
|
26
|
+
"""
|
|
27
|
+
Datagokr API를 반복 호출해서 응답 JSON을 반환한다.
|
|
28
|
+
1. `pageNo = 1`부터 `pageNo = totalCount / numOfRows` 페이지까지 반복 호출한다.
|
|
29
|
+
2. 각 페이지의 items를 모두 모아서 반환한다.
|
|
30
|
+
반환 형식은 기본 형식과 동일하며, items는 {"item": [...]} 구조와 단순 리스트 구조를 모두 지원한다.
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"response": {
|
|
34
|
+
"body": {
|
|
35
|
+
"pageNo": 1,
|
|
36
|
+
"numOfRows": 100,
|
|
37
|
+
"totalCount": ...,
|
|
38
|
+
"items": # contents
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
"""
|
|
44
|
+
# 공통 파라미터 설정 (기본값이 있으면 덮어쓰지 않음)
|
|
45
|
+
base_params: dict[str, Any] = dict(params)
|
|
46
|
+
base_params.setdefault("numOfRows", numOfRows)
|
|
47
|
+
base_params.setdefault("resultType", resultType)
|
|
48
|
+
|
|
49
|
+
# 1페이지 호출
|
|
50
|
+
first_params = dict(base_params)
|
|
51
|
+
first_params["pageNo"] = 1
|
|
52
|
+
first_doc = get_datagokr_data(serviceUrl, first_params, timeout_seconds)
|
|
53
|
+
if first_doc is None:
|
|
54
|
+
return None
|
|
55
|
+
try:
|
|
56
|
+
body = first_doc["response"]["body"]
|
|
57
|
+
except Exception:
|
|
58
|
+
return first_doc
|
|
59
|
+
|
|
60
|
+
# 전체 개수 확인
|
|
61
|
+
total_count_raw = body.get("totalCount", 0)
|
|
62
|
+
try:
|
|
63
|
+
total_count = int(total_count_raw)
|
|
64
|
+
except Exception:
|
|
65
|
+
total_count = 0
|
|
66
|
+
if total_count <= 0:
|
|
67
|
+
return first_doc
|
|
68
|
+
|
|
69
|
+
# items 추출
|
|
70
|
+
def extract_items(items_obj: Any) -> list[Any]:
|
|
71
|
+
# 1) items가 None인 경우 -> []
|
|
72
|
+
if items_obj is None:
|
|
73
|
+
return []
|
|
74
|
+
# 2) "items": {"item": [...]} OR "items": {"item": {...}} 인 경우
|
|
75
|
+
# - inner가 리스트면 그대로 리스트 반환
|
|
76
|
+
# - inner가 단일 객체면 리스트로 감싸서 반환
|
|
77
|
+
if isinstance(items_obj, dict) and "item" in items_obj:
|
|
78
|
+
inner = items_obj.get("item")
|
|
79
|
+
if inner is None:
|
|
80
|
+
return []
|
|
81
|
+
if isinstance(inner, list):
|
|
82
|
+
return inner
|
|
83
|
+
return [inner]
|
|
84
|
+
# 3) "items": [...] 인 경우 -> 그대로 리스트 반환
|
|
85
|
+
if isinstance(items_obj, list):
|
|
86
|
+
return items_obj
|
|
87
|
+
# 4) 그 외에는 리스트로 감싸서 반환
|
|
88
|
+
return [items_obj]
|
|
89
|
+
|
|
90
|
+
items_obj = body.get("items")
|
|
91
|
+
all_items: list[Any] = extract_items(items_obj)
|
|
92
|
+
|
|
93
|
+
# 총 페이지 수 계산
|
|
94
|
+
total_pages = (total_count + numOfRows - 1) // numOfRows
|
|
95
|
+
|
|
96
|
+
# 2페이지부터 마지막 페이지까지 반복 호출
|
|
97
|
+
for page_no in range(2, total_pages + 1):
|
|
98
|
+
page_params = dict(base_params)
|
|
99
|
+
page_params["pageNo"] = page_no
|
|
100
|
+
page_doc = get_datagokr_data(serviceUrl, page_params, timeout_seconds)
|
|
101
|
+
if page_doc is None:
|
|
102
|
+
break
|
|
103
|
+
try:
|
|
104
|
+
page_body = page_doc["response"]["body"]
|
|
105
|
+
page_items_obj = page_body.get("items")
|
|
106
|
+
except Exception:
|
|
107
|
+
break
|
|
108
|
+
all_items.extend(extract_items(page_items_obj))
|
|
109
|
+
|
|
110
|
+
# 첫 응답 객체에 모든 items를 합쳐서 세팅
|
|
111
|
+
if isinstance(items_obj, dict) and "item" in items_obj:
|
|
112
|
+
items_obj["item"] = all_items
|
|
113
|
+
body["items"] = items_obj
|
|
114
|
+
else:
|
|
115
|
+
body["items"] = all_items
|
|
116
|
+
|
|
117
|
+
# 메타 정보 정리 (pageNo는 1, numOfRows는 입력값, totalCount는 실제 아이템 개수)
|
|
118
|
+
body["pageNo"] = 1
|
|
119
|
+
body["numOfRows"] = numOfRows
|
|
120
|
+
body["totalCount"] = len(all_items)
|
|
121
|
+
|
|
122
|
+
return first_doc
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def normalize_name(name: str) -> str:
|
|
126
|
+
name_no_space = name.replace(" ", "")
|
|
127
|
+
name_no_space = re.sub(r"[A-Za-z]+", lambda m: m.group(0).lower(), name_no_space)
|
|
128
|
+
paren_index = name_no_space.find("(")
|
|
129
|
+
if paren_index != -1:
|
|
130
|
+
return name_no_space[:paren_index]
|
|
131
|
+
return name_no_space
|
|
132
|
+
|
|
133
|
+
def issuer_name_from_bond_name(bond_name: str) -> str:
|
|
134
|
+
""" 채권명에서 발행사명을 추출. (숫자를 기준으로 절삭) """
|
|
135
|
+
normalized = normalize_name(bond_name)
|
|
136
|
+
m = re.search(r"\d", normalized)
|
|
137
|
+
if not m:
|
|
138
|
+
return normalized
|
|
139
|
+
return normalized[: m.start()]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
from datagokr_reader.workflow import run_bond_workflow
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def main(argv: list[str] | None = None) -> int:
|
|
11
|
+
parser = argparse.ArgumentParser(prog="datagokr-reader")
|
|
12
|
+
parser.add_argument("--api-key", required=True, help="data.go.kr serviceKey")
|
|
13
|
+
parser.add_argument("--bond-name", required=True, help="채권명 (띄어쓰기/대소문자 무관)")
|
|
14
|
+
parser.add_argument(
|
|
15
|
+
"--pretty",
|
|
16
|
+
action="store_true",
|
|
17
|
+
help="Pretty JSON 출력",
|
|
18
|
+
)
|
|
19
|
+
args = parser.parse_args(argv)
|
|
20
|
+
|
|
21
|
+
result = run_bond_workflow(args.api_key, args.bond_name)
|
|
22
|
+
if args.pretty:
|
|
23
|
+
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|
24
|
+
else:
|
|
25
|
+
print(json.dumps(result, ensure_ascii=False))
|
|
26
|
+
return 0
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
if __name__ == "__main__":
|
|
30
|
+
raise SystemExit(main(sys.argv[1:]))
|
|
31
|
+
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from .base_reader import get_datagokr_document
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
BondBasiInfo : (bondIsurNm|crno)
|
|
9
|
+
-> bondBal(발행잔액) +
|
|
10
|
+
"basDt": 채권의 기준일
|
|
11
|
+
"crno": 기업의 법인등록번호
|
|
12
|
+
"bondIsurNm": 채권의 발행사명
|
|
13
|
+
"isinCdNm": 채권의 ISIN 종목명
|
|
14
|
+
"bondIssuDt": 채권의 발행일
|
|
15
|
+
"bondExprDt": 채권의 만기일
|
|
16
|
+
"bondIssuAmt": 채권의 발행액
|
|
17
|
+
"bondPymtAmt": 채권의 납입액
|
|
18
|
+
"issuDptyNm": 주간사명
|
|
19
|
+
"""
|
|
20
|
+
def get_BondBasiInfo(serviceKey: str, basDt: str, bondIsurNm: str, crno: str, timeout_seconds: float = 60.0) -> Any | None:
|
|
21
|
+
""" 채권 기본 정보 다운로드. """
|
|
22
|
+
service_url = "1160100/service/GetBondIssuInfoService/getBondBasiInfo"
|
|
23
|
+
if not bondIsurNm and not crno:
|
|
24
|
+
raise ValueError("bondIsurNm 또는 crno가 없습니다.")
|
|
25
|
+
|
|
26
|
+
params: dict[str, Any] = {
|
|
27
|
+
"serviceKey": serviceKey
|
|
28
|
+
}
|
|
29
|
+
if basDt:
|
|
30
|
+
params["basDt"] = basDt
|
|
31
|
+
if bondIsurNm:
|
|
32
|
+
params["bondIsurNm"] = bondIsurNm
|
|
33
|
+
if crno:
|
|
34
|
+
params["crno"] = crno
|
|
35
|
+
|
|
36
|
+
return get_datagokr_document(
|
|
37
|
+
serviceUrl=service_url,
|
|
38
|
+
params=params,
|
|
39
|
+
timeout_seconds=timeout_seconds
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def parse_bond_basi_info(serviceKey: str, basDt: str, bondIsurNm: str, crno: str, timeout_seconds: float = 60.0) -> dict[str, Any] | None:
|
|
44
|
+
""" Parse get_BondBasiInfo results. """
|
|
45
|
+
raw = get_BondBasiInfo(serviceKey=serviceKey, basDt=basDt, bondIsurNm=bondIsurNm, crno=crno, timeout_seconds=timeout_seconds)
|
|
46
|
+
if raw is None:
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
body = raw["response"]["body"]
|
|
51
|
+
items_obj = body.get("items")
|
|
52
|
+
except Exception:
|
|
53
|
+
return None
|
|
54
|
+
|
|
55
|
+
def extract_items(items: Any) -> list[dict[str, Any]]:
|
|
56
|
+
if items is None:
|
|
57
|
+
return []
|
|
58
|
+
if isinstance(items, dict) and "item" in items:
|
|
59
|
+
inner = items.get("item")
|
|
60
|
+
if inner is None:
|
|
61
|
+
return []
|
|
62
|
+
if isinstance(inner, list):
|
|
63
|
+
return [i for i in inner if isinstance(i, dict)]
|
|
64
|
+
return [inner] if isinstance(inner, dict) else []
|
|
65
|
+
if isinstance(items, list):
|
|
66
|
+
return [i for i in items if isinstance(i, dict)]
|
|
67
|
+
return [items] if isinstance(items, dict) else []
|
|
68
|
+
|
|
69
|
+
items = extract_items(items_obj)
|
|
70
|
+
if not items:
|
|
71
|
+
return {}
|
|
72
|
+
|
|
73
|
+
result: dict[str, Any] = {}
|
|
74
|
+
for it in items:
|
|
75
|
+
isin = it.get("isinCd")
|
|
76
|
+
if not isin:
|
|
77
|
+
continue
|
|
78
|
+
|
|
79
|
+
meta = result.setdefault(
|
|
80
|
+
isin,
|
|
81
|
+
{
|
|
82
|
+
"metadata": {
|
|
83
|
+
"basDt": it.get("basDt"),
|
|
84
|
+
"crno": it.get("crno"),
|
|
85
|
+
"bondIsurNm": it.get("bondIsurNm"),
|
|
86
|
+
"isinCd": it.get("isinCd"),
|
|
87
|
+
"isinCdNm": it.get("isinCdNm"),
|
|
88
|
+
"bondIssuDt": it.get("bondIssuDt"),
|
|
89
|
+
"bondIssuAmt": it.get("bondIssuAmt"),
|
|
90
|
+
"bondExprDt": it.get("bondExprDt"),
|
|
91
|
+
"bondPymtAmt": it.get("bondPymtAmt"),
|
|
92
|
+
# "bondSrfcInrt": it.get("bondSrfcInrt"), # 표면이자율(추후 복원 가능하도록 주석 처리)
|
|
93
|
+
"issuDptyNm": it.get("issuDptyNm"),
|
|
94
|
+
},
|
|
95
|
+
"items": [],
|
|
96
|
+
},
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
meta["items"].append(
|
|
100
|
+
{
|
|
101
|
+
"bondBal": it.get("bondBal"),
|
|
102
|
+
}
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
return result
|
|
106
|
+
|
|
107
|
+
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from .base_reader import get_datagokr_document
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_BondWithOptiCallRede(
|
|
9
|
+
serviceKey: str,
|
|
10
|
+
crno: str,
|
|
11
|
+
opbdIsurNm: str,
|
|
12
|
+
timeout_seconds: float = 60.0,
|
|
13
|
+
numOfRows: int = 100,
|
|
14
|
+
resultType: str = "json",
|
|
15
|
+
) -> Any | None:
|
|
16
|
+
""" 옵션 행사내역 다운로드. """
|
|
17
|
+
service_url = "1160100/service/GetBondRedeInfoService/getBondWithOptiCallRede"
|
|
18
|
+
if not crno and not opbdIsurNm:
|
|
19
|
+
raise ValueError("crno 또는 opbdIsurNm가 없습니다.")
|
|
20
|
+
|
|
21
|
+
params: dict[str, Any] = {
|
|
22
|
+
"serviceKey": serviceKey,
|
|
23
|
+
}
|
|
24
|
+
if crno:
|
|
25
|
+
params["crno"] = crno
|
|
26
|
+
if opbdIsurNm:
|
|
27
|
+
params["opbdIsurNm"] = opbdIsurNm
|
|
28
|
+
|
|
29
|
+
return get_datagokr_document(
|
|
30
|
+
serviceUrl=service_url,
|
|
31
|
+
params=params,
|
|
32
|
+
timeout_seconds=timeout_seconds,
|
|
33
|
+
numOfRows=numOfRows,
|
|
34
|
+
resultType=resultType,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def parse_bond_with_opti_call_rede(
|
|
39
|
+
serviceKey: str,
|
|
40
|
+
crno: str,
|
|
41
|
+
opbdIsurNm: str,
|
|
42
|
+
timeout_seconds: float = 60.0,
|
|
43
|
+
raw: Any | None = None,
|
|
44
|
+
isinCdKey: str | None = None,
|
|
45
|
+
) -> list[dict[str, Any]] | None:
|
|
46
|
+
""" Parse get_BondWithOptiCallRede results. """
|
|
47
|
+
if raw is None:
|
|
48
|
+
raw = get_BondWithOptiCallRede(
|
|
49
|
+
serviceKey=serviceKey,
|
|
50
|
+
crno=crno,
|
|
51
|
+
opbdIsurNm=opbdIsurNm,
|
|
52
|
+
timeout_seconds=timeout_seconds,
|
|
53
|
+
)
|
|
54
|
+
if raw is None:
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
body = raw["response"]["body"]
|
|
59
|
+
items_obj = body.get("items")
|
|
60
|
+
except Exception:
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
def extract_items(items: Any) -> list[dict[str, Any]]:
|
|
64
|
+
if items is None:
|
|
65
|
+
return []
|
|
66
|
+
if isinstance(items, dict) and "item" in items:
|
|
67
|
+
inner = items.get("item")
|
|
68
|
+
if inner is None:
|
|
69
|
+
return []
|
|
70
|
+
if isinstance(inner, list):
|
|
71
|
+
return [i for i in inner if isinstance(i, dict)]
|
|
72
|
+
return [inner] if isinstance(inner, dict) else []
|
|
73
|
+
if isinstance(items, list):
|
|
74
|
+
return [i for i in items if isinstance(i, dict)]
|
|
75
|
+
return [items] if isinstance(items, dict) else []
|
|
76
|
+
|
|
77
|
+
items = extract_items(items_obj)
|
|
78
|
+
if not items:
|
|
79
|
+
return []
|
|
80
|
+
|
|
81
|
+
# 아래 항목들은 it에 존재하지만, 현재 라이브러리 목적상 파싱하지 않음
|
|
82
|
+
# "isinCd": it.get("isinCd"), # ISIN 종목번호, BasiInfo에서 이미 있음(추후 복원 가능하도록 주석 처리)
|
|
83
|
+
# "crno": it.get("crno"), # 기업의 법인등록번호, BasiInfo에서 이미 있음(추후 복원 가능하도록 주석 처리)
|
|
84
|
+
# "isinCdNm": it.get("isinCdNm"), # ISIN 종목명, BasiInfo에서 이미 있음(추후 복원 가능하도록 주석 처리)
|
|
85
|
+
# "opbdIsurNm": it.get("opbdIsurNm"), # 채권의 발행사명, BasiInfo에서 이미 있음(추후 복원 가능하도록 주석 처리)
|
|
86
|
+
# "opbdIssuDt": it.get("opbdIssuDt"), # 채권의 발행일, BasiInfo에서 이미 있음(추후 복원 가능하도록 주석 처리)
|
|
87
|
+
# "opbdExprDt": it.get("opbdExprDt"), # 채권의 만기일, BasiInfo에서 이미 있음(추후 복원 가능하도록 주석 처리)
|
|
88
|
+
# "opbdIssuAmt": it.get("opbdIssuAmt"), # 최초발행액, BasiInfo에서 이미 있음(추후 복원 가능하도록 주석 처리)
|
|
89
|
+
# "bondIssuAmt": it.get("bondIssuAmt"), # 발행잔액, BasiInfo에서 이미 있음(추후 복원 가능하도록 주석 처리)
|
|
90
|
+
|
|
91
|
+
out: list[dict[str, Any]] = []
|
|
92
|
+
for it in items:
|
|
93
|
+
isin = it.get("isinCd")
|
|
94
|
+
if isinCdKey:
|
|
95
|
+
if not isin or isin != isinCdKey:
|
|
96
|
+
continue
|
|
97
|
+
else:
|
|
98
|
+
if not isin:
|
|
99
|
+
continue
|
|
100
|
+
out.append(
|
|
101
|
+
{
|
|
102
|
+
"isinCd": isin,
|
|
103
|
+
"optnTcdNm": it.get("optnTcdNm"),
|
|
104
|
+
"opbdClrdDt": it.get("opbdClrdDt"),
|
|
105
|
+
"opbdPamtPayAmt": it.get("opbdPamtPayAmt"),
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
return out
|
|
110
|
+
|
|
111
|
+
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from .base_reader import get_datagokr_document
|
|
6
|
+
|
|
7
|
+
def get_EarlExerOpti(
|
|
8
|
+
serviceKey: str,
|
|
9
|
+
basDt: str | None,
|
|
10
|
+
bondIsurNm: str,
|
|
11
|
+
crno: str,
|
|
12
|
+
timeout_seconds: float = 60.0,
|
|
13
|
+
) -> Any | None:
|
|
14
|
+
""" 옵션 행사 일정 다운로드.
|
|
15
|
+
사모 사채는 콜옵션이 공시되지 않는 경우가 있으니 주의.
|
|
16
|
+
"""
|
|
17
|
+
service_url = "1160100/service/GetBondRedeInfoService/getEarlExerOpti"
|
|
18
|
+
if not bondIsurNm and not crno:
|
|
19
|
+
raise ValueError("bondIsurNm 또는 crno가 없습니다.")
|
|
20
|
+
|
|
21
|
+
params: dict[str, Any] = {
|
|
22
|
+
"serviceKey": serviceKey
|
|
23
|
+
}
|
|
24
|
+
if basDt:
|
|
25
|
+
params["basDt"] = basDt
|
|
26
|
+
if bondIsurNm:
|
|
27
|
+
params["bondIsurNm"] = bondIsurNm
|
|
28
|
+
if crno:
|
|
29
|
+
params["crno"] = crno
|
|
30
|
+
return get_datagokr_document(
|
|
31
|
+
serviceUrl=service_url,
|
|
32
|
+
params=params,
|
|
33
|
+
timeout_seconds=timeout_seconds
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def parse_earl_exer_opti(
|
|
38
|
+
serviceKey: str,
|
|
39
|
+
basDt: str | None,
|
|
40
|
+
bondIsurNm: str,
|
|
41
|
+
crno: str,
|
|
42
|
+
timeout_seconds: float = 60.0,
|
|
43
|
+
isinCdKey: str | None = None,
|
|
44
|
+
) -> list[dict[str, Any]] | None:
|
|
45
|
+
""" Parse get_EarlExerOpti results. """
|
|
46
|
+
raw = get_EarlExerOpti(serviceKey=serviceKey, basDt=basDt, bondIsurNm=bondIsurNm, crno=crno, timeout_seconds=timeout_seconds)
|
|
47
|
+
if raw is None:
|
|
48
|
+
return None
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
body = raw["response"]["body"]
|
|
52
|
+
items_obj = body.get("items")
|
|
53
|
+
except Exception:
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
def extract_items(items: Any) -> list[dict[str, Any]]:
|
|
57
|
+
if items is None:
|
|
58
|
+
return []
|
|
59
|
+
if isinstance(items, dict) and "item" in items:
|
|
60
|
+
inner = items.get("item")
|
|
61
|
+
if inner is None:
|
|
62
|
+
return []
|
|
63
|
+
if isinstance(inner, list):
|
|
64
|
+
return [i for i in inner if isinstance(i, dict)]
|
|
65
|
+
return [inner] if isinstance(inner, dict) else []
|
|
66
|
+
if isinstance(items, list):
|
|
67
|
+
return [i for i in items if isinstance(i, dict)]
|
|
68
|
+
return [items] if isinstance(items, dict) else []
|
|
69
|
+
|
|
70
|
+
items = extract_items(items_obj)
|
|
71
|
+
if not items:
|
|
72
|
+
return []
|
|
73
|
+
|
|
74
|
+
# 아래 항목들은 it에 존재하지만, 현재 라이브러리 목적상 파싱하지 않음
|
|
75
|
+
# "isinCd": it.get("isinCd"), # ISIN 종목번호, BasiInfo에서 이미 있음(추후 복원 가능하도록 주석 처리)
|
|
76
|
+
# "basDt": it.get("basDt"), # 채권의 기준일, BasiInfo에서 이미 있음(추후 복원 가능하도록 주석 처리)
|
|
77
|
+
# "crno": it.get("crno"), # 기업의 법인등록번호, BasiInfo에서 이미 있음(추후 복원 가능하도록 주석 처리)
|
|
78
|
+
# "bondIsurNm": it.get("bondIsurNm"), # 채권의 발행사명, BasiInfo에서 이미 있음(추후 복원 가능하도록 주석 처리)
|
|
79
|
+
# "isinCdNm": it.get("isinCdNm"), # ISIN 종목명, BasiInfo에서 이미 있음(추후 복원 가능하도록 주석 처리)
|
|
80
|
+
|
|
81
|
+
out: list[dict[str, Any]] = []
|
|
82
|
+
for it in items:
|
|
83
|
+
isin = it.get("isinCd")
|
|
84
|
+
if not isin:
|
|
85
|
+
continue
|
|
86
|
+
if isinCdKey and isin != isinCdKey:
|
|
87
|
+
continue
|
|
88
|
+
out.append(
|
|
89
|
+
{
|
|
90
|
+
"isinCd": isin,
|
|
91
|
+
"type": it.get("optnTcdNm"),
|
|
92
|
+
"dates": [
|
|
93
|
+
it.get("optnExertSttgDt"),
|
|
94
|
+
it.get("optnExertEdDt"),
|
|
95
|
+
it.get("clrdDt"),
|
|
96
|
+
],
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
return out
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from .base_reader import get_datagokr_document, normalize_name
|
|
6
|
+
|
|
7
|
+
def get_IssuIssuItemStat(serviceKey: str, bondIsurNm: str, timeout_seconds: float = 60.0) -> Any | None:
|
|
8
|
+
""" 발행인에 따른 채권 조회. """
|
|
9
|
+
service_url = "1160100/service/GetBondTradInfoService/getIssuIssuItemStat"
|
|
10
|
+
|
|
11
|
+
params: dict[str, Any] = {
|
|
12
|
+
"serviceKey": serviceKey,
|
|
13
|
+
"bondIsurNm": bondIsurNm
|
|
14
|
+
}
|
|
15
|
+
return get_datagokr_document(
|
|
16
|
+
serviceUrl=service_url,
|
|
17
|
+
params=params,
|
|
18
|
+
timeout_seconds=timeout_seconds
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def parse_issu_issu_item_stat(
|
|
23
|
+
serviceKey: str,
|
|
24
|
+
bondIsurNm: str,
|
|
25
|
+
bondNm: str,
|
|
26
|
+
timeout_seconds: float = 60.0,
|
|
27
|
+
raw: Any | None = None,
|
|
28
|
+
) -> dict[str, Any] | None:
|
|
29
|
+
""" Parse get_IssuIssuItemStat results. """
|
|
30
|
+
if raw is None:
|
|
31
|
+
raw = get_IssuIssuItemStat(serviceKey=serviceKey, bondIsurNm=bondIsurNm, timeout_seconds=timeout_seconds)
|
|
32
|
+
if raw is None:
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
body = raw["response"]["body"]
|
|
37
|
+
items_obj = body.get("items")
|
|
38
|
+
except Exception:
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
def extract_items(items: Any) -> list[dict[str, Any]]:
|
|
42
|
+
if items is None:
|
|
43
|
+
return []
|
|
44
|
+
if isinstance(items, dict) and "item" in items:
|
|
45
|
+
inner = items.get("item")
|
|
46
|
+
if inner is None:
|
|
47
|
+
return []
|
|
48
|
+
if isinstance(inner, list):
|
|
49
|
+
return [i for i in inner if isinstance(i, dict)]
|
|
50
|
+
return [inner] if isinstance(inner, dict) else []
|
|
51
|
+
if isinstance(items, list):
|
|
52
|
+
return [i for i in items if isinstance(i, dict)]
|
|
53
|
+
return [items] if isinstance(items, dict) else []
|
|
54
|
+
|
|
55
|
+
items = extract_items(items_obj)
|
|
56
|
+
if not items:
|
|
57
|
+
return None
|
|
58
|
+
for it in items:
|
|
59
|
+
normalized_isinCdNm = normalize_name(it.get("isinCdNm"))
|
|
60
|
+
normalized_bondNm = normalize_name(bondNm)
|
|
61
|
+
if normalized_isinCdNm == normalized_bondNm:
|
|
62
|
+
result = {
|
|
63
|
+
"basDt": it.get("basDt"),
|
|
64
|
+
"crno": it.get("crno"),
|
|
65
|
+
"isinCd": it.get("isinCd"),
|
|
66
|
+
|
|
67
|
+
"bondExprDt": it.get("bondExprDt"),
|
|
68
|
+
"bondIssuDt": it.get("bondIssuDt"),
|
|
69
|
+
"bondIssuAmt": it.get("bondIssuAmt"),
|
|
70
|
+
|
|
71
|
+
"isinCdNm": it.get("isinCdNm"),
|
|
72
|
+
"bondPymtAmt": it.get("bondPymtAmt"),
|
|
73
|
+
}
|
|
74
|
+
return result
|
|
75
|
+
return None
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from .base_reader import get_datagokr_document
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_OptiExer(serviceKey: str, crno: str, timeout_seconds: float = 60.0) -> str | None:
|
|
9
|
+
""" 옵션 실제 행사내역 다운로드. """
|
|
10
|
+
service_url = "1160100/service/GetBondRedeInfoService/getOptiExer"
|
|
11
|
+
if not crno:
|
|
12
|
+
raise ValueError("crno가 없습니다.")
|
|
13
|
+
|
|
14
|
+
params: dict[str, Any] = {
|
|
15
|
+
"serviceKey": serviceKey,
|
|
16
|
+
"crno": crno,
|
|
17
|
+
}
|
|
18
|
+
return get_datagokr_document(
|
|
19
|
+
serviceUrl=service_url,
|
|
20
|
+
params=params,
|
|
21
|
+
timeout_seconds=timeout_seconds
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def parse_opti_exer(
|
|
26
|
+
serviceKey: str,
|
|
27
|
+
crno: str,
|
|
28
|
+
timeout_seconds: float = 60.0,
|
|
29
|
+
raw: Any | None = None,
|
|
30
|
+
isinCdKey: str | None = None,
|
|
31
|
+
) -> list[dict[str, Any]] | None:
|
|
32
|
+
""" Parse get_OptiExer results. """
|
|
33
|
+
if raw is None:
|
|
34
|
+
raw = get_OptiExer(serviceKey=serviceKey, crno=crno, timeout_seconds=timeout_seconds)
|
|
35
|
+
if raw is None:
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
body = raw["response"]["body"]
|
|
40
|
+
items_obj = body.get("items")
|
|
41
|
+
except Exception:
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
def extract_items(items: Any) -> list[dict[str, Any]]:
|
|
45
|
+
if items is None:
|
|
46
|
+
return []
|
|
47
|
+
if isinstance(items, dict) and "item" in items:
|
|
48
|
+
inner = items.get("item")
|
|
49
|
+
if inner is None:
|
|
50
|
+
return []
|
|
51
|
+
if isinstance(inner, list):
|
|
52
|
+
return [i for i in inner if isinstance(i, dict)]
|
|
53
|
+
return [inner] if isinstance(inner, dict) else []
|
|
54
|
+
if isinstance(items, list):
|
|
55
|
+
return [i for i in items if isinstance(i, dict)]
|
|
56
|
+
return [items] if isinstance(items, dict) else []
|
|
57
|
+
|
|
58
|
+
items = extract_items(items_obj)
|
|
59
|
+
if not items:
|
|
60
|
+
return []
|
|
61
|
+
|
|
62
|
+
# 아래 항목들은 it에 존재하지만, 현재 라이브러리 목적상 파싱하지 않음
|
|
63
|
+
# "basDt": it.get("basDt"), # 채권의 기준일, BasiInfo에서 이미 있음(추후 복원 가능하도록 주석 처리)
|
|
64
|
+
# "crno": it.get("crno"), # 기업의 법인등록번호, BasiInfo에서 이미 있음(추후 복원 가능하도록 주석 처리)
|
|
65
|
+
# "isinCdNm": it.get("isinCdNm"), # ISIN 종목명, BasiInfo에서 이미 있음(추후 복원 가능하도록 주석 처리)
|
|
66
|
+
# "scrsIsurNm": it.get("scrsIsurNm"), # 채권의 발행사명, BasiInfo에서 이미 있음(추후 복원 가능하도록 주석 처리)
|
|
67
|
+
# "trgtStckIsinCd": it.get("trgtStckIsinCd"), # 대상 주식의 ISIN 종목번호, 현재 프로젝트에서 사용하지 않음(추후 복원 가능하도록 주석 처리)
|
|
68
|
+
# "trgtStckIsinCdNm": it.get("trgtStckIsinCdNm"), # 대상 주식의 ISIN 종목명, 현재 프로젝트에서 사용하지 않음(추후 복원 가능하도록 주석 처리)
|
|
69
|
+
|
|
70
|
+
out: list[dict[str, Any]] = []
|
|
71
|
+
for it in items:
|
|
72
|
+
isin = it.get("isinCd")
|
|
73
|
+
if not isin:
|
|
74
|
+
continue
|
|
75
|
+
if isinCdKey and isin != isinCdKey:
|
|
76
|
+
continue
|
|
77
|
+
out.append(
|
|
78
|
+
{
|
|
79
|
+
"isinCd": isin,
|
|
80
|
+
"optnExertPrc": it.get("optnExertPrc"),
|
|
81
|
+
"rgtOccrBasDt": it.get("rgtOccrBasDt"),
|
|
82
|
+
"exertPric": it.get("exertPric"),
|
|
83
|
+
"exertAmt": it.get("exertAmt"),
|
|
84
|
+
}
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# items 에 "rgtOccrBasDt": "00010101" 하나만 있으면 items 를 None 으로 처리
|
|
88
|
+
out = [x for x in out if (x.get("rgtOccrBasDt") != "00010101")]
|
|
89
|
+
return out
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from .base_reader import get_datagokr_document
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_OptiExerPricAdju(
|
|
9
|
+
serviceKey: str,
|
|
10
|
+
crno: str,
|
|
11
|
+
timeout_seconds: float = 60.0,
|
|
12
|
+
numOfRows: int = 100,
|
|
13
|
+
resultType: str = "json",
|
|
14
|
+
) -> Any | None:
|
|
15
|
+
""" 행사가 변동내역 및 현재 행사가 다운로드. """
|
|
16
|
+
service_url = "1160100/service/GetBondRedeInfoService/getOptiExerPricAdju"
|
|
17
|
+
if not crno:
|
|
18
|
+
raise ValueError("crno가 없습니다.")
|
|
19
|
+
|
|
20
|
+
params: dict[str, Any] = {
|
|
21
|
+
"serviceKey": serviceKey,
|
|
22
|
+
"crno": crno,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return get_datagokr_document(
|
|
26
|
+
serviceUrl=service_url,
|
|
27
|
+
params=params,
|
|
28
|
+
timeout_seconds=timeout_seconds,
|
|
29
|
+
numOfRows=numOfRows,
|
|
30
|
+
resultType=resultType,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def parse_opti_exer_pric_adju(
|
|
35
|
+
serviceKey: str,
|
|
36
|
+
crno: str,
|
|
37
|
+
timeout_seconds: float = 60.0,
|
|
38
|
+
raw: Any | None = None,
|
|
39
|
+
isinCdKey: str | None = None,
|
|
40
|
+
) -> list[dict[str, Any]] | None:
|
|
41
|
+
""" Parse get_OptiExerPricAdju results. """
|
|
42
|
+
if raw is None:
|
|
43
|
+
raw = get_OptiExerPricAdju(
|
|
44
|
+
serviceKey=serviceKey,
|
|
45
|
+
crno=crno,
|
|
46
|
+
timeout_seconds=timeout_seconds,
|
|
47
|
+
)
|
|
48
|
+
if raw is None:
|
|
49
|
+
return None
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
body = raw["response"]["body"]
|
|
53
|
+
items_obj = body.get("items")
|
|
54
|
+
except Exception:
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
def extract_items(items: Any) -> list[dict[str, Any]]:
|
|
58
|
+
if items is None:
|
|
59
|
+
return []
|
|
60
|
+
if isinstance(items, dict) and "item" in items:
|
|
61
|
+
inner = items.get("item")
|
|
62
|
+
if inner is None:
|
|
63
|
+
return []
|
|
64
|
+
if isinstance(inner, list):
|
|
65
|
+
return [i for i in inner if isinstance(i, dict)]
|
|
66
|
+
return [inner] if isinstance(inner, dict) else []
|
|
67
|
+
if isinstance(items, list):
|
|
68
|
+
return [i for i in items if isinstance(i, dict)]
|
|
69
|
+
return [items] if isinstance(items, dict) else []
|
|
70
|
+
|
|
71
|
+
items = extract_items(items_obj)
|
|
72
|
+
if not items:
|
|
73
|
+
return []
|
|
74
|
+
|
|
75
|
+
out: list[dict[str, Any]] = []
|
|
76
|
+
# "trgtStckIsinCd": it.get("trgtStckIsinCd"), # 대상 주식의 ISIN 종목번호, 현재 프로젝트에서 사용하지 않음(추후 복원 가능하도록 주석 처리)
|
|
77
|
+
# "trgtStckIsinCdNm": it.get("trgtStckIsinCdNm"), # 대상 주식의 ISIN 종목명, 현재 프로젝트에서 사용하지 않음(추후 복원 가능하도록 주석 처리)
|
|
78
|
+
for it in items:
|
|
79
|
+
isin = it.get("isinCd")
|
|
80
|
+
if not isin:
|
|
81
|
+
continue
|
|
82
|
+
if isinCdKey and isin != isinCdKey:
|
|
83
|
+
continue
|
|
84
|
+
out.append(
|
|
85
|
+
{
|
|
86
|
+
"isinCd": isin,
|
|
87
|
+
"basDt": it.get("basDt"),
|
|
88
|
+
"crno": it.get("crno"),
|
|
89
|
+
"isinCdNm": it.get("isinCdNm"),
|
|
90
|
+
"scrsIsurNm": it.get("scrsIsurNm"),
|
|
91
|
+
"optnExertPrc": it.get("optnExertPrc"),
|
|
92
|
+
"rgtExertPricAdjDt": it.get("rgtExertPricAdjDt"),
|
|
93
|
+
"rgtBchgExertPrc": it.get("rgtBchgExertPrc"),
|
|
94
|
+
"rgtAchgExertPrc": it.get("rgtAchgExertPrc"),
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# items 에 "rgtExertPricAdjDt": "00010101" 하나만 있으면 items 를 None 으로 처리
|
|
99
|
+
out = [x for x in out if (x.get("rgtExertPricAdjDt") != "00010101")]
|
|
100
|
+
return out
|
|
101
|
+
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datagokr_reader import (
|
|
4
|
+
parse_bond_with_opti_call_rede,
|
|
5
|
+
parse_earl_exer_opti,
|
|
6
|
+
parse_issu_issu_item_stat,
|
|
7
|
+
parse_opti_exer,
|
|
8
|
+
parse_opti_exer_pric_adju,
|
|
9
|
+
)
|
|
10
|
+
from datagokr_reader.base_reader import issuer_name_from_bond_name, normalize_name
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def run_bond_workflow(datagokr_api_key: str, bond_name: str):
|
|
14
|
+
"""
|
|
15
|
+
채권명 to {"함수명": 결과, ...} dict
|
|
16
|
+
"""
|
|
17
|
+
bond_name_normalized = normalize_name(bond_name)
|
|
18
|
+
issuer_name = issuer_name_from_bond_name(bond_name)
|
|
19
|
+
|
|
20
|
+
issu = parse_issu_issu_item_stat(
|
|
21
|
+
serviceKey=datagokr_api_key,
|
|
22
|
+
bondIsurNm=issuer_name,
|
|
23
|
+
bondNm=bond_name_normalized,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
crno = issu.get("crno") if isinstance(issu, dict) else ""
|
|
27
|
+
isinCdKey = issu.get("isinCd") if isinstance(issu, dict) else None
|
|
28
|
+
basDt = issu.get("basDt") if isinstance(issu, dict) else None
|
|
29
|
+
|
|
30
|
+
bond_with = parse_bond_with_opti_call_rede(
|
|
31
|
+
serviceKey=datagokr_api_key,
|
|
32
|
+
crno=crno,
|
|
33
|
+
opbdIsurNm=issuer_name,
|
|
34
|
+
isinCdKey=isinCdKey,
|
|
35
|
+
)
|
|
36
|
+
opti_exer = parse_opti_exer(
|
|
37
|
+
serviceKey=datagokr_api_key,
|
|
38
|
+
crno=crno,
|
|
39
|
+
isinCdKey=isinCdKey,
|
|
40
|
+
)
|
|
41
|
+
opti_adju = parse_opti_exer_pric_adju(
|
|
42
|
+
serviceKey=datagokr_api_key,
|
|
43
|
+
crno=crno,
|
|
44
|
+
isinCdKey=isinCdKey,
|
|
45
|
+
)
|
|
46
|
+
earl = parse_earl_exer_opti(
|
|
47
|
+
serviceKey=datagokr_api_key,
|
|
48
|
+
basDt=basDt,
|
|
49
|
+
bondIsurNm=issuer_name,
|
|
50
|
+
crno=crno,
|
|
51
|
+
isinCdKey=isinCdKey,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
"parse_issu_issu_item_stat": issu,
|
|
56
|
+
"parse_bond_with_opti_call_rede": bond_with,
|
|
57
|
+
"parse_opti_exer": opti_exer,
|
|
58
|
+
"parse_opti_exer_pric_adju": opti_adju,
|
|
59
|
+
"parse_earl_exer_opti": earl,
|
|
60
|
+
}
|
|
61
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: datagokr_reader
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: data.go.kr의 채권 관련 API를 사용하기 편하게 만든 라이브러리입니다.
|
|
5
|
+
Author: WONWOO LEE
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Repository, https://example.com
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Requires-Dist: httpx>=0.27
|
|
11
|
+
Provides-Extra: dev
|
|
12
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
13
|
+
|
|
14
|
+
## DatagokrReader
|
|
15
|
+
|
|
16
|
+
data.go.kr의 채권 관련 API를 사용하기 편하게 만든 라이브러리입니다.
|
|
17
|
+
|
|
18
|
+
### Example
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
from datagokr_reader import run_bond_workflow
|
|
22
|
+
|
|
23
|
+
# 띄어쓰기 및 대소문자 무관, 채권 관련 주요 데이터 모두 정리
|
|
24
|
+
result = run_bond_workflow(datagokr_api_key, "공공데이터포털1EB")
|
|
25
|
+
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/datagokr_reader/__init__.py
|
|
4
|
+
src/datagokr_reader/base_reader.py
|
|
5
|
+
src/datagokr_reader/cli.py
|
|
6
|
+
src/datagokr_reader/getBondBasiInfo.py
|
|
7
|
+
src/datagokr_reader/getBondWithOptiCallRede.py
|
|
8
|
+
src/datagokr_reader/getEarlExerOpti.py
|
|
9
|
+
src/datagokr_reader/getIssuIssuItemStat.py
|
|
10
|
+
src/datagokr_reader/getOptiExer.py
|
|
11
|
+
src/datagokr_reader/getOptiExerPricAdju.py
|
|
12
|
+
src/datagokr_reader/workflow.py
|
|
13
|
+
src/datagokr_reader.egg-info/PKG-INFO
|
|
14
|
+
src/datagokr_reader.egg-info/SOURCES.txt
|
|
15
|
+
src/datagokr_reader.egg-info/dependency_links.txt
|
|
16
|
+
src/datagokr_reader.egg-info/entry_points.txt
|
|
17
|
+
src/datagokr_reader.egg-info/requires.txt
|
|
18
|
+
src/datagokr_reader.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
datagokr_reader
|