{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "e91f71c8",
   "metadata": {},
   "source": [
    "# 02 분석 — BOKDPM 재추정, 기준선 전망, 시나리오와 산업 외생 6종\n",
    "\n",
    "**계산 경로.**\n",
    "\n",
    "1. **정상상태 정비**: 기준선의 장기 수렴값을 정하는 정상상태 13개를 현재 여건(공식 추정·목표치, 최근 10년 평균)에 맞게 바꾼다(2026-10 정비).\n",
    "2. **재추정**: BOKDPM(70개 방정식)의 행태계수 20개와 구조충격 표준편차 34개를 2000Q1–2025Q4 관측 20변수로 베이지언 추정한다.\n",
    "3. **기준선**: 과거는 칼만 평활값, 미래(2026Q1–2035Q4)는 사후최빈값 모수로 계산한 무충격 전망이다.\n",
    "4. **시나리오 설계**: 인구 가정(저·중·고위)과 감쇠 충격 4종(미국수요 `yus`·유가 `oil`·교역비용 `tcost`·내수 `dshk`)을 레버로 하여 S1–S5를 정한다.\n",
    "5. **거시 블록**: 레버를 캘리브레이션 5식 모형(IS·필립스·테일러·UIP·오쿤)에 넣어 시나리오별 거시경로를 구한다.\n",
    "6. **산업 외생 6종**: 기준선에 시나리오 편차를 더한 뒤 BOKDPM 변수의 관측 가능한 조합(유가·세계수요·실질금리·실질환율·취업자 수·국내 경기)을 만든다. 외생의 정의에는 회귀계수가 없다.\n",
    "7. **투입형 변환**: 적분차수에 맞추어 수준 또는 차분으로 바꾼다. 2026-10 정비에서 실질금리를 1차 차분으로 바꾸었다.\n",
    "\n",
    "**실행 방식.** 기본 실행은 저장된 사후 표본·평활값·전망값을 읽으며, 2–3단계의 Dynare 계산을 다시 하지 않는다. 환경변수 `FULL=1`을 주면 2절과 3절에서 Octave+Dynare로 추정과 전망을 다시 실행한다(약 2시간 15분, Octave 11.1·Dynare 7.1 필요).\n",
    "\n",
    "**산출물.** `output/`의 `steady_state_revision.csv`, `posterior_summary.csv`, `mcmc_diagnostics.csv`, `baseline_forecast_70.csv`, `baseline_annual.csv`, `bokdpm_lgdp_2000_2035.csv`, `scenario_levers.csv`, `scenario_lever_paths.csv`, `dsge_macro_paths.csv`, `dsge_exog_paths.csv`, `exog_levels_2000_2035.csv`, `외생변수_수준_시나리오별(2000-2035).xlsx`, `lever_reach.csv`, `unit_root_real_rate.csv`, `exog_hist_v2.csv`, `exog_scen_v2.csv`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "91326d51",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T00:09:04.696398Z",
     "iopub.status.busy": "2026-10-08T00:09:04.696398Z",
     "iopub.status.idle": "2026-10-08T00:09:05.167537Z",
     "shell.execute_reply": "2026-10-08T00:09:05.167537Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Python 3.12.10 · pandas 2.2.3 · numpy 2.4.6 · scipy 1.18.0 · matplotlib 3.11.0\n",
      "  실행 방식   ONLINE=0 (ECOS·FRED 재수집) · FULL=0 (Dynare 재추정)\n",
      "  ECOS 키     없음 · FRED 키 없음  (ONLINE=0이면 쓰지 않는다)\n",
      "  Octave      있음 · Dynare 있음  (FULL=0이면 쓰지 않는다)\n",
      "  BOKDPM_data_2025.m               있음      23,440 bytes\n",
      "  bokdpm_forecast_2026_2035.csv    있음      56,135 bytes\n",
      "  bokdpm_posterior_draws.csv.gz    있음   5,426,716 bytes\n",
      "  bokdpm_posterior_mode.csv        있음       2,909 bytes\n",
      "  bokdpm_smoothed_2000_2025.csv    있음     142,828 bytes\n",
      "  cpi_kor_1999_2000.csv            있음         146 bytes\n",
      "  ecos_labor_force.csv             있음       2,620 bytes\n",
      "  keis_labor_supply_2024_2034.csv  있음         353 bytes\n",
      "  kosis_생산연령인구.csv                 있음         735 bytes\n"
     ]
    }
   ],
   "source": [
    "import os, time\n",
    "import numpy as np, pandas as pd\n",
    "import config as C\n",
    "import bokdpm_macro as B\n",
    "pd.set_option(\"display.float_format\", lambda v: f\"{v:,.4f}\")\n",
    "pd.set_option(\"display.width\", 160)\n",
    "env = C.setup()\n",
    "lab = pd.read_csv(C.LABOR, index_col=0, encoding=\"utf-8-sig\")\n",
    "pop = pd.read_csv(C.POP, encoding=\"utf-8-sig\")\n",
    "cpi = pd.read_csv(C.CPI, encoding=\"utf-8-sig\")\n",
    "keis = pd.read_csv(C.KEIS, encoding=\"utf-8-sig\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "424f62a6",
   "metadata": {},
   "source": [
    "## 1. 모형 구조\n",
    "\n",
    "BOKDPM은 한국은행 조사국이 개발한 준구조 거시모형이다(모형 파일 머리말: Kang 2008, Pyoun·Park 2009, Kim·Kim 2010). 한국 블록(IS 곡선, 필립스 곡선, 테일러 준칙, 금리 기간구조와 위험프리미엄, 동태적 오쿤 법칙, 경상수지와 환율, 주택가격), 미국 블록(IS, 필립스, 테일러), 유가 방정식으로 이루어진다. 행태계수 20개와 충격 표준편차 34개를 추정하고, 나머지 계수와 정상상태 값은 캘리브레이션한다. 아래 수치는 `dynare/BOKDPM_2025.mod`에서 직접 읽는다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "64e36b8d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T00:09:05.169043Z",
     "iopub.status.busy": "2026-10-08T00:09:05.168041Z",
     "iopub.status.idle": "2026-10-08T00:09:05.177703Z",
     "shell.execute_reply": "2026-10-08T00:09:05.177199Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "estimation(datafile=BOKDPM_data_2025, nobs=104, diffuse_filter, mode_compute=5, mh_replic=30000, mh_nblocks=2, mh_jscale=0.3, mh_drop=0.3)\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>개수</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>내생변수</th>\n",
       "      <td>70</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>방정식</th>\n",
       "      <td>70</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>구조충격</th>\n",
       "      <td>34</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>관측변수</th>\n",
       "      <td>20</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>추정 행태계수</th>\n",
       "      <td>20</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>추정 충격 표준편차</th>\n",
       "      <td>34</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "            개수\n",
       "내생변수        70\n",
       "방정식         70\n",
       "구조충격        34\n",
       "관측변수        20\n",
       "추정 행태계수     20\n",
       "추정 충격 표준편차  34"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mod = B.parse_mod(C.MODFILE)\n",
    "assert mod[\"관측변수\"] == B.VAROBS\n",
    "struct = pd.Series({\"내생변수\": mod[\"내생변수\"], \"방정식\": mod[\"방정식\"], \"구조충격\": mod[\"외생충격\"],\n",
    "                    \"관측변수\": len(mod[\"관측변수\"]),\n",
    "                    \"추정 행태계수\": int((mod[\"사전분포\"][\"구분\"] == \"parameters\").sum()),\n",
    "                    \"추정 충격 표준편차\": int((mod[\"사전분포\"][\"구분\"] == \"shocks_std\").sum())}, name=\"개수\")\n",
    "print(\"estimation(\" + mod[\"추정옵션\"] + \")\")\n",
    "struct.to_frame()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bfc76224",
   "metadata": {},
   "source": [
    "## 1-1. 정상상태(장기 수렴값) 정비\n",
    "\n",
    "모형의 정상상태 값은 무충격 전망이 수렴하는 장기값이며, 관측방정식의 추세(`observation_trends`: 실질GDP·물가·디플레이터·주택가격·미국 GDP·미국 물가·유가)에도 들어간다. 원 모형은 이 값을 2000년대 표본 평균으로 정하였다(파일 주석 \"til '08q3\", \"til '09q4\"). 그 결과 이전 판의 기준선은 잠재성장률 4.29%, 물가목표 3.0%, 유가 상승률 연 9.88%로 수렴하여 2030년대 실질GDP 성장률이 연 4%대, 두바이유가 2035년 배럴당 약 163달러가 되었다.\n",
    "\n",
    "2026-10 정비는 정상상태 13개를 다음 원칙으로 바꾸었다.\n",
    "\n",
    "1. **공식 추정·목표치가 있으면 그 값을 쓴다**: 잠재성장률(한국은행 2025–2029년 연평균 1.8%), 물가목표(2%), 중립금리(한국은행 명목 중간값 2.55% − 물가목표), 미국 잠재성장률(CBO 1.8%), 미국 중립금리(FOMC 장기 연방기금금리 3.2% − 미국 CPI 목표).\n",
    "2. **공식 값이 없으면 물가안정목표 2% 도입 이후 최근 10년(2016Q1–2025Q4) 평균을 쓴다**: 금리 기간프리미엄 3개, 주택가격 상승률, 재정지출 증가율.\n",
    "3. **유가 상승률은 장기 실질유가 불변**(달러 기준 상승률 = 미국 CPI 목표 2.3%)으로 둔다. 2000년대 원유 호황기의 연 9.9%는 장기 수렴값으로 쓸 근거가 없고, 2010–2025년 두바이유는 오히려 하락하였다.\n",
    "\n",
    "정상상태는 선형 모형의 절편이므로 동학 계수(결정규칙)를 바꾸지 않는다. 다만 관측방정식의 추세가 바뀌므로 모수의 사후분포와 평활값이 달라지며, 따라서 Dynare로 다시 추정한다(2절). 아래 표의 이전 값과 새 값은 원본 모형 파일(`dynare/original/`)과 정비한 모형 파일에서 직접 읽어 정비표와 대조한 것이다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "695d1ddd",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T00:09:05.178202Z",
     "iopub.status.busy": "2026-10-08T00:09:05.178202Z",
     "iopub.status.idle": "2026-10-08T00:09:05.188743Z",
     "shell.execute_reply": "2026-10-08T00:09:05.188743Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "최근 10년(2016Q1–2025Q4) 평균 — 정비표 근거값:\n",
      "def_kor_ss        2.0830\n",
      "tprem_kor_ss      0.1840\n",
      "tprem_tb_kor_ss   0.3000\n",
      "tprem_cb_kor_ss   0.6710\n",
      "house_kor_ss      2.3340\n",
      "ge_kor_ss         6.5650\n",
      "참고: CPI 상승률       2.0640\n",
      "참고: 실질GDP 성장률     2.4430\n",
      "참고: 두바이유 상승률      3.1800\n",
      "UNR_kor_ss(3.6083)는 바꾸지 않았다. UNR_kor_BAR가 확률보행이라 이 값은 전망·우도에 영향이 없다. blt_kor_ss·blt_us_ss·s_kor_ss는 0(갭 변수의 정상상태)이다.\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>의미</th>\n",
       "      <th>이전 값</th>\n",
       "      <th>새 값</th>\n",
       "      <th>근거</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>파라미터</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>growth_kor_ss</th>\n",
       "      <td>한국 잠재성장률(%)</td>\n",
       "      <td>4.2925</td>\n",
       "      <td>1.8000</td>\n",
       "      <td>한국은행(2024.12) 「우리 경제의 잠재성장률과 향후 전망」: 2024–2026...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>pietar_kor_ss</th>\n",
       "      <td>한국 물가목표(CPI, %)</td>\n",
       "      <td>3.0000</td>\n",
       "      <td>2.0000</td>\n",
       "      <td>한국은행 물가안정목표 2%(2016년 이후)</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>def_kor_ss</th>\n",
       "      <td>GDP 디플레이터 상승률(%)</td>\n",
       "      <td>2.1943</td>\n",
       "      <td>2.0000</td>\n",
       "      <td>물가목표와 같게(최근 10년 평균 2.08%)</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>rr_kor_bar_ss</th>\n",
       "      <td>한국 중립 실질 정책금리(%)</td>\n",
       "      <td>1.5000</td>\n",
       "      <td>0.5500</td>\n",
       "      <td>한국은행(2024.11) BOK 경제연구 「한국의 중립금리 추정」: 명목 1.8–3...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>tprem_kor_ss</th>\n",
       "      <td>CD(91일) − 콜금리(%p)</td>\n",
       "      <td>0.3900</td>\n",
       "      <td>0.1800</td>\n",
       "      <td>최근 10년 평균</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>tprem_tb_kor_ss</th>\n",
       "      <td>국고채(3년) − 콜금리(%p)</td>\n",
       "      <td>0.7645</td>\n",
       "      <td>0.3000</td>\n",
       "      <td>최근 10년 평균</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>tprem_cb_kor_ss</th>\n",
       "      <td>회사채(3년, AA−) − 국고채(3년)(%p)</td>\n",
       "      <td>0.7419</td>\n",
       "      <td>0.6700</td>\n",
       "      <td>최근 10년 평균</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>house_kor_ss</th>\n",
       "      <td>주택가격 상승률(%)</td>\n",
       "      <td>4.2000</td>\n",
       "      <td>2.3300</td>\n",
       "      <td>최근 10년 평균</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>ge_kor_ss</th>\n",
       "      <td>통합재정 총지출 증가율(%)</td>\n",
       "      <td>7.5885</td>\n",
       "      <td>6.5700</td>\n",
       "      <td>최근 10년 평균</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>growth_us_ss</th>\n",
       "      <td>미국 잠재성장률(%)</td>\n",
       "      <td>2.3000</td>\n",
       "      <td>1.8000</td>\n",
       "      <td>미 의회예산처(CBO, 2025.1) 잠재GDP 증가율 2027–2035년 1.8%...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>pietar_us_ss</th>\n",
       "      <td>미국 물가목표(CPI, %)</td>\n",
       "      <td>2.4982</td>\n",
       "      <td>2.3000</td>\n",
       "      <td>CBO 소비자물가(CPI-U) 장기 전망 2.3%(PCE 목표 2%에 CPI 괴리를...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>rr_us_bar_ss</th>\n",
       "      <td>미국 중립 실질 정책금리(%)</td>\n",
       "      <td>2.0000</td>\n",
       "      <td>0.9000</td>\n",
       "      <td>FOMC 경제전망(2026.9) 장기 연방기금금리 중간값 3.2% − 미국 CPI ...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>oil_g_us_ss</th>\n",
       "      <td>유가 상승률(달러 기준, %)</td>\n",
       "      <td>9.8763</td>\n",
       "      <td>2.3000</td>\n",
       "      <td>장기 실질유가 불변(= 미국 CPI 목표 2.3%)</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                         의미   이전 값    새 값                                                 근거\n",
       "파라미터                                                                                                        \n",
       "growth_kor_ss                   한국 잠재성장률(%) 4.2925 1.8000  한국은행(2024.12) 「우리 경제의 잠재성장률과 향후 전망」: 2024–2026...\n",
       "pietar_kor_ss               한국 물가목표(CPI, %) 3.0000 2.0000                           한국은행 물가안정목표 2%(2016년 이후)\n",
       "def_kor_ss                 GDP 디플레이터 상승률(%) 2.1943 2.0000                          물가목표와 같게(최근 10년 평균 2.08%)\n",
       "rr_kor_bar_ss              한국 중립 실질 정책금리(%) 1.5000 0.5500  한국은행(2024.11) BOK 경제연구 「한국의 중립금리 추정」: 명목 1.8–3...\n",
       "tprem_kor_ss              CD(91일) − 콜금리(%p) 0.3900 0.1800                                          최근 10년 평균\n",
       "tprem_tb_kor_ss           국고채(3년) − 콜금리(%p) 0.7645 0.3000                                          최근 10년 평균\n",
       "tprem_cb_kor_ss  회사채(3년, AA−) − 국고채(3년)(%p) 0.7419 0.6700                                          최근 10년 평균\n",
       "house_kor_ss                    주택가격 상승률(%) 4.2000 2.3300                                          최근 10년 평균\n",
       "ge_kor_ss                   통합재정 총지출 증가율(%) 7.5885 6.5700                                          최근 10년 평균\n",
       "growth_us_ss                    미국 잠재성장률(%) 2.3000 1.8000  미 의회예산처(CBO, 2025.1) 잠재GDP 증가율 2027–2035년 1.8%...\n",
       "pietar_us_ss                미국 물가목표(CPI, %) 2.4982 2.3000  CBO 소비자물가(CPI-U) 장기 전망 2.3%(PCE 목표 2%에 CPI 괴리를...\n",
       "rr_us_bar_ss               미국 중립 실질 정책금리(%) 2.0000 0.9000  FOMC 경제전망(2026.9) 장기 연방기금금리 중간값 3.2% − 미국 CPI ...\n",
       "oil_g_us_ss                유가 상승률(달러 기준, %) 9.8763 2.3000                       장기 실질유가 불변(= 미국 CPI 목표 2.3%)"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ss_tab = B.steady_state_table(C.MODFILE, C.MODFILE_PREV)\n",
    "ss_tab.to_csv(os.path.join(C.OUTPUT, \"steady_state_revision.csv\"), encoding=\"utf-8-sig\")\n",
    "obs = B.read_dynare_data(C.DATAFILE)\n",
    "recent = B.recent_averages(obs)\n",
    "print(\"최근 10년(2016Q1–2025Q4) 평균 — 정비표 근거값:\")\n",
    "print(recent.round(3).to_string())\n",
    "for k in [\"def_kor_ss\", \"tprem_kor_ss\", \"tprem_tb_kor_ss\", \"tprem_cb_kor_ss\", \"house_kor_ss\", \"ge_kor_ss\"]:\n",
    "    if k != \"def_kor_ss\":\n",
    "        assert abs(round(recent[k], 2) - ss_tab.loc[k, \"새 값\"]) < 1e-9, k\n",
    "print(B.SS_UNCHANGED_NOTE)\n",
    "ss_tab"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "56565225",
   "metadata": {},
   "source": [
    "## 2. 베이지언 재추정 — 메트로폴리스–헤이스팅스\n",
    "\n",
    "`estimation` 명령은 확산 칼만필터(`diffuse_filter`)로 우도를 계산하고, 사후최빈값을 찾은 뒤(`mode_compute=5`) 메트로폴리스–헤이스팅스 표본을 2블록 × 30,000개 생성한다(제안분포 배율 0.3). 각 블록의 앞 30%(9,000개)를 버리고 남은 42,000개로 사후 평균·표준편차·90% 최고사후밀도(HPD) 구간을 계산한다. HPD 구간은 정렬한 표본에서 90%를 포함하는 가장 짧은 구간이며, 계산식은 Dynare 7.1의 `posterior_moments.m`과 같다.\n",
    "\n",
    "사전분포로는 충격 표준편차에 평균과 표준편차가 같은 역감마분포, 0과 1 사이 계수에 베타분포, 양수 계수에 감마분포를 둔다. 사전분포와 추정 옵션은 이전 판과 같고 정상상태만 다르다.\n",
    "\n",
    "기본 실행은 저장된 사후 표본(연속 반복을 한 행으로 묶어 저장한 파일)을 읽는다. `FULL=1`이면 Octave에서 `dynare/run_est_local.m`을 실행하여 표본을 새로 만들고 같은 방식으로 추출한다. Dynare의 난수 시드는 기본값(0)으로 고정되어 있다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "c9684882",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T00:09:05.189746Z",
     "iopub.status.busy": "2026-10-08T00:09:05.189746Z",
     "iopub.status.idle": "2026-10-08T00:09:05.387120Z",
     "shell.execute_reply": "2026-10-08T00:09:05.386616Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "FULL=0: 저장된 사후 표본과 평활값을 사용한다. Dynare 추정은 실행하지 않았다.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "사후 표본 42,000개 × 추정 대상 54개, 블록 [1, 2]\n"
     ]
    }
   ],
   "source": [
    "if C.FULL:\n",
    "    info = C.setup(verbose=False)\n",
    "    assert info[\"octave\"] and info[\"dynare\"], \"FULL=1에는 Octave(OCTAVE_CLI)와 Dynare(DYNARE_MATLAB)가 필요하다\"\n",
    "    B.prepare_dynare_workdir(C.DYNARE_WORK, C.DYNARE_SRC, C.DATAFILE)\n",
    "    t0 = time.time()\n",
    "    B.run_octave(\"run_est_local.m\", C.DYNARE_WORK, C.OCTAVE_CLI, C.DYNARE_MATLAB,\n",
    "                 os.path.join(C.OUTPUT, \"estimation_log.txt\"), \"END MOMENTS\")\n",
    "    draws_path = os.path.join(C.INTERIM, \"bokdpm_posterior_draws.csv.gz\")\n",
    "    B.write_exact(B.extract_kept_draws(C.DYNARE_WORK), draws_path, index=False, compression={\"method\": \"gzip\", \"mtime\": 0})\n",
    "    smoothed_path = os.path.join(C.INTERIM, \"bokdpm_smoothed_2000_2025.csv\")\n",
    "    B.write_exact(B.extract_smoothed(os.path.join(C.DYNARE_WORK, \"BOKDPM_2025\", \"Output\", \"BOKDPM_2025_results.mat\")), smoothed_path)\n",
    "    print(f\"Dynare 재추정 완료({(time.time() - t0) / 60:.1f}분). 새 사후 표본과 평활값을 data/interim/에 저장하였다.\")\n",
    "else:\n",
    "    draws_path, smoothed_path = C.DRAWS, C.SMOOTHED\n",
    "    print(\"FULL=0: 저장된 사후 표본과 평활값을 사용한다. Dynare 추정은 실행하지 않았다.\")\n",
    "names, X, blk = B.load_kept_draws(draws_path)\n",
    "print(f\"사후 표본 {X.shape[0]:,}개 × 추정 대상 {X.shape[1]}개, 블록 {sorted(set(blk.tolist()))}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "315dec58",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T00:09:05.388119Z",
     "iopub.status.busy": "2026-10-08T00:09:05.387620Z",
     "iopub.status.idle": "2026-10-08T00:09:05.413132Z",
     "shell.execute_reply": "2026-10-08T00:09:05.413132Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>사전분포</th>\n",
       "      <th>사전평균</th>\n",
       "      <th>사전표준편차</th>\n",
       "      <th>사후평균</th>\n",
       "      <th>표준편차</th>\n",
       "      <th>HPD_low</th>\n",
       "      <th>HPD_high</th>\n",
       "      <th>이전 판 사후평균</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>파라미터</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>gamma_kor1</th>\n",
       "      <td>beta</td>\n",
       "      <td>0.5000</td>\n",
       "      <td>0.1500</td>\n",
       "      <td>0.8415</td>\n",
       "      <td>0.0209</td>\n",
       "      <td>0.8081</td>\n",
       "      <td>0.8757</td>\n",
       "      <td>0.8826</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>gamma_kor3</th>\n",
       "      <td>gamma</td>\n",
       "      <td>0.5000</td>\n",
       "      <td>0.2000</td>\n",
       "      <td>0.4662</td>\n",
       "      <td>0.1180</td>\n",
       "      <td>0.2687</td>\n",
       "      <td>0.6538</td>\n",
       "      <td>0.5744</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>beta_kor1</th>\n",
       "      <td>beta</td>\n",
       "      <td>0.5000</td>\n",
       "      <td>0.1500</td>\n",
       "      <td>0.2112</td>\n",
       "      <td>0.0588</td>\n",
       "      <td>0.1129</td>\n",
       "      <td>0.3057</td>\n",
       "      <td>0.2025</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>beta_kor2</th>\n",
       "      <td>beta</td>\n",
       "      <td>0.1000</td>\n",
       "      <td>0.0700</td>\n",
       "      <td>0.0206</td>\n",
       "      <td>0.0150</td>\n",
       "      <td>0.0008</td>\n",
       "      <td>0.0425</td>\n",
       "      <td>0.0191</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>beta_kor3</th>\n",
       "      <td>gamma</td>\n",
       "      <td>0.1000</td>\n",
       "      <td>0.0500</td>\n",
       "      <td>0.0813</td>\n",
       "      <td>0.0409</td>\n",
       "      <td>0.0184</td>\n",
       "      <td>0.1419</td>\n",
       "      <td>0.0862</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>lambda_kor1</th>\n",
       "      <td>beta</td>\n",
       "      <td>0.5500</td>\n",
       "      <td>0.1500</td>\n",
       "      <td>0.3055</td>\n",
       "      <td>0.1138</td>\n",
       "      <td>0.1283</td>\n",
       "      <td>0.4926</td>\n",
       "      <td>0.3211</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>lambda_kor2</th>\n",
       "      <td>gamma</td>\n",
       "      <td>0.1500</td>\n",
       "      <td>0.0800</td>\n",
       "      <td>0.0834</td>\n",
       "      <td>0.0414</td>\n",
       "      <td>0.0212</td>\n",
       "      <td>0.1429</td>\n",
       "      <td>0.0920</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>lambda_kor3</th>\n",
       "      <td>beta</td>\n",
       "      <td>0.6500</td>\n",
       "      <td>0.1500</td>\n",
       "      <td>0.7656</td>\n",
       "      <td>0.0275</td>\n",
       "      <td>0.7246</td>\n",
       "      <td>0.8133</td>\n",
       "      <td>0.7531</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>alpha_kor1</th>\n",
       "      <td>beta</td>\n",
       "      <td>0.8000</td>\n",
       "      <td>0.1000</td>\n",
       "      <td>0.5791</td>\n",
       "      <td>0.0966</td>\n",
       "      <td>0.4211</td>\n",
       "      <td>0.7380</td>\n",
       "      <td>0.6112</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>alpha_kor2</th>\n",
       "      <td>gamma</td>\n",
       "      <td>0.3000</td>\n",
       "      <td>0.1200</td>\n",
       "      <td>0.0703</td>\n",
       "      <td>0.0146</td>\n",
       "      <td>0.0464</td>\n",
       "      <td>0.0942</td>\n",
       "      <td>0.0724</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>gamma_us1</th>\n",
       "      <td>beta</td>\n",
       "      <td>0.5000</td>\n",
       "      <td>0.1500</td>\n",
       "      <td>0.8870</td>\n",
       "      <td>0.0174</td>\n",
       "      <td>0.8601</td>\n",
       "      <td>0.9171</td>\n",
       "      <td>0.9157</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>gamma_us2</th>\n",
       "      <td>gamma</td>\n",
       "      <td>1.5000</td>\n",
       "      <td>0.2500</td>\n",
       "      <td>1.7214</td>\n",
       "      <td>0.2734</td>\n",
       "      <td>1.2627</td>\n",
       "      <td>2.1533</td>\n",
       "      <td>1.6772</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>gamma_us3</th>\n",
       "      <td>gamma</td>\n",
       "      <td>0.2000</td>\n",
       "      <td>0.1000</td>\n",
       "      <td>0.4619</td>\n",
       "      <td>0.1544</td>\n",
       "      <td>0.2079</td>\n",
       "      <td>0.7073</td>\n",
       "      <td>0.3491</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>beta_us1</th>\n",
       "      <td>beta</td>\n",
       "      <td>0.7000</td>\n",
       "      <td>0.1200</td>\n",
       "      <td>0.4517</td>\n",
       "      <td>0.0578</td>\n",
       "      <td>0.3593</td>\n",
       "      <td>0.5469</td>\n",
       "      <td>0.4464</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>beta_us2</th>\n",
       "      <td>beta</td>\n",
       "      <td>0.1500</td>\n",
       "      <td>0.0800</td>\n",
       "      <td>0.0386</td>\n",
       "      <td>0.0214</td>\n",
       "      <td>0.0064</td>\n",
       "      <td>0.0698</td>\n",
       "      <td>0.0408</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>lambda_us1</th>\n",
       "      <td>beta</td>\n",
       "      <td>0.4500</td>\n",
       "      <td>0.1500</td>\n",
       "      <td>0.3776</td>\n",
       "      <td>0.0980</td>\n",
       "      <td>0.2195</td>\n",
       "      <td>0.5430</td>\n",
       "      <td>0.3400</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>lambda_us2</th>\n",
       "      <td>gamma</td>\n",
       "      <td>0.0500</td>\n",
       "      <td>0.0300</td>\n",
       "      <td>0.0905</td>\n",
       "      <td>0.0238</td>\n",
       "      <td>0.0495</td>\n",
       "      <td>0.1277</td>\n",
       "      <td>0.0765</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>lambda_us3</th>\n",
       "      <td>beta</td>\n",
       "      <td>0.5000</td>\n",
       "      <td>0.1500</td>\n",
       "      <td>0.4313</td>\n",
       "      <td>0.0372</td>\n",
       "      <td>0.3681</td>\n",
       "      <td>0.4895</td>\n",
       "      <td>0.4386</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>rho_oil1</th>\n",
       "      <td>beta</td>\n",
       "      <td>0.9000</td>\n",
       "      <td>0.0500</td>\n",
       "      <td>0.8795</td>\n",
       "      <td>0.0493</td>\n",
       "      <td>0.7979</td>\n",
       "      <td>0.9585</td>\n",
       "      <td>0.8812</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>gamma_kor2</th>\n",
       "      <td>gamma</td>\n",
       "      <td>1.5000</td>\n",
       "      <td>0.1000</td>\n",
       "      <td>1.5395</td>\n",
       "      <td>0.1009</td>\n",
       "      <td>1.3678</td>\n",
       "      <td>1.7021</td>\n",
       "      <td>1.5953</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "              사전분포   사전평균  사전표준편차   사후평균   표준편차  HPD_low  HPD_high  이전 판 사후평균\n",
       "파라미터                                                                         \n",
       "gamma_kor1    beta 0.5000  0.1500 0.8415 0.0209   0.8081    0.8757     0.8826\n",
       "gamma_kor3   gamma 0.5000  0.2000 0.4662 0.1180   0.2687    0.6538     0.5744\n",
       "beta_kor1     beta 0.5000  0.1500 0.2112 0.0588   0.1129    0.3057     0.2025\n",
       "beta_kor2     beta 0.1000  0.0700 0.0206 0.0150   0.0008    0.0425     0.0191\n",
       "beta_kor3    gamma 0.1000  0.0500 0.0813 0.0409   0.0184    0.1419     0.0862\n",
       "lambda_kor1   beta 0.5500  0.1500 0.3055 0.1138   0.1283    0.4926     0.3211\n",
       "lambda_kor2  gamma 0.1500  0.0800 0.0834 0.0414   0.0212    0.1429     0.0920\n",
       "lambda_kor3   beta 0.6500  0.1500 0.7656 0.0275   0.7246    0.8133     0.7531\n",
       "alpha_kor1    beta 0.8000  0.1000 0.5791 0.0966   0.4211    0.7380     0.6112\n",
       "alpha_kor2   gamma 0.3000  0.1200 0.0703 0.0146   0.0464    0.0942     0.0724\n",
       "gamma_us1     beta 0.5000  0.1500 0.8870 0.0174   0.8601    0.9171     0.9157\n",
       "gamma_us2    gamma 1.5000  0.2500 1.7214 0.2734   1.2627    2.1533     1.6772\n",
       "gamma_us3    gamma 0.2000  0.1000 0.4619 0.1544   0.2079    0.7073     0.3491\n",
       "beta_us1      beta 0.7000  0.1200 0.4517 0.0578   0.3593    0.5469     0.4464\n",
       "beta_us2      beta 0.1500  0.0800 0.0386 0.0214   0.0064    0.0698     0.0408\n",
       "lambda_us1    beta 0.4500  0.1500 0.3776 0.0980   0.2195    0.5430     0.3400\n",
       "lambda_us2   gamma 0.0500  0.0300 0.0905 0.0238   0.0495    0.1277     0.0765\n",
       "lambda_us3    beta 0.5000  0.1500 0.4313 0.0372   0.3681    0.4895     0.4386\n",
       "rho_oil1      beta 0.9000  0.0500 0.8795 0.0493   0.7979    0.9585     0.8812\n",
       "gamma_kor2   gamma 1.5000  0.1000 1.5395 0.1009   1.3678    1.7021     1.5953"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "post = B.posterior_summary(names, X, mod[\"사전분포\"], C.MH_CONF)\n",
    "post.to_csv(os.path.join(C.OUTPUT, \"posterior_summary.csv\"), index=False, encoding=\"utf-8-sig\")\n",
    "prev = pd.read_csv(C.REF_PREV[\"estimation_results\"]).set_index(\"파라미터\")[\"사후평균\"]\n",
    "t = post[post[\"구분\"] == \"parameters\"].drop(columns=\"구분\").set_index(\"파라미터\")\n",
    "t[\"이전 판 사후평균\"] = prev.reindex(t.index)\n",
    "t"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7f88be59",
   "metadata": {},
   "source": [
    "### 수렴 진단\n",
    "\n",
    "두 블록의 이동 비율(유지 표본에서 직전 값과 달라진 비율로, 채택률의 근사치이다)과 Gelman–Rubin 잠재척도축소계수(PSRF)를 계산한다. PSRF가 1.1을 넘으면 두 블록의 표본 분포가 아직 충분히 겹치지 않았다는 뜻이다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "dba706f3",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T00:09:05.414139Z",
     "iopub.status.busy": "2026-10-08T00:09:05.414139Z",
     "iopub.status.idle": "2026-10-08T00:09:05.432799Z",
     "shell.execute_reply": "2026-10-08T00:09:05.432295Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "이동비율_블록1 0.254 · 이동비율_블록2 0.265\n",
      "PSRF > 1.1: 3개 · 행태계수 20개의 최대 PSRF 1.006\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>PSRF</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>파라미터</th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>RES_S_G_kor</th>\n",
       "      <td>1.8460</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RES_RTB_kor_BAR</th>\n",
       "      <td>1.3486</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RES_LS_kor_BAR</th>\n",
       "      <td>1.3439</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RES_UNR_kor_BAR</th>\n",
       "      <td>1.0407</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RES_PIE_E_kor4</th>\n",
       "      <td>1.0387</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RES_S_kor</th>\n",
       "      <td>1.0352</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                  PSRF\n",
       "파라미터                  \n",
       "RES_S_G_kor     1.8460\n",
       "RES_RTB_kor_BAR 1.3486\n",
       "RES_LS_kor_BAR  1.3439\n",
       "RES_UNR_kor_BAR 1.0407\n",
       "RES_PIE_E_kor4  1.0387\n",
       "RES_S_kor       1.0352"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "diag = B.mcmc_diagnostics(names, X, blk)\n",
    "diag.to_csv(os.path.join(C.OUTPUT, \"mcmc_diagnostics.csv\"), index=False, encoding=\"utf-8-sig\")\n",
    "is_par = diag[\"파라미터\"].isin(post.loc[post[\"구분\"] == \"parameters\", \"파라미터\"])\n",
    "print(\" · \".join(f\"{k} {v:.3f}\" for k, v in diag.attrs.items()))\n",
    "print(f\"PSRF > 1.1: {int((diag['PSRF'] > 1.1).sum())}개 · 행태계수 20개의 최대 PSRF {diag.loc[is_par, 'PSRF'].max():.3f}\")\n",
    "diag.sort_values(\"PSRF\", ascending=False).head(6).set_index(\"파라미터\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b159bf57",
   "metadata": {},
   "source": [
    "정비판에서 PSRF가 1.1을 넘는 모수는 모두 구조충격 표준편차이며(환율 추세·추세 증가율, 국고채 실질 중립금리 추세 등 확률보행 추세의 충격), 행태계수 20개는 모두 1.01 미만이다.\n",
    "추세 성분의 충격 크기는 관측자료로 잘 식별되지 않으므로 두 블록의 표본이 덜 겹친다. 이 모수들의 사후분포 요약은 해석에 주의가 필요하다. 기준선은 사후최빈값 모수의 전망이므로 이 진단의 영향을 받지 않는다."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "95f8d922",
   "metadata": {},
   "source": [
    "## 3. 기준선 전망(2026Q1–2035Q4)\n",
    "\n",
    "기준선은 표본 끝(2025Q4)의 상태에서 이후 충격을 0으로 두고 계산한 40분기 전망이다. Dynare는 `mh_replic=0, forecast=40`으로 사후최빈값을 다시 찾은 뒤 그 모수로 전망한다(`dynare/BOKDPM_forecast.mod`). 따라서 기준선은 사후평균이 아니라 사후최빈값 모수의 전망이다. 전망 실행에서 찾은 최빈값은 추정 실행의 최빈값과 같다(아래 출력).\n",
    "\n",
    "정비한 정상상태에서 기준선의 실질GDP 성장률은 잠재성장률 1.8%로, CPI 상승률은 물가목표 2%로, 두바이유는 장기 실질가격 불변 경로로 수렴한다. 아래 표는 이전 판 기준선과 나란히 보인다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "7c20632a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T00:09:05.433799Z",
     "iopub.status.busy": "2026-10-08T00:09:05.433301Z",
     "iopub.status.idle": "2026-10-08T00:09:05.459752Z",
     "shell.execute_reply": "2026-10-08T00:09:05.459247Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "FULL=0: 저장된 기준선 전망을 사용한다.\n",
      "사후최빈값: 추정 실행과 전망 실행의 최대 차이 0.00e+00\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead tr th {\n",
       "        text-align: left;\n",
       "    }\n",
       "\n",
       "    .dataframe thead tr:last-of-type th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr>\n",
       "      <th></th>\n",
       "      <th colspan=\"4\" halign=\"left\">정비판</th>\n",
       "      <th colspan=\"4\" halign=\"left\">이전 판</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th></th>\n",
       "      <th>실질GDP 성장률(%)</th>\n",
       "      <th>CPI 상승률(%)</th>\n",
       "      <th>콜금리(%)</th>\n",
       "      <th>두바이유(달러/배럴)</th>\n",
       "      <th>실질GDP 성장률(%)</th>\n",
       "      <th>CPI 상승률(%)</th>\n",
       "      <th>콜금리(%)</th>\n",
       "      <th>두바이유(달러/배럴)</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>연도</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>2024</th>\n",
       "      <td>1.5634</td>\n",
       "      <td>2.3217</td>\n",
       "      <td>3.3958</td>\n",
       "      <td>80.9908</td>\n",
       "      <td>1.5634</td>\n",
       "      <td>2.3217</td>\n",
       "      <td>3.3958</td>\n",
       "      <td>80.9908</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2025</th>\n",
       "      <td>1.8322</td>\n",
       "      <td>2.1231</td>\n",
       "      <td>2.6042</td>\n",
       "      <td>69.3915</td>\n",
       "      <td>1.8322</td>\n",
       "      <td>2.1231</td>\n",
       "      <td>2.6042</td>\n",
       "      <td>69.3915</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2026</th>\n",
       "      <td>2.1919</td>\n",
       "      <td>1.7174</td>\n",
       "      <td>2.4212</td>\n",
       "      <td>63.8591</td>\n",
       "      <td>2.3932</td>\n",
       "      <td>2.1489</td>\n",
       "      <td>2.7790</td>\n",
       "      <td>66.7794</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2027</th>\n",
       "      <td>1.7264</td>\n",
       "      <td>1.7910</td>\n",
       "      <td>2.2395</td>\n",
       "      <td>65.6162</td>\n",
       "      <td>3.1539</td>\n",
       "      <td>2.7141</td>\n",
       "      <td>3.2426</td>\n",
       "      <td>74.0007</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2028</th>\n",
       "      <td>2.2396</td>\n",
       "      <td>1.8713</td>\n",
       "      <td>2.2652</td>\n",
       "      <td>67.2367</td>\n",
       "      <td>3.9166</td>\n",
       "      <td>2.9400</td>\n",
       "      <td>3.7132</td>\n",
       "      <td>81.8466</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2029</th>\n",
       "      <td>1.9029</td>\n",
       "      <td>1.9153</td>\n",
       "      <td>2.3016</td>\n",
       "      <td>68.7818</td>\n",
       "      <td>3.7975</td>\n",
       "      <td>3.0118</td>\n",
       "      <td>4.0074</td>\n",
       "      <td>90.3642</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2030</th>\n",
       "      <td>1.8909</td>\n",
       "      <td>1.9222</td>\n",
       "      <td>2.3300</td>\n",
       "      <td>70.3598</td>\n",
       "      <td>4.0035</td>\n",
       "      <td>3.0026</td>\n",
       "      <td>4.1692</td>\n",
       "      <td>99.7441</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2031</th>\n",
       "      <td>1.8602</td>\n",
       "      <td>1.9275</td>\n",
       "      <td>2.3505</td>\n",
       "      <td>71.9832</td>\n",
       "      <td>4.1305</td>\n",
       "      <td>2.9890</td>\n",
       "      <td>4.2544</td>\n",
       "      <td>110.0958</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2032</th>\n",
       "      <td>1.8423</td>\n",
       "      <td>1.9311</td>\n",
       "      <td>2.3638</td>\n",
       "      <td>73.6503</td>\n",
       "      <td>4.2180</td>\n",
       "      <td>2.9797</td>\n",
       "      <td>4.2977</td>\n",
       "      <td>121.5222</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2033</th>\n",
       "      <td>1.8322</td>\n",
       "      <td>1.9330</td>\n",
       "      <td>2.3718</td>\n",
       "      <td>75.3598</td>\n",
       "      <td>4.2764</td>\n",
       "      <td>2.9740</td>\n",
       "      <td>4.3191</td>\n",
       "      <td>134.1352</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2034</th>\n",
       "      <td>1.8262</td>\n",
       "      <td>1.9338</td>\n",
       "      <td>2.3763</td>\n",
       "      <td>77.1109</td>\n",
       "      <td>4.3146</td>\n",
       "      <td>2.9707</td>\n",
       "      <td>4.3292</td>\n",
       "      <td>148.0578</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2035</th>\n",
       "      <td>1.8226</td>\n",
       "      <td>1.9342</td>\n",
       "      <td>2.3788</td>\n",
       "      <td>78.9038</td>\n",
       "      <td>4.3394</td>\n",
       "      <td>2.9688</td>\n",
       "      <td>4.3337</td>\n",
       "      <td>163.4261</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "              정비판                                       이전 판                              \n",
       "     실질GDP 성장률(%) CPI 상승률(%) 콜금리(%) 두바이유(달러/배럴) 실질GDP 성장률(%) CPI 상승률(%) 콜금리(%) 두바이유(달러/배럴)\n",
       "연도                                                                                        \n",
       "2024       1.5634     2.3217 3.3958     80.9908       1.5634     2.3217 3.3958     80.9908\n",
       "2025       1.8322     2.1231 2.6042     69.3915       1.8322     2.1231 2.6042     69.3915\n",
       "2026       2.1919     1.7174 2.4212     63.8591       2.3932     2.1489 2.7790     66.7794\n",
       "2027       1.7264     1.7910 2.2395     65.6162       3.1539     2.7141 3.2426     74.0007\n",
       "2028       2.2396     1.8713 2.2652     67.2367       3.9166     2.9400 3.7132     81.8466\n",
       "2029       1.9029     1.9153 2.3016     68.7818       3.7975     3.0118 4.0074     90.3642\n",
       "2030       1.8909     1.9222 2.3300     70.3598       4.0035     3.0026 4.1692     99.7441\n",
       "2031       1.8602     1.9275 2.3505     71.9832       4.1305     2.9890 4.2544    110.0958\n",
       "2032       1.8423     1.9311 2.3638     73.6503       4.2180     2.9797 4.2977    121.5222\n",
       "2033       1.8322     1.9330 2.3718     75.3598       4.2764     2.9740 4.3191    134.1352\n",
       "2034       1.8262     1.9338 2.3763     77.1109       4.3146     2.9707 4.3292    148.0578\n",
       "2035       1.8226     1.9342 2.3788     78.9038       4.3394     2.9688 4.3337    163.4261"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "if C.FULL:\n",
    "    t0 = time.time()\n",
    "    B.run_octave(\"run_forecast.m\", C.DYNARE_WORK, C.OCTAVE_CLI, C.DYNARE_MATLAB,\n",
    "                 os.path.join(C.OUTPUT, \"forecast_log.txt\"), \"FORECAST_DONE\")\n",
    "    out_dir = lambda f: os.path.join(C.DYNARE_WORK, f, \"Output\", f\"{f}_results.mat\")\n",
    "    fc = B.extract_forecast(out_dir(\"BOKDPM_forecast\"))\n",
    "    mode = pd.DataFrame({\"추정 실행(MH)\": B.extract_posterior_mode(out_dir(\"BOKDPM_2025\")),\n",
    "                         \"전망 실행(mh_replic=0)\": B.extract_posterior_mode(out_dir(\"BOKDPM_forecast\"))})\n",
    "    print(f\"Dynare 전망 완료({(time.time() - t0) / 60:.1f}분)\")\n",
    "else:\n",
    "    fc = B.read_exact(C.FORECAST, index_col=0)\n",
    "    mode = B.read_exact(C.MODE, index_col=0)\n",
    "    print(\"FULL=0: 저장된 기준선 전망을 사용한다.\")\n",
    "sm = B.read_exact(smoothed_path, index_col=0)\n",
    "B.write_exact(fc, os.path.join(C.OUTPUT, \"baseline_forecast_70.csv\"))\n",
    "print(f\"사후최빈값: 추정 실행과 전망 실행의 최대 차이 {(mode.iloc[:, 0] - mode.iloc[:, 1]).abs().max():.2e}\")\n",
    "ann = B.annual_baseline(sm, fc)\n",
    "ann.to_csv(os.path.join(C.OUTPUT, \"baseline_annual.csv\"), encoding=\"utf-8-sig\")\n",
    "# 5장 통제 총량의 기준선: 실질GDP(100·ln) 과거 평활값 + 기준선 전망\n",
    "pd.concat([sm[\"LGDP_kor\"], fc[\"LGDP_kor\"]]).rename(\"LGDP_kor\").rename_axis(\"분기\").to_frame().pipe(\n",
    "    B.write_exact, os.path.join(C.OUTPUT, \"bokdpm_lgdp_2000_2035.csv\"))\n",
    "ann_prev = pd.read_csv(C.REF_PREV[\"baseline_annual\"], index_col=0, encoding=\"utf-8-sig\")\n",
    "cols = [\"실질GDP 성장률(%)\", \"CPI 상승률(%)\", \"콜금리(%)\", \"두바이유(달러/배럴)\"]\n",
    "pd.concat({\"정비판\": ann[cols], \"이전 판\": ann_prev[cols]}, axis=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3b913ee2",
   "metadata": {},
   "source": [
    "평활한 잠재성장률(`G_kor`)은 표본 초 연 4%대에서 표본 끝 2%대로 낮아지는 경로이며, 한국은행의 기간별 잠재성장률 추정(2000년대 초 5% 안팎 → 2020년대 2% 안팎)과 같은 방향이다. 정비한 모형은 이 하락을 표본 안의 충격으로 설명하고, 전망에서는 1.8%로 수렴시킨다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "3d2f4d82",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T00:09:05.460251Z",
     "iopub.status.busy": "2026-10-08T00:09:05.460251Z",
     "iopub.status.idle": "2026-10-08T00:09:05.463912Z",
     "shell.execute_reply": "2026-10-08T00:09:05.463912Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "평활 잠재성장률 G_kor(연율, %) — 5년 평균:\n",
      "2000   4.1600\n",
      "2005   3.5800\n",
      "2010   2.8100\n",
      "2015   2.6600\n",
      "2020   2.2800\n",
      "2025   2.1000\n",
      "2025Q4 2.08% → 전망 2035Q4 1.80%\n"
     ]
    }
   ],
   "source": [
    "g = sm[\"G_kor\"]\n",
    "print(\"평활 잠재성장률 G_kor(연율, %) — 5년 평균:\")\n",
    "yr = pd.Series([int(q[:4]) for q in g.index], index=g.index)\n",
    "print(g.groupby((yr // 5) * 5).mean().round(2).to_string())\n",
    "print(f\"2025Q4 {g.iloc[-1]:.2f}% → 전망 2035Q4 {fc['G_kor'].iloc[-1]:.2f}%\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a23b2ce7",
   "metadata": {},
   "source": [
    "## 4. 시나리오 설계 — 레버 5개\n",
    "\n",
    "다섯 시나리오는 노동공급(인구 가정)과 대외·대내 환경(감쇠 충격 4종)의 조합이다. 모든 충격은 2026Q1에 최고치로 시작하여 $k$분기에 걸쳐 직선으로 0이 된다.\n",
    "\n",
    "$$\\mathrm{dec}(k)_t = \\min\\{\\max(1 - t/k,\\ 0),\\ 1\\}, \\qquad t = 0\\,(2026\\mathrm{Q1}),\\ 1,\\ \\dots$$\n",
    "\n",
    "S1(기준)은 충격이 없고 중위 인구를 쓴다. 인구 가정은 노동공급을 통해 영구적인 수준 차이를 만들고, 충격은 감쇠하여 사라진다. 레버의 크기와 감쇠 기간은 이전 판과 같다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "740f99d7",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T00:09:05.464917Z",
     "iopub.status.busy": "2026-10-08T00:09:05.464917Z",
     "iopub.status.idle": "2026-10-08T00:09:05.471962Z",
     "shell.execute_reply": "2026-10-08T00:09:05.471962Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>인구(생산연령인구 추계)</th>\n",
       "      <th>미국수요 yus</th>\n",
       "      <th>유가 oil</th>\n",
       "      <th>교역비용 tcost</th>\n",
       "      <th>내수 dshk</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>시나리오</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>S1 기준</th>\n",
       "      <td>중위</td>\n",
       "      <td>—</td>\n",
       "      <td>—</td>\n",
       "      <td>—</td>\n",
       "      <td>—</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>S2 공급제약+충격</th>\n",
       "      <td>저위</td>\n",
       "      <td>-1.2 (12분기)</td>\n",
       "      <td>-8.0 (8분기)</td>\n",
       "      <td>+1.5 (12분기)</td>\n",
       "      <td>-1.0 (12분기)</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>S3 제약완화</th>\n",
       "      <td>고위</td>\n",
       "      <td>+0.6 (12분기)</td>\n",
       "      <td>-3.0 (8분기)</td>\n",
       "      <td>-1.0 (12분기)</td>\n",
       "      <td>+0.5 (12분기)</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>S4 기술도약</th>\n",
       "      <td>중위</td>\n",
       "      <td>+0.8 (20분기)</td>\n",
       "      <td>—</td>\n",
       "      <td>—</td>\n",
       "      <td>+0.6 (28분기)</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>S5 지정학</th>\n",
       "      <td>중위</td>\n",
       "      <td>-0.6 (10분기)</td>\n",
       "      <td>+15.0 (8분기)</td>\n",
       "      <td>+2.0 (10분기)</td>\n",
       "      <td>-0.8 (10분기)</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "           인구(생산연령인구 추계)     미국수요 yus       유가 oil   교역비용 tcost      내수 dshk\n",
       "시나리오                                                                        \n",
       "S1 기준                 중위            —            —            —            —\n",
       "S2 공급제약+충격            저위  -1.2 (12분기)   -8.0 (8분기)  +1.5 (12분기)  -1.0 (12분기)\n",
       "S3 제약완화               고위  +0.6 (12분기)   -3.0 (8분기)  -1.0 (12분기)  +0.5 (12분기)\n",
       "S4 기술도약               중위  +0.8 (20분기)            —            —  +0.6 (28분기)\n",
       "S5 지정학                중위  -0.6 (10분기)  +15.0 (8분기)  +2.0 (10분기)  -0.8 (10분기)"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "levers = B.lever_table()\n",
    "levers.to_csv(os.path.join(C.OUTPUT, \"scenario_levers.csv\"), encoding=\"utf-8-sig\")\n",
    "qs = B.quarters(\"2026Q1\", B.H)\n",
    "paths = pd.concat([pd.DataFrame(B.scenario_levers(s, B.H), index=qs).assign(시나리오=s) for s in B.SCENARIOS])\n",
    "paths.index.name = \"분기\"\n",
    "paths.reset_index()[[\"시나리오\", \"분기\", \"yus\", \"oil\", \"tcost\", \"dshk\"]].to_csv(\n",
    "    os.path.join(C.OUTPUT, \"scenario_lever_paths.csv\"), index=False, encoding=\"utf-8-sig\")\n",
    "levers"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "39b74699",
   "metadata": {},
   "source": [
    "## 5. 거시 블록 — 레버에서 거시경로로\n",
    "\n",
    "레버는 BOKDPM 한국 블록의 핵심 행태식을 옮긴 캘리브레이션 모형에 들어간다(원본 `repo/ch4/code/ch4_dsge.py`). 이 블록은 1–3절에서 추정한 BOKDPM(Dynare)과 별개의 결정론적 모형이며, 계수는 고정값이다. 블록의 출력은 시나리오 사이의 **차이**(각 시나리오 − S1)로만 쓰이므로 $\\bar r$, $\\pi^*$ 같은 수준 상수는 결과에 영향을 주지 않는다.\n",
    "\n",
    "| 식 | 형태 |\n",
    "|---|---|\n",
    "| IS | $Y_t = 0.5Y_{t-1} + 0.1Y_{t+1} - 0.1(RS_{t-1} - \\pi^* - \\bar r) + 0.3\\,(yus_t - 0.75\\,tcost_t) - 0.01\\,oil_t + dshk_t$ |\n",
    "| 필립스 | $\\pi_t = 0.65\\pi^* + 0.35\\,[0.55\\pi_{t+1} + 0.45\\pi_{t-1} + 0.6\\,Y_{t-1}]$ |\n",
    "| 테일러 | $RS_t = 0.5RS_{t-1} + 0.5\\,[\\bar r + \\pi^* + 1.5(\\pi_{t+1} - \\pi^*) + 2\\,Y_t]$ |\n",
    "| UIP(실질환율갭) | $S_t = 0.9\\,[0.5S_{t+1} + 0.5S_{t-1} - 0.25(RS_t - \\pi^* - \\bar r)] - 0.2Y_t$ |\n",
    "| 오쿤(실업갭) | $U_t = 0.8U_{t-1} - 0.3Y_t$ |\n",
    "\n",
    "전향 항이 있으므로 64분기(40분기 + 여유 24분기) 구간에서 시간 순서대로 반복 대입하여 수렴할 때까지 푼다(Fair–Taylor 방식).\n",
    "\n",
    "**2026-10 정비 — 레버 전달 경로 두 가지.**\n",
    "\n",
    "- **교역비용 `tcost`**: 원 블록에서는 교역량 증가율 $trd\\_g = 1 + 0.8\\,yus - 0.6\\,tcost$에만 들어갔고, 교역량이 외생에서 제외되어 어디에도 전달되지 않았다. 설계 교역량 식의 계수비를 그대로 써서, 교역비용 1단위를 미국수요 0.75단위(= 0.6/0.8)의 감소로 보고 IS 식의 해외수요 항을 실효 해외수요 $yus - 0.75\\,tcost$로 바꾸었다. 새로 추정한 계수는 없다. 교역비용은 산출갭을 거쳐 실질금리·실질환율·실업률·국내 경기에 전달된다.\n",
    "- **고용 증가율 `lab_g`**: 원 블록은 생산연령인구 증가율 + 참가율 추세 − Δ실업갭으로 계산하여(10년 누적 약 −11%), 외생 '취업자 수'가 쓰는 경제활동인구와 다른 노동공급을 가정하였다. 정비판은 외생과 같은 노동공급(경제활동인구 기준 경로 × 인구 레버)에서 Δ실업갭을 빼서 계산한다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "b9008a22",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T00:09:05.473091Z",
     "iopub.status.busy": "2026-10-08T00:09:05.473091Z",
     "iopub.status.idle": "2026-10-08T00:09:05.530907Z",
     "shell.execute_reply": "2026-10-08T00:09:05.530907Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>2030Q1 산출갭(%)</th>\n",
       "      <th>10년 누적 고용 증가율(%)</th>\n",
       "      <th>이전 판 10년 누적 고용(%)</th>\n",
       "      <th>10년 실질금리 변화(%p)</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>S1 기준</th>\n",
       "      <td>0.0000</td>\n",
       "      <td>-0.1690</td>\n",
       "      <td>-10.9160</td>\n",
       "      <td>0.0000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>S2 공급제약+충격</th>\n",
       "      <td>0.0130</td>\n",
       "      <td>-1.9250</td>\n",
       "      <td>-12.6550</td>\n",
       "      <td>2.1670</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>S3 제약완화</th>\n",
       "      <td>-0.0070</td>\n",
       "      <td>1.4920</td>\n",
       "      <td>-9.2380</td>\n",
       "      <td>-1.2550</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>S4 기술도약</th>\n",
       "      <td>0.4630</td>\n",
       "      <td>-0.1710</td>\n",
       "      <td>-10.9000</td>\n",
       "      <td>-1.1280</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>S5 지정학</th>\n",
       "      <td>-0.0000</td>\n",
       "      <td>-0.1870</td>\n",
       "      <td>-10.9190</td>\n",
       "      <td>2.1100</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "            2030Q1 산출갭(%)  10년 누적 고용 증가율(%)  이전 판 10년 누적 고용(%)  10년 실질금리 변화(%p)\n",
       "S1 기준              0.0000           -0.1690           -10.9160           0.0000\n",
       "S2 공급제약+충격         0.0130           -1.9250           -12.6550           2.1670\n",
       "S3 제약완화           -0.0070            1.4920            -9.2380          -1.2550\n",
       "S4 기술도약            0.4630           -0.1710           -10.9000          -1.1280\n",
       "S5 지정학            -0.0000           -0.1870           -10.9190           2.1100"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "lf_all = B.labor_force_base(lab, keis)\n",
    "lf41 = lf_all[-(B.H + 1):]                               # 2025Q4–2035Q4\n",
    "ex, mac = B.dsge_paths(pop, lf41)\n",
    "ex.to_csv(os.path.join(C.OUTPUT, \"dsge_exog_paths.csv\"), index=False, encoding=\"utf-8-sig\")\n",
    "mac.to_csv(os.path.join(C.OUTPUT, \"dsge_macro_paths.csv\"), index=False, encoding=\"utf-8-sig\")\n",
    "# 원본 처리와 같이, 기록한 파일을 다시 읽어 다음 단계에 넘긴다\n",
    "ex = pd.read_csv(os.path.join(C.OUTPUT, \"dsge_exog_paths.csv\"), encoding=\"utf-8-sig\")\n",
    "mac = pd.read_csv(os.path.join(C.OUTPUT, \"dsge_macro_paths.csv\"), encoding=\"utf-8-sig\")\n",
    "ex_prev = pd.read_csv(C.REF_PREV[\"dsge_exog_paths\"], encoding=\"utf-8-sig\")\n",
    "summ = pd.DataFrame({\"2030Q1 산출갭(%)\": mac[mac[\"분기\"] == \"2030Q1\"].set_index(\"시나리오\")[\"산출갭Y\"],\n",
    "                     \"10년 누적 고용 증가율(%)\": ex.groupby(\"시나리오\")[\"lab_g\"].sum(),\n",
    "                     \"이전 판 10년 누적 고용(%)\": ex_prev.groupby(\"시나리오\")[\"lab_g\"].sum(),\n",
    "                     \"10년 실질금리 변화(%p)\": ex.groupby(\"시나리오\")[\"d_rrate\"].sum()})\n",
    "summ.index = [f\"{s} {B.SCN_KO[s]}\" for s in summ.index]\n",
    "summ"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7d51918f",
   "metadata": {},
   "source": [
    "## 6. 산업 외생 6종 — 관측 가능한 조합\n",
    "\n",
    "산업 VARX의 외생변수는 BOKDPM 변수를 가감승제로 조합한 값이며, 결과가 관측 가능한 거시지표가 되도록 정의한다. 잠재 갭·추세(`_GAP`, `_BAR`)를 그대로 쓰지 않고, 회귀로 추정한 계수도 쓰지 않는다.\n",
    "\n",
    "| 외생 | 산식 | 산업모형 투입 형태 |\n",
    "|---|---|---|\n",
    "| 유가 | `LOIL_us` = 100·ln(두바이유, 달러/배럴) | 1차 차분 |\n",
    "| 세계수요 | `Y_us` = 100·ln(미국 실질GDP/잠재GDP) | 수준 |\n",
    "| 실질금리 | `CB_kor` − (`LCPI_kor` − `LCPI_kor`₋₄) = 회사채(3년, AA−) 수익률 − 전년동기 CPI 상승률 | **1차 차분**(정비) |\n",
    "| 실질환율 | `LS_kor` + `LCPI_us` − `LCPI_kor` = 로그 실질 원/달러 환율 | 1차 차분 |\n",
    "| 취업자 수 | 경제활동인구 × (1 − `UNR_kor`/100), 천 명 | 로그 차분(×100) |\n",
    "| 국내 경기 | `Y_kor` = 한국 산출갭 | 수준 |\n",
    "\n",
    "실질금리의 명목금리는 정책금리가 아니라 **회사채(3년, AA−) 수익률**이다. BOKDPM IS 곡선의 실질금리 항이 회사채 실질금리(`RCB_kor`)이고, 산업의 자금조달 비용을 나타내기에 정책금리보다 적합하다. 이전 판의 일부 문서(용어 툴팁)는 '명목 정책금리 − 전년동기 CPI 상승률'로 적었으나 계산은 처음부터 회사채 수익률이었다.\n",
    "\n",
    "**과거(2000Q1–2025Q4)** 는 추정 결과의 평활값으로 만든다. 관측변수의 평활값은 자료와 같으므로 과거 외생은 실측치와 정합한다. **미래(2026Q1–2035Q4)** 는 기준선(BOKDPM 전망)에 시나리오 편차(각 시나리오 − S1)를 더한다.\n",
    "\n",
    "| 외생 | 시나리오 편차 |\n",
    "|---|---|\n",
    "| 유가 | 거시 블록 유가 증가율 편차의 누적합 $\\sum_{\\tau \\le t}(oil\\_g_{s,\\tau} - oil\\_g_{S1,\\tau})$. 일시적 충격이 영구적인 수준 차이로 남는다(6장 보완 과제) |\n",
    "| 세계수요 | 레버 `yus`를 그대로 더한다. **정비: S3(+0.6, 12분기)를 포함**하여 모든 시나리오가 레버표를 따른다(이전 판은 S2·S4·S5만) |\n",
    "| 실질금리 | 거시 블록 실질금리 $RR$의 차이 |\n",
    "| 실질환율 | 거시 블록 실질환율갭 $S$의 차이 |\n",
    "| 취업자 수 | 경제활동인구 × 인구 레버 × (1 − (기준선 실업률 + 실업갭 차이)/100). **정비: 인구 레버를 곱한다** |\n",
    "| 국내 경기 | 거시 블록 산출갭 $Y$의 차이 |\n",
    "\n",
    "**경제활동인구 기준 경로(정비).** 관측 구간(2026Q2까지) 이후는 중장기 인력수급 전망의 기간별 증감을 로그증가율로 바꾸어 잇는다. 2024년 관측 평균을 기준으로 2024→2029년 +34.6만 명, 2029→2034년 −21.0만 명이며, 2035년은 후기 증가율을 잇는다. **인구 레버**는 저위·고위 추계와 중위 추계의 분기 증가율 차이를 2026Q1부터 누적한 값으로, 2025Q4에는 모든 시나리오가 같다.\n",
    "\n",
    "2000년 4개 분기의 실질금리는 1999년 물가가 있어야 계산되므로, 원본 BOKDPM 워크북의 1999–2000년 소비자물가지수로 구한 전년동기 상승률(소수 둘째 자리)을 회사채 수익률에서 뺀다(2026-09-02 보정)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "d9f2a832",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T00:09:05.531911Z",
     "iopub.status.busy": "2026-10-08T00:09:05.531911Z",
     "iopub.status.idle": "2026-10-08T00:09:05.755601Z",
     "shell.execute_reply": "2026-10-08T00:09:05.755601Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2000년 실질금리(%): [8.1736, 8.2946, 6.1046, 5.9111]\n",
      "검산 2025Q4: 경제활동인구 × (1 − 실업률) = 28,800.8천 명 · 관측 취업자 28,792천 명\n",
      "취업자 수 S1: 2029Q4 − 2024Q4 +413천 명(인력수급 전망 2024→2029 +367) · 2034Q4 − 2029Q4 -203천 명(전망 2029→2034 -303)\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>유가</th>\n",
       "      <th>두바이유(달러)</th>\n",
       "      <th>세계수요</th>\n",
       "      <th>실질금리</th>\n",
       "      <th>실질환율</th>\n",
       "      <th>취업자 수</th>\n",
       "      <th>국내 경기</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>시나리오</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>S1</th>\n",
       "      <td>437.6830</td>\n",
       "      <td>79.5853</td>\n",
       "      <td>-0.0016</td>\n",
       "      <td>1.3132</td>\n",
       "      <td>833.4609</td>\n",
       "      <td>28,762.7493</td>\n",
       "      <td>-0.0888</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>S2</th>\n",
       "      <td>419.6830</td>\n",
       "      <td>66.4753</td>\n",
       "      <td>-0.0016</td>\n",
       "      <td>1.3132</td>\n",
       "      <td>833.4609</td>\n",
       "      <td>28,267.5356</td>\n",
       "      <td>-0.0888</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>S3</th>\n",
       "      <td>430.9330</td>\n",
       "      <td>74.3906</td>\n",
       "      <td>-0.0016</td>\n",
       "      <td>1.3132</td>\n",
       "      <td>833.4609</td>\n",
       "      <td>29,249.7006</td>\n",
       "      <td>-0.0888</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>S4</th>\n",
       "      <td>437.6830</td>\n",
       "      <td>79.5853</td>\n",
       "      <td>-0.0016</td>\n",
       "      <td>1.3132</td>\n",
       "      <td>833.4589</td>\n",
       "      <td>28,767.7823</td>\n",
       "      <td>-0.0888</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>S5</th>\n",
       "      <td>471.4330</td>\n",
       "      <td>111.5341</td>\n",
       "      <td>-0.0016</td>\n",
       "      <td>1.3132</td>\n",
       "      <td>833.4609</td>\n",
       "      <td>28,762.4533</td>\n",
       "      <td>-0.0888</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "           유가  두바이유(달러)    세계수요   실질금리     실질환율       취업자 수   국내 경기\n",
       "시나리오                                                               \n",
       "S1   437.6830   79.5853 -0.0016 1.3132 833.4609 28,762.7493 -0.0888\n",
       "S2   419.6830   66.4753 -0.0016 1.3132 833.4609 28,267.5356 -0.0888\n",
       "S3   430.9330   74.3906 -0.0016 1.3132 833.4609 29,249.7006 -0.0888\n",
       "S4   437.6830   79.5853 -0.0016 1.3132 833.4589 28,767.7823 -0.0888\n",
       "S5   471.4330  111.5341 -0.0016 1.3132 833.4609 28,762.4533 -0.0888"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "lev = B.build_exog_levels(sm, fc, lab, keis, pop, mac, ex, cpi)\n",
    "lev.to_csv(os.path.join(C.OUTPUT, \"exog_levels_2000_2035.csv\"), index=False, encoding=\"utf-8-sig\")\n",
    "lev = pd.read_csv(os.path.join(C.OUTPUT, \"exog_levels_2000_2035.csv\"), encoding=\"utf-8-sig\")\n",
    "B.write_levels_xlsx(lev, os.path.join(C.OUTPUT, \"외생변수_수준_시나리오별(2000-2035).xlsx\"))\n",
    "print(\"2000년 실질금리(%):\", lev[(lev[\"시나리오\"] == \"S1\") & (lev[\"분기\"] < \"2001Q1\")][\"실질금리\"].tolist())\n",
    "emp = lev[(lev[\"시나리오\"] == \"S1\") & (lev[\"분기\"] == \"2025Q4\")][\"고용_취업자\"].iloc[0]\n",
    "print(f\"검산 2025Q4: 경제활동인구 × (1 − 실업률) = {emp:,.1f}천 명 · 관측 취업자 {lab.loc['2025Q4', '취업자']:,.0f}천 명\")\n",
    "e = lev[lev[\"시나리오\"] == \"S1\"].set_index(\"분기\")[\"고용_취업자\"]\n",
    "k = keis.set_index(\"구간\")[\"취업자_증감_천명\"]\n",
    "print(f\"취업자 수 S1: 2029Q4 − 2024Q4 {e['2029Q4'] - e['2024Q4']:+,.0f}천 명(인력수급 전망 2024→2029 {k['2024-2029']:+,.0f}) · \"\n",
    "      f\"2034Q4 − 2029Q4 {e['2034Q4'] - e['2029Q4']:+,.0f}천 명(전망 2029→2034 {k['2029-2034']:+,.0f})\")\n",
    "t = lev[lev[\"분기\"] == \"2035Q4\"].set_index(\"시나리오\")[B.EXOG_COLS].rename(columns=B.EXOG_LABEL)\n",
    "t.insert(1, \"두바이유(달러)\", np.exp(t[\"유가\"] / 100))\n",
    "t"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "22b69b4e",
   "metadata": {},
   "source": [
    "### 레버 전달 점검\n",
    "\n",
    "레버 하나씩을 끄고(인구는 모든 시나리오를 중위로, 충격은 0으로) 외생 6종을 다시 만든 뒤, 2026–2035년의 최대 변화폭을 잰다. 값이 0이면 그 레버는 외생 6종에 전달되지 않는다. 이전 판에서는 인구와 교역비용 두 레버의 행이 모두 0이었다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "12ea2038",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T00:09:05.756606Z",
     "iopub.status.busy": "2026-10-08T00:09:05.756606Z",
     "iopub.status.idle": "2026-10-08T00:09:06.030869Z",
     "shell.execute_reply": "2026-10-08T00:09:06.030869Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "전달되지 않는 레버: 없음\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>유가</th>\n",
       "      <th>세계수요</th>\n",
       "      <th>실질금리</th>\n",
       "      <th>실질환율</th>\n",
       "      <th>취업자 수</th>\n",
       "      <th>국내 경기</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>끈 레버</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>인구</th>\n",
       "      <td>0.0000</td>\n",
       "      <td>0.0000</td>\n",
       "      <td>0.0000</td>\n",
       "      <td>0.0000</td>\n",
       "      <td>494.6216</td>\n",
       "      <td>0.0000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>yus</th>\n",
       "      <td>0.0000</td>\n",
       "      <td>1.2000</td>\n",
       "      <td>0.9590</td>\n",
       "      <td>2.5030</td>\n",
       "      <td>138.5692</td>\n",
       "      <td>0.5390</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>oil</th>\n",
       "      <td>33.7500</td>\n",
       "      <td>0.0000</td>\n",
       "      <td>0.3660</td>\n",
       "      <td>0.8620</td>\n",
       "      <td>50.1373</td>\n",
       "      <td>0.2160</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>tcost</th>\n",
       "      <td>0.0000</td>\n",
       "      <td>0.0000</td>\n",
       "      <td>1.1450</td>\n",
       "      <td>2.9020</td>\n",
       "      <td>162.8720</td>\n",
       "      <td>0.6630</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>dshk</th>\n",
       "      <td>0.0000</td>\n",
       "      <td>0.0000</td>\n",
       "      <td>2.6640</td>\n",
       "      <td>6.9540</td>\n",
       "      <td>384.9143</td>\n",
       "      <td>1.4960</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "           유가   세계수요   실질금리   실질환율    취업자 수  국내 경기\n",
       "끈 레버                                              \n",
       "인구     0.0000 0.0000 0.0000 0.0000 494.6216 0.0000\n",
       "yus    0.0000 1.2000 0.9590 2.5030 138.5692 0.5390\n",
       "oil   33.7500 0.0000 0.3660 0.8620  50.1373 0.2160\n",
       "tcost  0.0000 0.0000 1.1450 2.9020 162.8720 0.6630\n",
       "dshk   0.0000 0.0000 2.6640 6.9540 384.9143 1.4960"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "reach = B.lever_reach(pop, sm, fc, lab, keis, cpi, lev)\n",
    "reach.to_csv(os.path.join(C.OUTPUT, \"lever_reach.csv\"), encoding=\"utf-8-sig\")\n",
    "none = [k for k, r in reach.iterrows() if (r == 0).all()]\n",
    "print(\"전달되지 않는 레버:\", none if none else \"없음\")\n",
    "assert not none, \"외생 6종에 전달되지 않는 레버가 있다\"\n",
    "reach.rename(columns=B.EXOG_LABEL)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a94e2553",
   "metadata": {},
   "source": [
    "## 7. 산업모형 투입형 — 적분차수에 맞춘 변환\n",
    "\n",
    "산업 VARX의 내생변수는 분기 성장률(100·Δlog 부가가치, 4장에서 선택한 차분 명세)이다. 성장률 식에 들어가는 외생은 정상(I(0))이어야 계수가 과거의 추세 공행을 담지 않는다. 그래서 정상 변수(세계수요·국내 경기 = 산출갭)는 수준으로, 단위근이나 추세를 지닌 변수(유가·실질환율·취업자 수)는 차분으로 넣는다.\n",
    "\n",
    "**실질금리 — 수준에서 1차 차분으로(2026-10 정비).** 이전 판은 실질금리를 정상 변수로 보고 수준으로 넣었다. 그러나 아래 검정에서 실질금리 수준은 상수항만 둔 정상성 귀무가설(KPSS)이 1% 수준에서 기각되고, DF-GLS는 단위근을 기각하지 못하며, 상수항과 추세를 함께 두면 추세 정상으로 판정된다. 즉 실질금리는 표본 기간(2000–2025) 동안 하락 추세를 지닌 계열이다. 같은 기간 산업 성장률도 낮아졌으므로, 수준을 성장률 식에 넣으면 두 추세의 공행이 계수에 담겨 '실질금리가 높을수록 성장률이 높다'는 양(+)의 계수가 나온다(6장 진단의 기여 1위·이론과 반대 부호). 1차 차분은 모든 검정에서 정상이며, 성장률(차분) 식과 적분차수가 맞는다. 차분 투입에서 실질금리의 효과는 금리가 **변하는 동안**의 성장률 변화로 나타나고, 금리 수준이 높게 유지된다고 성장률이 계속 높아지지는 않는다.\n",
    "\n",
    "각 시나리오의 첫 분기(2000Q1)는 차분값이 없으므로 다음 분기 값으로 채운다(원본 처리). 과거 구간은 S1 경로를 공통으로 쓰고, 미래 구간은 시나리오별로 저장한다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "47abcf39",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T00:09:06.031871Z",
     "iopub.status.busy": "2026-10-08T00:09:06.031871Z",
     "iopub.status.idle": "2026-10-08T00:09:07.138896Z",
     "shell.execute_reply": "2026-10-08T00:09:07.138392Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style type=\"text/css\">\n",
       "</style>\n",
       "<table id=\"T_37b0e\">\n",
       "  <thead>\n",
       "    <tr>\n",
       "      <th class=\"blank\" >&nbsp;</th>\n",
       "      <th class=\"blank level0\" >&nbsp;</th>\n",
       "      <th id=\"T_37b0e_level0_col0\" class=\"col_heading level0 col0\" >관측수</th>\n",
       "      <th id=\"T_37b0e_level0_col1\" class=\"col_heading level0 col1\" >ADF p</th>\n",
       "      <th id=\"T_37b0e_level0_col2\" class=\"col_heading level0 col2\" >PP p</th>\n",
       "      <th id=\"T_37b0e_level0_col3\" class=\"col_heading level0 col3\" >DF-GLS p</th>\n",
       "      <th id=\"T_37b0e_level0_col4\" class=\"col_heading level0 col4\" >KPSS p</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th class=\"index_name level0\" >계열</th>\n",
       "      <th class=\"index_name level1\" >결정항</th>\n",
       "      <th class=\"blank col0\" >&nbsp;</th>\n",
       "      <th class=\"blank col1\" >&nbsp;</th>\n",
       "      <th class=\"blank col2\" >&nbsp;</th>\n",
       "      <th class=\"blank col3\" >&nbsp;</th>\n",
       "      <th class=\"blank col4\" >&nbsp;</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th id=\"T_37b0e_level0_row0\" class=\"row_heading level0 row0\" rowspan=\"2\">실질금리(수준)</th>\n",
       "      <th id=\"T_37b0e_level1_row0\" class=\"row_heading level1 row0\" >c</th>\n",
       "      <td id=\"T_37b0e_row0_col0\" class=\"data row0 col0\" >104</td>\n",
       "      <td id=\"T_37b0e_row0_col1\" class=\"data row0 col1\" >0.000</td>\n",
       "      <td id=\"T_37b0e_row0_col2\" class=\"data row0 col2\" >0.000</td>\n",
       "      <td id=\"T_37b0e_row0_col3\" class=\"data row0 col3\" >0.516</td>\n",
       "      <td id=\"T_37b0e_row0_col4\" class=\"data row0 col4\" >0.001</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_37b0e_level1_row1\" class=\"row_heading level1 row1\" >ct</th>\n",
       "      <td id=\"T_37b0e_row1_col0\" class=\"data row1 col0\" >104</td>\n",
       "      <td id=\"T_37b0e_row1_col1\" class=\"data row1 col1\" >0.010</td>\n",
       "      <td id=\"T_37b0e_row1_col2\" class=\"data row1 col2\" >0.002</td>\n",
       "      <td id=\"T_37b0e_row1_col3\" class=\"data row1 col3\" >0.501</td>\n",
       "      <td id=\"T_37b0e_row1_col4\" class=\"data row1 col4\" >0.188</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_37b0e_level0_row2\" class=\"row_heading level0 row2\" rowspan=\"2\">실질금리(1차 차분)</th>\n",
       "      <th id=\"T_37b0e_level1_row2\" class=\"row_heading level1 row2\" >c</th>\n",
       "      <td id=\"T_37b0e_row2_col0\" class=\"data row2 col0\" >103</td>\n",
       "      <td id=\"T_37b0e_row2_col1\" class=\"data row2 col1\" >0.000</td>\n",
       "      <td id=\"T_37b0e_row2_col2\" class=\"data row2 col2\" >0.000</td>\n",
       "      <td id=\"T_37b0e_row2_col3\" class=\"data row2 col3\" >0.000</td>\n",
       "      <td id=\"T_37b0e_row2_col4\" class=\"data row2 col4\" >0.283</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_37b0e_level1_row3\" class=\"row_heading level1 row3\" >ct</th>\n",
       "      <td id=\"T_37b0e_row3_col0\" class=\"data row3 col0\" >103</td>\n",
       "      <td id=\"T_37b0e_row3_col1\" class=\"data row3 col1\" >0.011</td>\n",
       "      <td id=\"T_37b0e_row3_col2\" class=\"data row3 col2\" >0.000</td>\n",
       "      <td id=\"T_37b0e_row3_col3\" class=\"data row3 col3\" >0.000</td>\n",
       "      <td id=\"T_37b0e_row3_col4\" class=\"data row3 col4\" >0.436</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_37b0e_level0_row4\" class=\"row_heading level0 row4\" rowspan=\"2\">실질금리(수준, 2001Q1~)</th>\n",
       "      <th id=\"T_37b0e_level1_row4\" class=\"row_heading level1 row4\" >c</th>\n",
       "      <td id=\"T_37b0e_row4_col0\" class=\"data row4 col0\" >100</td>\n",
       "      <td id=\"T_37b0e_row4_col1\" class=\"data row4 col1\" >0.038</td>\n",
       "      <td id=\"T_37b0e_row4_col2\" class=\"data row4 col2\" >0.064</td>\n",
       "      <td id=\"T_37b0e_row4_col3\" class=\"data row4 col3\" >0.083</td>\n",
       "      <td id=\"T_37b0e_row4_col4\" class=\"data row4 col4\" >0.002</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_37b0e_level1_row5\" class=\"row_heading level1 row5\" >ct</th>\n",
       "      <td id=\"T_37b0e_row5_col0\" class=\"data row5 col0\" >100</td>\n",
       "      <td id=\"T_37b0e_row5_col1\" class=\"data row5 col1\" >0.000</td>\n",
       "      <td id=\"T_37b0e_row5_col2\" class=\"data row5 col2\" >0.084</td>\n",
       "      <td id=\"T_37b0e_row5_col3\" class=\"data row5 col3\" >0.000</td>\n",
       "      <td id=\"T_37b0e_row5_col4\" class=\"data row5 col4\" >0.683</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n"
      ],
      "text/plain": [
       "<pandas.io.formats.style.Styler at 0x20b4ad92cc0>"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "h1 = lev[(lev[\"시나리오\"] == \"S1\") & (lev[\"분기\"] < \"2026Q1\")].set_index(\"분기\")\n",
    "ur = B.unit_root_battery({\"실질금리(수준)\": h1[\"실질금리\"], \"실질금리(1차 차분)\": h1[\"실질금리\"].diff(),\n",
    "                          \"실질금리(수준, 2001Q1~)\": h1.loc[\"2001Q1\":, \"실질금리\"]})\n",
    "ur.to_csv(os.path.join(C.OUTPUT, \"unit_root_real_rate.csv\"), encoding=\"utf-8-sig\")\n",
    "ur.style.format({c: \"{:.3f}\" for c in ur.columns if c != \"관측수\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "858b2889",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T00:09:07.139395Z",
     "iopub.status.busy": "2026-10-08T00:09:07.139395Z",
     "iopub.status.idle": "2026-10-08T00:09:07.156165Z",
     "shell.execute_reply": "2026-10-08T00:09:07.156165Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "과거 104분기(2000Q1–2025Q4) · 미래 40분기 × 5개 시나리오\n",
      "투입 형태: 유가 1차 차분 · 세계수요 수준 · 실질금리 1차 차분 · 실질환율 1차 차분 · 취업자 수 로그 차분(×100) · 국내 경기 수준\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<style type=\"text/css\">\n",
       "</style>\n",
       "<table id=\"T_3c264\">\n",
       "  <caption>미래 40분기 평균(투입형)</caption>\n",
       "  <thead>\n",
       "    <tr>\n",
       "      <th class=\"blank level0\" >&nbsp;</th>\n",
       "      <th id=\"T_3c264_level0_col0\" class=\"col_heading level0 col0\" >유가</th>\n",
       "      <th id=\"T_3c264_level0_col1\" class=\"col_heading level0 col1\" >세계수요</th>\n",
       "      <th id=\"T_3c264_level0_col2\" class=\"col_heading level0 col2\" >실질금리</th>\n",
       "      <th id=\"T_3c264_level0_col3\" class=\"col_heading level0 col3\" >실질환율</th>\n",
       "      <th id=\"T_3c264_level0_col4\" class=\"col_heading level0 col4\" >취업자 수</th>\n",
       "      <th id=\"T_3c264_level0_col5\" class=\"col_heading level0 col5\" >국내 경기</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th id=\"T_3c264_level0_row0\" class=\"row_heading level0 row0\" >S1 기준</th>\n",
       "      <td id=\"T_3c264_row0_col0\" class=\"data row0 col0\" >0.561</td>\n",
       "      <td id=\"T_3c264_row0_col1\" class=\"data row0 col1\" >0.153</td>\n",
       "      <td id=\"T_3c264_row0_col2\" class=\"data row0 col2\" >0.009</td>\n",
       "      <td id=\"T_3c264_row0_col3\" class=\"data row0 col3\" >0.104</td>\n",
       "      <td id=\"T_3c264_row0_col4\" class=\"data row0 col4\" >-0.003</td>\n",
       "      <td id=\"T_3c264_row0_col5\" class=\"data row0 col5\" >-0.150</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_3c264_level0_row1\" class=\"row_heading level0 row1\" >S2 공급제약+충격</th>\n",
       "      <td id=\"T_3c264_row1_col0\" class=\"data row1 col0\" >0.111</td>\n",
       "      <td id=\"T_3c264_row1_col1\" class=\"data row1 col1\" >-0.042</td>\n",
       "      <td id=\"T_3c264_row1_col2\" class=\"data row1 col2\" >0.009</td>\n",
       "      <td id=\"T_3c264_row1_col3\" class=\"data row1 col3\" >0.104</td>\n",
       "      <td id=\"T_3c264_row1_col4\" class=\"data row1 col4\" >-0.047</td>\n",
       "      <td id=\"T_3c264_row1_col5\" class=\"data row1 col5\" >-0.555</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_3c264_level0_row2\" class=\"row_heading level0 row2\" >S3 제약완화</th>\n",
       "      <td id=\"T_3c264_row2_col0\" class=\"data row2 col0\" >0.392</td>\n",
       "      <td id=\"T_3c264_row2_col1\" class=\"data row2 col1\" >0.250</td>\n",
       "      <td id=\"T_3c264_row2_col2\" class=\"data row2 col2\" >0.009</td>\n",
       "      <td id=\"T_3c264_row2_col3\" class=\"data row2 col3\" >0.104</td>\n",
       "      <td id=\"T_3c264_row2_col4\" class=\"data row2 col4\" >0.039</td>\n",
       "      <td id=\"T_3c264_row2_col5\" class=\"data row2 col5\" >0.078</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_3c264_level0_row3\" class=\"row_heading level0 row3\" >S4 기술도약</th>\n",
       "      <td id=\"T_3c264_row3_col0\" class=\"data row3 col0\" >0.561</td>\n",
       "      <td id=\"T_3c264_row3_col1\" class=\"data row3 col1\" >0.363</td>\n",
       "      <td id=\"T_3c264_row3_col2\" class=\"data row3 col2\" >0.009</td>\n",
       "      <td id=\"T_3c264_row3_col3\" class=\"data row3 col3\" >0.104</td>\n",
       "      <td id=\"T_3c264_row3_col4\" class=\"data row3 col4\" >-0.003</td>\n",
       "      <td id=\"T_3c264_row3_col5\" class=\"data row3 col5\" >0.279</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_3c264_level0_row4\" class=\"row_heading level0 row4\" >S5 지정학</th>\n",
       "      <td id=\"T_3c264_row4_col0\" class=\"data row4 col0\" >1.404</td>\n",
       "      <td id=\"T_3c264_row4_col1\" class=\"data row4 col1\" >0.070</td>\n",
       "      <td id=\"T_3c264_row4_col2\" class=\"data row4 col2\" >0.009</td>\n",
       "      <td id=\"T_3c264_row4_col3\" class=\"data row4 col3\" >0.104</td>\n",
       "      <td id=\"T_3c264_row4_col4\" class=\"data row4 col4\" >-0.003</td>\n",
       "      <td id=\"T_3c264_row4_col5\" class=\"data row4 col5\" >-0.473</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n"
      ],
      "text/plain": [
       "<pandas.io.formats.style.Styler at 0x20b6e98c5c0>"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "hist, scen = B.to_stationary(lev)\n",
    "hist.to_csv(os.path.join(C.OUTPUT, \"exog_hist_v2.csv\"), index=False, encoding=\"utf-8-sig\")\n",
    "scen.to_csv(os.path.join(C.OUTPUT, \"exog_scen_v2.csv\"), index=False, encoding=\"utf-8-sig\")\n",
    "print(f\"과거 {len(hist)}분기({hist['분기'].iloc[0]}–{hist['분기'].iloc[-1]}) · 미래 {len(scen) // 5}분기 × 5개 시나리오\")\n",
    "print(\"투입 형태:\", \" · \".join(f\"{B.EXOG_LABEL[c]} {B.EXOG_DEF[c][1]}\" for c in B.EXOG_COLS))\n",
    "avg = scen.groupby(\"시나리오\")[B.EXOG_COLS].mean().rename(columns=B.EXOG_LABEL)\n",
    "avg.index = [f\"{s} {B.SCN_KO[s]}\" for s in avg.index]\n",
    "avg.style.set_caption(\"미래 40분기 평균(투입형)\").format(\"{:.3f}\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
