ecos-reader 0.1.0__py3-none-any.whl

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.
ecos/config.py ADDED
@@ -0,0 +1,148 @@
1
+ """
2
+ ecos-reader 설정 관리
3
+
4
+ API Key 및 기본 설정을 관리합니다.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import os
10
+
11
+ from .exceptions import EcosConfigError
12
+
13
+ # dotenv 로드 상태(명시적으로만 로드)
14
+ _dotenv_loaded: bool = False
15
+
16
+ # 전역 API Key 저장소
17
+ _api_key: str | None = None
18
+
19
+
20
+ def load_env(*, dotenv_path: str | None = None, override: bool = False) -> bool:
21
+ """
22
+ .env 파일을 명시적으로 로드합니다.
23
+
24
+ Notes
25
+ -----
26
+ 라이브러리 import 시점에 자동으로 .env를 로드하지 않습니다.
27
+ 필요 시 사용자가 직접 호출하여 환경 변수를 로드할 수 있습니다.
28
+
29
+ Parameters
30
+ ----------
31
+ dotenv_path : str, optional
32
+ .env 파일 경로. 미지정 시 python-dotenv 기본 탐색 규칙을 따릅니다.
33
+ override : bool, optional
34
+ 이미 설정된 환경 변수를 덮어쓸지 여부
35
+
36
+ Returns
37
+ -------
38
+ bool
39
+ .env 파일을 로드했으면 True, 아니면 False
40
+ """
41
+ global _dotenv_loaded
42
+ try:
43
+ from dotenv import load_dotenv
44
+ except ImportError:
45
+ # python-dotenv가 설치되어 있지 않은 경우
46
+ _dotenv_loaded = False
47
+ return False
48
+
49
+ loaded = load_dotenv(dotenv_path=dotenv_path, override=override)
50
+ _dotenv_loaded = bool(loaded)
51
+ return _dotenv_loaded
52
+
53
+
54
+ def set_api_key(api_key: str) -> None:
55
+ """
56
+ API Key를 설정합니다.
57
+
58
+ Parameters
59
+ ----------
60
+ api_key : str
61
+ 한국은행에서 발급받은 ECOS Open API 인증키
62
+
63
+ Examples
64
+ --------
65
+ >>> import ecos
66
+ >>> ecos.set_api_key("your_api_key")
67
+ """
68
+ global _api_key
69
+ if not api_key or not isinstance(api_key, str):
70
+ raise EcosConfigError("API Key는 빈 문자열이 아닌 문자열이어야 합니다.")
71
+ _api_key = api_key
72
+
73
+
74
+ def get_api_key() -> str:
75
+ """
76
+ 현재 설정된 API Key를 반환합니다.
77
+
78
+ 환경 변수 `ECOS_API_KEY`가 설정되어 있으면 해당 값을 사용합니다.
79
+ `set_api_key()`로 직접 설정한 값이 있으면 해당 값을 우선합니다.
80
+ `.env` 파일은 import 시점에 자동으로 로드되지 않습니다(필요 시 `load_env()` 호출).
81
+
82
+ Returns
83
+ -------
84
+ str
85
+ ECOS API 인증키
86
+
87
+ Raises
88
+ ------
89
+ EcosConfigError
90
+ API Key가 설정되지 않은 경우
91
+
92
+ Examples
93
+ --------
94
+ >>> import ecos
95
+ >>> ecos.set_api_key("your_api_key")
96
+ >>> ecos.get_api_key()
97
+ 'your_api_key'
98
+ """
99
+ global _api_key
100
+
101
+ # 직접 설정한 값 우선
102
+ if _api_key is not None:
103
+ return _api_key
104
+
105
+ # 환경 변수에서 로드
106
+ env_key = os.environ.get("ECOS_API_KEY")
107
+ if env_key:
108
+ return env_key
109
+
110
+ raise EcosConfigError(
111
+ "API Key가 설정되지 않았습니다. "
112
+ "환경 변수 ECOS_API_KEY를 설정하거나, "
113
+ "ecos.load_env()로 .env 파일을 로드하거나, "
114
+ "ecos.set_api_key('your_api_key')를 호출하세요. "
115
+ "API Key는 https://ecos.bok.or.kr/api/ 에서 신청할 수 있습니다."
116
+ )
117
+
118
+
119
+ def clear_api_key() -> None:
120
+ """
121
+ 설정된 API Key를 초기화합니다.
122
+
123
+ 주로 테스트 목적으로 사용됩니다.
124
+ """
125
+ global _api_key
126
+ _api_key = None
127
+
128
+
129
+ # 기본 설정값
130
+ class Settings:
131
+ """기본 설정값"""
132
+
133
+ # API 기본값
134
+ BASE_URL: str = "https://ecos.bok.or.kr/api/"
135
+ DEFAULT_FORMAT: str = "json"
136
+ DEFAULT_LANG: str = "kr"
137
+
138
+ # 요청 설정
139
+ DEFAULT_TIMEOUT: int = 30 # 초
140
+ MAX_RETRIES: int = 3
141
+ RETRY_BACKOFF_FACTOR: float = 1.0
142
+
143
+ # 캐시 설정
144
+ CACHE_TTL: int = 3600 # 1시간
145
+ CACHE_MAXSIZE: int = 100
146
+
147
+ # 페이지네이션
148
+ DEFAULT_PAGE_SIZE: int = 10000
ecos/constants.py ADDED
@@ -0,0 +1,155 @@
1
+ """
2
+ ecos-reader 상수 정의
3
+
4
+ ECOS API 통계코드 및 항목코드를 정의합니다.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ # ============================================================================
10
+ # 주기 코드
11
+ # ============================================================================
12
+
13
+ PERIOD_DAILY = "D"
14
+ PERIOD_MONTHLY = "M"
15
+ PERIOD_QUARTERLY = "Q"
16
+ PERIOD_ANNUAL = "A"
17
+
18
+
19
+ # ============================================================================
20
+ # 금리 지표 (interest_rate)
21
+ # ============================================================================
22
+
23
+ # 한국은행 기준금리
24
+ STAT_BASE_RATE = "722Y001"
25
+ ITEM_BASE_RATE = "0101000"
26
+
27
+ # 시장금리 (국고채, 회사채 등)
28
+ STAT_MARKET_RATE = "817Y002"
29
+
30
+ # 국고채 만기별 항목코드
31
+ TREASURY_YIELD_ITEMS: dict[str, str] = {
32
+ "1Y": "010190000", # 국고채 1년
33
+ "3Y": "010200000", # 국고채 3년
34
+ "5Y": "010200001", # 국고채 5년
35
+ "10Y": "010210000", # 국고채 10년
36
+ "20Y": "010220000", # 국고채 20년
37
+ "30Y": "010230000", # 국고채 30년
38
+ }
39
+
40
+
41
+ # ============================================================================
42
+ # 물가 지표 (prices)
43
+ # ============================================================================
44
+
45
+ # 소비자물가지수 (전년동월비)
46
+ STAT_CPI = "901Y009"
47
+ ITEM_CPI_TOTAL = "0" # 총지수
48
+
49
+ # 근원 소비자물가지수
50
+ STAT_CORE_CPI = "901Y010"
51
+ ITEM_CORE_CPI = "AA0000" # 농산물및석유류제외
52
+
53
+ # 생산자물가지수
54
+ STAT_PPI = "404Y014"
55
+ ITEM_PPI_TOTAL = "A00" # 총지수
56
+
57
+
58
+ # ============================================================================
59
+ # 성장 지표 (growth)
60
+ # ============================================================================
61
+
62
+ # GDP (국내총생산)
63
+ STAT_GDP_REAL = "200Y001" # 실질 GDP
64
+ STAT_GDP_NOMINAL = "200Y002" # 명목 GDP
65
+ ITEM_GDP = "10101" # 국내총생산
66
+
67
+ # GDP 디플레이터
68
+ STAT_GDP_DEFLATOR = "200Y004"
69
+ ITEM_GDP_DEFLATOR = "10101"
70
+
71
+
72
+ # ============================================================================
73
+ # 통화 지표 (money)
74
+ # ============================================================================
75
+
76
+ # 통화량
77
+ STAT_MONEY_SUPPLY = "101Y018"
78
+
79
+ # 통화량 항목코드
80
+ MONEY_SUPPLY_ITEMS: dict[str, str] = {
81
+ "M1": "BBGA00", # 협의통화(M1)
82
+ "M2": "BBGS00", # 광의통화(M2)
83
+ "Lf": "BBLA00", # 금융기관유동성(Lf)
84
+ }
85
+
86
+ # 은행 대출
87
+ STAT_BANK_LENDING = "104Y016"
88
+
89
+ # 대출 부문별 항목코드
90
+ BANK_LENDING_ITEMS: dict[str, str] = {
91
+ "all": "BDCA", # 총대출
92
+ "household": "BDCB", # 가계대출
93
+ "corporate": "BDCC", # 기업대출
94
+ }
95
+
96
+
97
+ # ============================================================================
98
+ # 환율 지표 (forex) - Phase 3
99
+ # ============================================================================
100
+
101
+ STAT_EXCHANGE_RATE = "731Y003"
102
+
103
+ # 통화별 항목코드
104
+ EXCHANGE_RATE_ITEMS: dict[str, str] = {
105
+ "USD": "0000001", # 원/달러
106
+ "JPY": "0000002", # 원/100엔
107
+ "EUR": "0000003", # 원/유로
108
+ "CNY": "0000053", # 원/위안
109
+ }
110
+
111
+ # 실효환율
112
+ STAT_EFFECTIVE_RATE = "731Y004"
113
+ ITEM_NEER = "0000001" # 명목실효환율
114
+ ITEM_REER = "0000002" # 실질실효환율
115
+
116
+
117
+ # ============================================================================
118
+ # 국제수지 지표 (bop) - Phase 3
119
+ # ============================================================================
120
+
121
+ STAT_BOP = "301Y017"
122
+ ITEM_CURRENT_ACCOUNT = "CA" # 경상수지
123
+ ITEM_CAPITAL_ACCOUNT = "KA" # 자본수지
124
+
125
+
126
+ # ============================================================================
127
+ # 실물 지표 (real_economy) - Phase 4
128
+ # ============================================================================
129
+
130
+ # 산업생산지수
131
+ STAT_INDUSTRIAL_PRODUCTION = "901Y033"
132
+ ITEM_INDUSTRIAL_PRODUCTION = "A00"
133
+
134
+ # 설비투자지수
135
+ STAT_FACILITY_INVESTMENT = "901Y049"
136
+ ITEM_FACILITY_INVESTMENT = "I16A"
137
+
138
+ # 소매판매지수
139
+ STAT_RETAIL_SALES = "901Y037"
140
+ ITEM_RETAIL_SALES = "*"
141
+
142
+
143
+ # ============================================================================
144
+ # 심리 지표 (sentiment) - Phase 4
145
+ # ============================================================================
146
+
147
+ # 기업경기실사지수 (BSI)
148
+ STAT_BSI = "512Y014"
149
+ ITEM_BSI_MANUFACTURING = "A001"
150
+ ITEM_BSI_NON_MANUFACTURING = "B001"
151
+ ITEM_BSI_ALL = "C001"
152
+
153
+ # 소비자심리지수 (CSI)
154
+ STAT_CSI = "511Y002"
155
+ ITEM_CSI = "FME"
ecos/exceptions.py ADDED
@@ -0,0 +1,73 @@
1
+ """
2
+ ecos-reader 예외 클래스 정의
3
+
4
+ ECOS API 에러 및 라이브러리 예외를 처리합니다.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+
10
+ class EcosError(Exception):
11
+ """ecos-reader 기본 예외"""
12
+
13
+ pass
14
+
15
+
16
+ class EcosAPIError(EcosError):
17
+ """ECOS API 호출 에러
18
+
19
+ Attributes
20
+ ----------
21
+ code : str
22
+ ECOS API 에러 코드
23
+ message : str
24
+ 에러 메시지
25
+ """
26
+
27
+ def __init__(self, code: str, message: str):
28
+ self.code = code
29
+ self.message = message
30
+ super().__init__(f"[{code}] {message}")
31
+
32
+
33
+ class EcosConfigError(EcosError):
34
+ """설정 관련 에러 (API Key 등)"""
35
+
36
+ pass
37
+
38
+
39
+ class EcosNetworkError(EcosError):
40
+ """네트워크 연결 에러"""
41
+
42
+ pass
43
+
44
+
45
+ class EcosRateLimitError(EcosError):
46
+ """Rate Limit 초과"""
47
+
48
+ def __init__(self, message: str, code: str = "602"):
49
+ self.code = code
50
+ self.message = message
51
+ super().__init__(f"[{code}] {message}")
52
+
53
+
54
+ # ECOS 에러 코드 매핑
55
+ ECOS_ERROR_CODES: dict[str, tuple[type[EcosError], str]] = {
56
+ # 정보 코드
57
+ "INFO_100": (EcosConfigError, "인증키가 유효하지 않습니다."),
58
+ "INFO_200": (EcosAPIError, "해당하는 데이터가 없습니다."), # 빈 DataFrame 반환으로 처리
59
+ # 에러 코드
60
+ "ERROR_100": (EcosAPIError, "필수 값이 누락되어 있습니다."),
61
+ "ERROR_101": (EcosAPIError, "주기와 다른 형식의 날짜 형식입니다."),
62
+ "ERROR_200": (EcosAPIError, "파일타입 값이 누락 혹은 유효하지 않습니다."),
63
+ "ERROR_300": (EcosAPIError, "조회건수 값이 누락되어 있습니다."),
64
+ "ERROR_301": (EcosAPIError, "조회건수 값의 타입이 유효하지 않습니다."),
65
+ "ERROR_400": (EcosAPIError, "검색범위가 적정범위를 초과하여 TIMEOUT이 발생하였습니다."),
66
+ "ERROR_500": (EcosAPIError, "서버 오류입니다."), # 재시도 대상
67
+ "ERROR_600": (EcosAPIError, "DB Connection 오류입니다."), # 재시도 대상
68
+ "ERROR_601": (EcosAPIError, "SQL 오류입니다."),
69
+ "ERROR_602": (EcosRateLimitError, "과도한 OpenAPI 호출로 이용이 제한되었습니다."),
70
+ }
71
+
72
+ # 재시도 가능한 에러 코드 (ECOS API는 하이픈 형식 사용: ERROR-500)
73
+ RETRYABLE_ERROR_CODES = {"ERROR-500", "ERROR-600", "ERROR-602"}
@@ -0,0 +1,29 @@
1
+ """
2
+ ecos-reader 지표 모듈
3
+
4
+ 각종 거시경제 지표 조회 함수를 제공합니다.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from .growth import get_gdp, get_gdp_deflator
10
+ from .interest_rate import get_base_rate, get_treasury_yield, get_yield_spread
11
+ from .money import get_bank_lending, get_money_supply
12
+ from .prices import get_core_cpi, get_cpi, get_ppi
13
+
14
+ __all__ = [
15
+ # 금리 지표
16
+ "get_base_rate",
17
+ "get_treasury_yield",
18
+ "get_yield_spread",
19
+ # 물가 지표
20
+ "get_cpi",
21
+ "get_core_cpi",
22
+ "get_ppi",
23
+ # 성장 지표
24
+ "get_gdp",
25
+ "get_gdp_deflator",
26
+ # 통화 지표
27
+ "get_money_supply",
28
+ "get_bank_lending",
29
+ ]
@@ -0,0 +1,186 @@
1
+ """
2
+ 성장 지표 모듈
3
+
4
+ GDP(국내총생산), GDP 디플레이터 등 성장 관련 지표를 조회합니다.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from datetime import datetime
10
+ from typing import Literal
11
+
12
+ import pandas as pd
13
+
14
+ from ..client import get_client
15
+ from ..constants import (
16
+ ITEM_GDP,
17
+ ITEM_GDP_DEFLATOR,
18
+ PERIOD_ANNUAL,
19
+ PERIOD_QUARTERLY,
20
+ STAT_GDP_DEFLATOR,
21
+ STAT_GDP_NOMINAL,
22
+ STAT_GDP_REAL,
23
+ )
24
+ from ..parser import normalize_stat_result, parse_response
25
+
26
+
27
+ def _get_default_dates_quarterly(years_back: int = 5) -> tuple[str, str]:
28
+ """기본 조회 기간을 반환합니다 (분기)."""
29
+ end_date = datetime.now()
30
+ start_year = end_date.year - years_back
31
+ current_quarter = (end_date.month - 1) // 3 + 1
32
+
33
+ start_str = f"{start_year}Q1"
34
+ end_str = f"{end_date.year}Q{current_quarter}"
35
+
36
+ return start_str, end_str
37
+
38
+
39
+ def _get_default_dates_annual(years_back: int = 10) -> tuple[str, str]:
40
+ """기본 조회 기간을 반환합니다 (연간)."""
41
+ end_date = datetime.now()
42
+ start_year = end_date.year - years_back
43
+
44
+ return str(start_year), str(end_date.year)
45
+
46
+
47
+ def get_gdp(
48
+ frequency: Literal["Q", "A"] = "Q",
49
+ basis: Literal["real", "nominal"] = "real",
50
+ start_date: str | None = None,
51
+ end_date: str | None = None,
52
+ ) -> pd.DataFrame:
53
+ """
54
+ 국내총생산(GDP)을 조회합니다.
55
+
56
+ Parameters
57
+ ----------
58
+ frequency : str
59
+ 조회 주기
60
+ - 'Q': 분기 (기본값)
61
+ - 'A': 연간
62
+ basis : str
63
+ GDP 기준
64
+ - 'real': 실질 GDP (기본값)
65
+ - 'nominal': 명목 GDP
66
+ start_date : str, optional
67
+ 조회 시작일
68
+ - 분기: YYYYQN 형식 (예: 2020Q1)
69
+ - 연간: YYYY 형식 (예: 2020)
70
+ end_date : str, optional
71
+ 조회 종료일
72
+
73
+ Returns
74
+ -------
75
+ pd.DataFrame
76
+ 컬럼: date, value, unit
77
+ - date: 날짜 (datetime)
78
+ - value: GDP (조원)
79
+ - unit: 단위
80
+
81
+ Notes
82
+ -----
83
+ - 실질 GDP: 물가 변동을 제외한 실제 생산량 변화
84
+ - 명목 GDP: 당해 연도 가격 기준 GDP
85
+
86
+ Examples
87
+ --------
88
+ >>> import ecos
89
+ >>> df = ecos.get_gdp() # 분기별 실질 GDP
90
+ >>> df.head()
91
+
92
+ >>> df = ecos.get_gdp(frequency="A", basis="nominal") # 연간 명목 GDP
93
+ """
94
+ # 통계코드 선택
95
+ stat_code = STAT_GDP_REAL if basis == "real" else STAT_GDP_NOMINAL
96
+
97
+ # 주기 코드
98
+ period = PERIOD_QUARTERLY if frequency == "Q" else PERIOD_ANNUAL
99
+
100
+ # 기본 날짜 설정
101
+ if start_date is None or end_date is None:
102
+ if frequency == "Q":
103
+ default_start, default_end = _get_default_dates_quarterly(5)
104
+ else:
105
+ default_start, default_end = _get_default_dates_annual(10)
106
+ start_date = start_date or default_start
107
+ end_date = end_date or default_end
108
+
109
+ client = get_client()
110
+ response = client.get_statistic_search(
111
+ stat_code=stat_code,
112
+ period=period,
113
+ start_date=start_date,
114
+ end_date=end_date,
115
+ item_code1=ITEM_GDP,
116
+ )
117
+
118
+ df = parse_response(response)
119
+ return normalize_stat_result(df)
120
+
121
+
122
+ def get_gdp_deflator(
123
+ frequency: Literal["Q", "A"] = "Q",
124
+ start_date: str | None = None,
125
+ end_date: str | None = None,
126
+ ) -> pd.DataFrame:
127
+ """
128
+ GDP 디플레이터를 조회합니다.
129
+
130
+ GDP 디플레이터는 명목 GDP와 실질 GDP의 비율로 계산되는
131
+ 종합 물가지수입니다.
132
+
133
+ Parameters
134
+ ----------
135
+ frequency : str
136
+ 조회 주기
137
+ - 'Q': 분기 (기본값)
138
+ - 'A': 연간
139
+ start_date : str, optional
140
+ 조회 시작일
141
+ end_date : str, optional
142
+ 조회 종료일
143
+
144
+ Returns
145
+ -------
146
+ pd.DataFrame
147
+ 컬럼: date, value, unit
148
+ - date: 날짜 (datetime)
149
+ - value: GDP 디플레이터
150
+ - unit: 단위
151
+
152
+ Notes
153
+ -----
154
+ - GDP 디플레이터 = (명목 GDP / 실질 GDP) × 100
155
+ - CPI보다 포괄적인 물가 지표
156
+ - 국내에서 생산된 모든 재화와 서비스의 가격 변화 반영
157
+
158
+ Examples
159
+ --------
160
+ >>> import ecos
161
+ >>> df = ecos.get_gdp_deflator()
162
+ >>> df.head()
163
+ """
164
+ # 주기 코드
165
+ period = PERIOD_QUARTERLY if frequency == "Q" else PERIOD_ANNUAL
166
+
167
+ # 기본 날짜 설정
168
+ if start_date is None or end_date is None:
169
+ if frequency == "Q":
170
+ default_start, default_end = _get_default_dates_quarterly(5)
171
+ else:
172
+ default_start, default_end = _get_default_dates_annual(10)
173
+ start_date = start_date or default_start
174
+ end_date = end_date or default_end
175
+
176
+ client = get_client()
177
+ response = client.get_statistic_search(
178
+ stat_code=STAT_GDP_DEFLATOR,
179
+ period=period,
180
+ start_date=start_date,
181
+ end_date=end_date,
182
+ item_code1=ITEM_GDP_DEFLATOR,
183
+ )
184
+
185
+ df = parse_response(response)
186
+ return normalize_stat_result(df)