fix(ecos): correct all 6 ECOS API stat/item codes #task-292
- GDP: 111Y002/10111 -> 902Y015/KOR (international comparative stats) - Unemployment: 901Y027/3 -> 901Y027/I61BC (correct item for rate) - CD rate: 817Y002/010502000 -> 721Y001/2010000 (market interest rates) - CPI: now computes YoY growth from level index (pct_change) - Leading index: monthly (M) fetch + annual average (no annual data available) - Fix DataFrame merge: dedup index, dropna before concat - Fix NaN in scenario Z paths: fallback to z_scenario - Update config.yaml with verified stat codes
This commit is contained in:
15
config.yaml
15
config.yaml
@@ -6,15 +6,14 @@
|
|||||||
ecos:
|
ecos:
|
||||||
api_key: "C5220CGY8FYFDN43B7ON"
|
api_key: "C5220CGY8FYFDN43B7ON"
|
||||||
base_url: "https://ecos.bok.or.kr/api"
|
base_url: "https://ecos.bok.or.kr/api"
|
||||||
# 주요 통계코드
|
# 주요 통계코드 (검증 완료 2026-03-10)
|
||||||
stat_codes:
|
stat_codes:
|
||||||
gdp_growth: "111Y002" # 국내총생산(실질성장률)
|
gdp_growth: "902Y015" # 국제 주요국 경제성장률 / ITEM: KOR
|
||||||
unemployment: "901Y027" # 실업률
|
unemployment: "901Y027" # 경제활동인구 / ITEM: I61BC (실업률)
|
||||||
base_rate: "722Y001" # 한국은행 기준금리
|
base_rate: "722Y001" # 한국은행 기준금리 / ITEM: 0101000
|
||||||
cd_rate: "817Y002" # CD(91일) 금리
|
cd_rate: "721Y001" # 시장금리 / ITEM: 2010000 (CD 91일)
|
||||||
treasury_3y: "817Y002" # 국고채(3년) 수익률
|
cpi: "901Y009" # 소비자물가지수 / ITEM: 0 (총지수, level→YoY% 변환)
|
||||||
cpi: "901Y009" # 소비자물가지수
|
composite_leading: "901Y067" # 경기종합지수 / ITEM: I16A (선행, 월별→연평균)
|
||||||
composite_leading: "901Y067" # 경기선행지수
|
|
||||||
|
|
||||||
# 모형 파라미터
|
# 모형 파라미터
|
||||||
model:
|
model:
|
||||||
|
|||||||
@@ -136,10 +136,10 @@ def collect_macro_data(
|
|||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
# 1) GDP 실질성장률 (%)
|
# 1) GDP 실질성장률 (%)
|
||||||
# 통계표: 111Y002 (국민계정 - 주요지표 - 경제성장률)
|
# 통계표: 902Y015 (국제 주요국 경제성장률) / 항목: KOR
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
logger.info("GDP 성장률 조회 중...")
|
logger.info("GDP 성장률 조회 중...")
|
||||||
df_gdp = api.fetch_stat("111Y002", "A", start, end, "10111")
|
df_gdp = api.fetch_stat("902Y015", "A", start, end, "KOR")
|
||||||
if not df_gdp.empty:
|
if not df_gdp.empty:
|
||||||
gdp_series = df_gdp.set_index("TIME")["DATA_VALUE"].astype(float)
|
gdp_series = df_gdp.set_index("TIME")["DATA_VALUE"].astype(float)
|
||||||
gdp_series.index = gdp_series.index.astype(int)
|
gdp_series.index = gdp_series.index.astype(int)
|
||||||
@@ -148,10 +148,10 @@ def collect_macro_data(
|
|||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
# 2) 실업률 (%)
|
# 2) 실업률 (%)
|
||||||
# 통계표: 901Y027 (고용 - 주요고용지표)
|
# 통계표: 901Y027 (경제활동인구) / 항목: I61BC (실업률)
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
logger.info("실업률 조회 중...")
|
logger.info("실업률 조회 중...")
|
||||||
df_unemp = api.fetch_stat("901Y027", "A", start, end, "3", " ")
|
df_unemp = api.fetch_stat("901Y027", "A", start, end, "I61BC")
|
||||||
if not df_unemp.empty:
|
if not df_unemp.empty:
|
||||||
unemp_series = df_unemp.set_index("TIME")["DATA_VALUE"].astype(float)
|
unemp_series = df_unemp.set_index("TIME")["DATA_VALUE"].astype(float)
|
||||||
unemp_series.index = unemp_series.index.astype(int)
|
unemp_series.index = unemp_series.index.astype(int)
|
||||||
@@ -172,10 +172,10 @@ def collect_macro_data(
|
|||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
# 4) CD(91일) 금리 (%)
|
# 4) CD(91일) 금리 (%)
|
||||||
# 통계표: 817Y002
|
# 통계표: 721Y001 (시장금리) / 항목: 2010000 (CD 91일)
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
logger.info("CD 금리 조회 중...")
|
logger.info("CD 금리 조회 중...")
|
||||||
df_cd = api.fetch_stat("817Y002", "A", start, end, "010502000")
|
df_cd = api.fetch_stat("721Y001", "A", start, end, "2010000")
|
||||||
if not df_cd.empty:
|
if not df_cd.empty:
|
||||||
cd_series = df_cd.set_index("TIME")["DATA_VALUE"].astype(float)
|
cd_series = df_cd.set_index("TIME")["DATA_VALUE"].astype(float)
|
||||||
cd_series.index = cd_series.index.astype(int)
|
cd_series.index = cd_series.index.astype(int)
|
||||||
@@ -184,32 +184,57 @@ def collect_macro_data(
|
|||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
# 5) 소비자물가지수 상승률 (%)
|
# 5) 소비자물가지수 상승률 (%)
|
||||||
# 통계표: 901Y009
|
# 통계표: 901Y009 / 항목: 0 (총지수)
|
||||||
|
# 지수(level)로 조회 후 전년대비 상승률(%) 계산
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
logger.info("소비자물가 상승률 조회 중...")
|
logger.info("소비자물가 상승률 조회 중...")
|
||||||
df_cpi = api.fetch_stat("901Y009", "A", start, end, "0")
|
# 전년도까지 필요 → start를 1년 앞당겨 조회
|
||||||
|
df_cpi = api.fetch_stat("901Y009", "A", str(start_year - 1), end, "0")
|
||||||
if not df_cpi.empty:
|
if not df_cpi.empty:
|
||||||
cpi_series = df_cpi.set_index("TIME")["DATA_VALUE"].astype(float)
|
cpi_level = df_cpi.set_index("TIME")["DATA_VALUE"].astype(float)
|
||||||
cpi_series.index = cpi_series.index.astype(int)
|
cpi_level.index = cpi_level.index.astype(int)
|
||||||
macro_vars["CPI_GROWTH"] = cpi_series
|
cpi_level = cpi_level.sort_index()
|
||||||
|
# 전년대비 증가율 (%)
|
||||||
|
cpi_growth = cpi_level.pct_change() * 100
|
||||||
|
cpi_growth = cpi_growth.loc[start_year:end_year]
|
||||||
|
macro_vars["CPI_GROWTH"] = cpi_growth
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
# 6) 경기선행지수 순환변동치
|
# 6) 경기선행종합지수
|
||||||
# 통계표: 901Y067
|
# 통계표: 901Y067 / 항목: I16A (선행종합지수)
|
||||||
|
# 월별만 존재 → 월별 조회 후 연평균 산출
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
logger.info("경기선행지수 조회 중...")
|
logger.info("경기선행지수 조회 중...")
|
||||||
df_leading = api.fetch_stat("901Y067", "A", start, end, "I16A")
|
df_leading = api.fetch_stat(
|
||||||
|
"901Y067", "M",
|
||||||
|
f"{start_year}01", f"{end_year}12",
|
||||||
|
"I16A"
|
||||||
|
)
|
||||||
if not df_leading.empty:
|
if not df_leading.empty:
|
||||||
leading_series = df_leading.set_index("TIME")["DATA_VALUE"].astype(float)
|
monthly = df_leading[["TIME", "DATA_VALUE"]].copy()
|
||||||
leading_series.index = leading_series.index.astype(int)
|
monthly["DATA_VALUE"] = monthly["DATA_VALUE"].astype(float)
|
||||||
macro_vars["LEADING_INDEX"] = leading_series
|
monthly["YEAR"] = monthly["TIME"].str[:4].astype(int)
|
||||||
|
annual_avg = monthly.groupby("YEAR")["DATA_VALUE"].mean()
|
||||||
|
annual_avg = annual_avg.loc[start_year:end_year]
|
||||||
|
macro_vars["LEADING_INDEX"] = annual_avg
|
||||||
|
|
||||||
# DataFrame 결합
|
# DataFrame 결합 (각 Series의 인덱스를 정리하여 결합)
|
||||||
if macro_vars:
|
if macro_vars:
|
||||||
result = pd.DataFrame(macro_vars)
|
# 각 Series의 인덱스를 정수로 통일, 중복 제거
|
||||||
|
clean_vars = {}
|
||||||
|
for name, series in macro_vars.items():
|
||||||
|
s = series.copy()
|
||||||
|
s.index = s.index.astype(int)
|
||||||
|
s = s[~s.index.duplicated(keep='first')] # 중복 제거
|
||||||
|
s = s.dropna()
|
||||||
|
clean_vars[name] = s
|
||||||
|
|
||||||
|
result = pd.DataFrame(clean_vars)
|
||||||
result.index.name = "YEAR"
|
result.index.name = "YEAR"
|
||||||
result = result.sort_index()
|
result = result.sort_index()
|
||||||
|
|
||||||
|
logger.info(f"ECOS API 데이터 수집 완료: {len(result)}개 연도, {len(result.columns)}개 변수")
|
||||||
return result
|
return result
|
||||||
else:
|
else:
|
||||||
logger.warning("거시경제 데이터 수집 실패. 내장 fallback 데이터 사용.")
|
logger.warning("거시경제 데이터 수집 실패. 내장 fallback 데이터 사용.")
|
||||||
|
|||||||
BIN
doc/ECOS_API/API개발명세서_100대.xls
Normal file
BIN
doc/ECOS_API/API개발명세서_100대.xls
Normal file
Binary file not shown.
BIN
doc/ECOS_API/API개발명세서_서비스통계목록.xls
Normal file
BIN
doc/ECOS_API/API개발명세서_서비스통계목록.xls
Normal file
Binary file not shown.
BIN
doc/ECOS_API/API개발명세서_통계메타DB.xls
Normal file
BIN
doc/ECOS_API/API개발명세서_통계메타DB.xls
Normal file
Binary file not shown.
BIN
doc/ECOS_API/API개발명세서_통계세부항목목록.xls
Normal file
BIN
doc/ECOS_API/API개발명세서_통계세부항목목록.xls
Normal file
Binary file not shown.
BIN
doc/ECOS_API/API개발명세서_통계용어사전.xls
Normal file
BIN
doc/ECOS_API/API개발명세서_통계용어사전.xls
Normal file
Binary file not shown.
BIN
doc/ECOS_API/API개발명세서_통계조회조건.xls
Normal file
BIN
doc/ECOS_API/API개발명세서_통계조회조건.xls
Normal file
Binary file not shown.
BIN
doc/ECOS_API/통계표_10221547.xlsx
Normal file
BIN
doc/ECOS_API/통계표_10221547.xlsx
Normal file
Binary file not shown.
@@ -107,7 +107,8 @@ class ScenarioEngine:
|
|||||||
|
|
||||||
# Phase 1: PIT 기간 (1~pit_horizon년)
|
# Phase 1: PIT 기간 (1~pit_horizon년)
|
||||||
for t in range(min(n_short, self.total_horizon)):
|
for t in range(min(n_short, self.total_horizon)):
|
||||||
z_path[t] = z_short[t] if t < len(z_short) else z_scenario
|
val = z_short[t] if t < len(z_short) else z_scenario
|
||||||
|
z_path[t] = val if np.isfinite(val) else z_scenario
|
||||||
|
|
||||||
# Phase 2: Mean-reversion 기간 (pit_horizon+1 ~ transition_horizon년)
|
# Phase 2: Mean-reversion 기간 (pit_horizon+1 ~ transition_horizon년)
|
||||||
for t in range(self.pit_horizon, min(self.transition_horizon, self.total_horizon)):
|
for t in range(self.pit_horizon, min(self.transition_horizon, self.total_horizon)):
|
||||||
|
|||||||
Reference in New Issue
Block a user