{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "87149e6d",
   "metadata": {},
   "source": [
    "# 01 데이터 입수 — 산업 패널·외생 6종·유가 원천\n",
    "\n",
    "**목적.** 외생 6종 진단에 필요한 동결 입력을 불러오고, 파일이 온전한지와 입력 사이의 변환 관계가 맞는지 확인한다.\n",
    "\n",
    "| 입력(`data/inputs/`) | 내용 | 출처 | 공개 |\n",
    "|---|---|---|---|\n",
    "| `levels_agg.csv` | 총량 모형 패널: 실질 GDP(계절조정, 십억원) 1980Q1–2025Q4 | 보고서1 2장 산출물(ECOS) | 공개 |\n",
    "| `levels_sa.csv`, `levels_62.csv`, `levels_69.csv` | 36·62·69분류 분기 실질부가가치(십억원) | 보고서1 2장 산출물(ECOS·KOSIS·산업연관표) | 공개 |\n",
    "| `io_linkage_W.csv`, `io_linkage_62.csv`, `io_linkage_69.csv` | 산업연관 강도 행렬(IO 사전) | 보고서1 2장 산출물(한국은행 산업연관표) | 공개 |\n",
    "| `exog_scenarios_v2.csv` | 외생 6종 수준값, S1–S5, 2000Q1–2035Q4 | 보고서1 3장 산출물(BOKDPM 평활·전망 + 시나리오 편차) | 공개 |\n",
    "| `exog_hist_v2.csv`, `exog_scen_v2.csv` | 위 수준값을 VARX 투입 형태(수준·차분·로그차분)로 바꾼 과거값·시나리오 경로 | 보고서1 3장 산출물 | 공개 |\n",
    "| `dsge_exog_paths.csv` | 시나리오 외생 충격 경로(`oil_g` 등) | 보고서1 3장 산출물 | 공개 |\n",
    "| `bokdpm_obs_2000_2025.csv` | BOKDPM 관측자료 20종(`LOIL_us` 포함) | ECOS·FRED 수집본 | 공개 |\n",
    "| `fred_POILWTIUSDM.csv`, `fred_POILDUBUSDM.csv` | WTI·두바이유 월별 가격(달러/배럴), 1992-01–2026-07 | FRED(IMF 국제상품가격) | 공개 |\n",
    "| `varx_coef_frozen.npz` | 원 파이프라인으로 추정한 VARX 계수 4종(총량·36·62·69) | 본 연구 모형 산출물 | 공개 |\n",
    "| `postreform/exog_hist_v2.csv`, `postreform/exog_scen_v2.csv` | 2026-10 정비판 외생 6종(실질금리 1차 차분)의 과거값·시나리오 경로 | 보고서1 3장 정비판 산출물(5장 패키지 입력과 같은 파일) | 공개 |\n",
    "\n",
    "출처와 원래 위치는 `data/inputs/PROVENANCE.md`에 정리하였다. 공표 SHAP 값(`data/published/shap_*.json`)은 03의 검증 기준이다.\n",
    "`data/published/jeongbi/`의 5장 기록 사본(62분류 정비판 분해 `shap_62_s5.csv`, 드리프트 재설정 `drift_anchor_s5.csv`)은 02 10절의 대조 기준이다.\n",
    "\n",
    "**산출물.** `data/interim/input_inventory.csv`, `data/interim/oil_quarterly_100ln.csv`, (ONLINE=1) `data/interim/fred_*_online.csv`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "2b07969a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T08:04:33.851433Z",
     "iopub.status.busy": "2026-10-08T08:04:33.850934Z",
     "iopub.status.idle": "2026-10-08T08:04:34.222953Z",
     "shell.execute_reply": "2026-10-08T08:04:34.222453Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Python 3.12.10 · pandas 2.2.3 · numpy 2.4.6\n",
      "  ONLINE=0 (FRED 유가 재수집)  FULL=0 (확장 점검)  FRED 키 없음(공개 CSV 사용)\n",
      "  외생 과거(정상형)     있음  data\\inputs\\exog_hist_v2.csv\n",
      "  외생 시나리오(정상형)   있음  data\\inputs\\exog_scen_v2.csv\n",
      "  외생 수준          있음  data\\inputs\\exog_scenarios_v2.csv\n",
      "  시나리오 충격        있음  data\\inputs\\dsge_exog_paths.csv\n",
      "  BOKDPM 관측      있음  data\\inputs\\bokdpm_obs_2000_2025.csv\n",
      "  FRED WTI       있음  data\\inputs\\fred_POILWTIUSDM.csv\n",
      "  FRED 두바이       있음  data\\inputs\\fred_POILDUBUSDM.csv\n",
      "  동결 계수          있음  data\\inputs\\varx_coef_frozen.npz\n",
      "  패널 총량          있음  data\\inputs\\levels_agg.csv\n",
      "  패널 36          있음  data\\inputs\\levels_sa.csv\n",
      "  패널 62          있음  data\\inputs\\levels_62.csv\n",
      "  패널 69          있음  data\\inputs\\levels_69.csv\n"
     ]
    }
   ],
   "source": [
    "import os, json\n",
    "import config as C            # BLAS 스레드 설정이 적용되도록 numpy보다 먼저 불러온다\n",
    "import numpy as np, pandas as pd\n",
    "import exog_diagnosis as F\n",
    "pd.set_option(\"display.float_format\", lambda v: f\"{v:,.3f}\")\n",
    "pd.set_option(\"display.width\", 200)\n",
    "C.setup()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "12f9cba9",
   "metadata": {},
   "source": [
    "## 1. 동결 입력과 SHA-256\n",
    "\n",
    "`config.INPUT_SHA256`에 적힌 해시와 파일 해시가 모두 같아야 다음 단계로 넘어간다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "489b03a1",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T08:04:34.223955Z",
     "iopub.status.busy": "2026-10-08T08:04:34.223955Z",
     "iopub.status.idle": "2026-10-08T08:04:34.260454Z",
     "shell.execute_reply": "2026-10-08T08:04:34.260454Z"
    }
   },
   "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>크기(bytes)</th>\n",
       "      <th>형태</th>\n",
       "      <th>SHA-256(앞 16자리)</th>\n",
       "      <th>일치</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>postreform/exog_hist_v2.csv</td>\n",
       "      <td>5521</td>\n",
       "      <td>104×7</td>\n",
       "      <td>d142ff2992b5ae3e</td>\n",
       "      <td>True</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>postreform/exog_scen_v2.csv</td>\n",
       "      <td>11096</td>\n",
       "      <td>200×8</td>\n",
       "      <td>f45f8007d7b88faf</td>\n",
       "      <td>True</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>postreform/exog_levels_2000_2035.csv</td>\n",
       "      <td>43925</td>\n",
       "      <td>720×8</td>\n",
       "      <td>748cc232732adb73</td>\n",
       "      <td>True</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>bokdpm_obs_2000_2025.csv</td>\n",
       "      <td>33329</td>\n",
       "      <td>104×21</td>\n",
       "      <td>8ae2f6709bf15265</td>\n",
       "      <td>True</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>dsge_exog_paths.csv</td>\n",
       "      <td>7540</td>\n",
       "      <td>200×7</td>\n",
       "      <td>b5fe00a341fe6763</td>\n",
       "      <td>True</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>exog_hist_v2.csv</td>\n",
       "      <td>5481</td>\n",
       "      <td>104×7</td>\n",
       "      <td>0943ba6160f6faf4</td>\n",
       "      <td>True</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>exog_scen_v2.csv</td>\n",
       "      <td>11034</td>\n",
       "      <td>200×8</td>\n",
       "      <td>f490a1cb17390289</td>\n",
       "      <td>True</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>exog_scenarios_v2.csv</td>\n",
       "      <td>43830</td>\n",
       "      <td>720×8</td>\n",
       "      <td>79e4a02cd4c3e7c2</td>\n",
       "      <td>True</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>fred_POILDUBUSDM.csv</td>\n",
       "      <td>12117</td>\n",
       "      <td>415×2</td>\n",
       "      <td>cc766e2524a02c7c</td>\n",
       "      <td>True</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>fred_POILWTIUSDM.csv</td>\n",
       "      <td>12095</td>\n",
       "      <td>415×2</td>\n",
       "      <td>ac49d9350ae20bf0</td>\n",
       "      <td>True</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>10</th>\n",
       "      <td>io_linkage_62.csv</td>\n",
       "      <td>67817</td>\n",
       "      <td>62×63</td>\n",
       "      <td>3f747a97c3804541</td>\n",
       "      <td>True</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>11</th>\n",
       "      <td>io_linkage_69.csv</td>\n",
       "      <td>79382</td>\n",
       "      <td>69×70</td>\n",
       "      <td>1f57d01c6867b456</td>\n",
       "      <td>True</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>12</th>\n",
       "      <td>io_linkage_W.csv</td>\n",
       "      <td>11035</td>\n",
       "      <td>36×37</td>\n",
       "      <td>cf11efc744586d35</td>\n",
       "      <td>True</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>13</th>\n",
       "      <td>levels_62.csv</td>\n",
       "      <td>105355</td>\n",
       "      <td>104×64</td>\n",
       "      <td>00469e892aac4742</td>\n",
       "      <td>True</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>14</th>\n",
       "      <td>levels_69.csv</td>\n",
       "      <td>122145</td>\n",
       "      <td>104×71</td>\n",
       "      <td>c8a5adcedfa4635c</td>\n",
       "      <td>True</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>15</th>\n",
       "      <td>levels_agg.csv</td>\n",
       "      <td>4748</td>\n",
       "      <td>184×3</td>\n",
       "      <td>f04021ff282e5387</td>\n",
       "      <td>True</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>16</th>\n",
       "      <td>levels_sa.csv</td>\n",
       "      <td>51177</td>\n",
       "      <td>184×38</td>\n",
       "      <td>ccfc8dad3c8cd7b0</td>\n",
       "      <td>True</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>17</th>\n",
       "      <td>varx_coef_frozen.npz</td>\n",
       "      <td>328559</td>\n",
       "      <td>B_agg(23, 1) · B_36(163, 36) · B_62(267, 62) ·...</td>\n",
       "      <td>2442a16d850a1891</td>\n",
       "      <td>True</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                      파일  크기(bytes)                                                 형태   SHA-256(앞 16자리)    일치\n",
       "0            postreform/exog_hist_v2.csv       5521                                              104×7  d142ff2992b5ae3e  True\n",
       "1            postreform/exog_scen_v2.csv      11096                                              200×8  f45f8007d7b88faf  True\n",
       "2   postreform/exog_levels_2000_2035.csv      43925                                              720×8  748cc232732adb73  True\n",
       "3               bokdpm_obs_2000_2025.csv      33329                                             104×21  8ae2f6709bf15265  True\n",
       "4                    dsge_exog_paths.csv       7540                                              200×7  b5fe00a341fe6763  True\n",
       "5                       exog_hist_v2.csv       5481                                              104×7  0943ba6160f6faf4  True\n",
       "6                       exog_scen_v2.csv      11034                                              200×8  f490a1cb17390289  True\n",
       "7                  exog_scenarios_v2.csv      43830                                              720×8  79e4a02cd4c3e7c2  True\n",
       "8                   fred_POILDUBUSDM.csv      12117                                              415×2  cc766e2524a02c7c  True\n",
       "9                   fred_POILWTIUSDM.csv      12095                                              415×2  ac49d9350ae20bf0  True\n",
       "10                     io_linkage_62.csv      67817                                              62×63  3f747a97c3804541  True\n",
       "11                     io_linkage_69.csv      79382                                              69×70  1f57d01c6867b456  True\n",
       "12                      io_linkage_W.csv      11035                                              36×37  cf11efc744586d35  True\n",
       "13                         levels_62.csv     105355                                             104×64  00469e892aac4742  True\n",
       "14                         levels_69.csv     122145                                             104×71  c8a5adcedfa4635c  True\n",
       "15                        levels_agg.csv       4748                                              184×3  f04021ff282e5387  True\n",
       "16                         levels_sa.csv      51177                                             184×38  ccfc8dad3c8cd7b0  True\n",
       "17                  varx_coef_frozen.npz     328559  B_agg(23, 1) · B_36(163, 36) · B_62(267, 62) ·...  2442a16d850a1891  True"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "rows = []\n",
    "for name, h in C.INPUT_SHA256.items():\n",
    "    p = os.path.join(C.INPUTS, name)\n",
    "    got = F.sha256(p)\n",
    "    shape = \"\"\n",
    "    if name.endswith(\".csv\"):\n",
    "        d = pd.read_csv(p, encoding=\"utf-8-sig\")\n",
    "        shape = f\"{d.shape[0]}×{d.shape[1]}\"\n",
    "    elif name.endswith(\".npz\"):\n",
    "        z = np.load(p, allow_pickle=False)\n",
    "        shape = \" · \".join(f\"{k}{z[k].shape}\" for k in z.files if k.startswith(\"B_\"))\n",
    "    rows.append({\"파일\": name, \"크기(bytes)\": os.path.getsize(p), \"형태\": shape, \"SHA-256(앞 16자리)\": got[:16], \"일치\": got == h})\n",
    "inv = pd.DataFrame(rows)\n",
    "inv.to_csv(os.path.join(C.INTERIM, \"input_inventory.csv\"), index=False, encoding=\"utf-8-sig\")\n",
    "assert inv[\"일치\"].all(), \"동결 입력의 SHA-256이 다르다 — data/inputs/ 파일을 확인한다\"\n",
    "inv"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2289b4d6",
   "metadata": {},
   "source": [
    "## 2. 산업 패널 — 총량·36·62·69\n",
    "\n",
    "네 패널 모두 추정 표본 2000Q1–2025Q4(104분기)에 결측이 없어야 한다. 총량 모형은 실질 GDP 한 계열을 수준으로,\n",
    "산업 모형은 각 산업 실질부가가치를 로그차분으로 쓴다. 62·69분류의 산업 합계는 같다(69분류는 62분류 일부를 분화한 것이다)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "fbfdd057",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T08:04:34.261473Z",
     "iopub.status.busy": "2026-10-08T08:04:34.261473Z",
     "iopub.status.idle": "2026-10-08T08:04:34.278033Z",
     "shell.execute_reply": "2026-10-08T08:04:34.277527Z"
    }
   },
   "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>2025 합계(조원)</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>총량</th>\n",
       "      <td>수준</td>\n",
       "      <td>1</td>\n",
       "      <td>1980Q1–2025Q4</td>\n",
       "      <td>0</td>\n",
       "      <td>2,326.267</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>36</th>\n",
       "      <td>차분</td>\n",
       "      <td>36</td>\n",
       "      <td>1980Q1–2025Q4</td>\n",
       "      <td>0</td>\n",
       "      <td>2,178.489</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>62</th>\n",
       "      <td>차분</td>\n",
       "      <td>62</td>\n",
       "      <td>2000Q1–2025Q4</td>\n",
       "      <td>0</td>\n",
       "      <td>2,178.489</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>69</th>\n",
       "      <td>차분</td>\n",
       "      <td>69</td>\n",
       "      <td>2000Q1–2025Q4</td>\n",
       "      <td>0</td>\n",
       "      <td>2,178.489</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "    명세  산업 수          전체 기간  표본 결측  2025 합계(조원)\n",
       "패널                                             \n",
       "총량  수준     1  1980Q1–2025Q4      0    2,326.267\n",
       "36  차분    36  1980Q1–2025Q4      0    2,178.489\n",
       "62  차분    62  2000Q1–2025Q4      0    2,178.489\n",
       "69  차분    69  2000Q1–2025Q4      0    2,178.489"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "SAMPLE = F.SAMPLE\n",
    "rows, ann = [], {}\n",
    "for name, spec in C.MODELS.items():\n",
    "    lev = pd.read_csv(spec[\"levels\"], index_col=0)\n",
    "    inds = [c for c in lev.columns if c != spec[\"gdp\"]]\n",
    "    sub = lev[inds].reindex(SAMPLE)\n",
    "    a = F.q_to_annual(sub).sum(axis=1)\n",
    "    ann[name] = a\n",
    "    rows.append({\"패널\": name, \"명세\": spec[\"spec\"], \"산업 수\": len(inds), \"전체 기간\": f\"{lev.index[0]}–{lev.index[-1]}\",\n",
    "                 \"표본 결측\": int(sub.isna().sum().sum()), \"2025 합계(조원)\": a.loc[2025] / 1e3})\n",
    "panels = pd.DataFrame(rows).set_index(\"패널\")\n",
    "assert panels[\"표본 결측\"].eq(0).all(), \"추정 표본에 결측이 있다\"\n",
    "assert abs(ann[\"62\"] - ann[\"69\"]).max() < 1e-3, \"62·69 산업 합계가 다르다\"\n",
    "panels"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "56896c9d",
   "metadata": {},
   "source": [
    "## 3. 외생 6종 — 수준값과 VARX 투입 형태\n",
    "\n",
    "외생은 BOKDPM 내생·관측 변수를 조합한 관측 가능한 계열이다. VARX에는 적분차수에 맞춰 수준·차분·로그차분 중 하나로 들어간다.\n",
    "\n",
    "| 외생 | 구성 | 투입 형태 |\n",
    "|---|---|---|\n",
    "| 유가 | `LOIL_us`(100·ln 달러/배럴) | 1차 차분 |\n",
    "| 세계수요 | `Y_us`(미국 산출갭) | 수준 |\n",
    "| 실질금리 | `CB_kor` − 소비자물가 전년동기비 | 수준 |\n",
    "| 실질환율 | `LS_kor` + 미국 CPI − 한국 CPI(100·ln) | 1차 차분 |\n",
    "| 고용_취업자 | 경제활동인구 × (1 − 실업률/100), 천 명 | 100·로그차분 |\n",
    "| 우리경기 | `Y_kor`(한국 산출갭) | 수준 |\n",
    "\n",
    "수준값 파일에 원 변환(`build_varx_exog.py`와 같은 규칙)을 다시 적용해 과거값·시나리오 경로 두 파일과 같아지는지 확인한다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "4190899b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T08:04:34.278033Z",
     "iopub.status.busy": "2026-10-08T08:04:34.278033Z",
     "iopub.status.idle": "2026-10-08T08:04:34.296167Z",
     "shell.execute_reply": "2026-10-08T08:04:34.296167Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "과거값 (104, 6) · 시나리오 5개 × (40, 6) · 수준값 (720, 8)\n",
      "재변환 최대 차이: 과거 0.0e+00 · 시나리오 0.0e+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 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>2025Q4(과거 끝)</th>\n",
       "      <th>2035Q4(S1)</th>\n",
       "      <th>변환</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>유가</th>\n",
       "      <td>-9.763</td>\n",
       "      <td>2.469</td>\n",
       "      <td>diff</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>세계수요</th>\n",
       "      <td>1.022</td>\n",
       "      <td>-0.001</td>\n",
       "      <td>level</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>실질금리</th>\n",
       "      <td>0.939</td>\n",
       "      <td>2.781</td>\n",
       "      <td>level</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>실질환율</th>\n",
       "      <td>3.610</td>\n",
       "      <td>-0.109</td>\n",
       "      <td>diff</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>고용_취업자</th>\n",
       "      <td>0.024</td>\n",
       "      <td>0.216</td>\n",
       "      <td>dlog</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>우리경기</th>\n",
       "      <td>0.102</td>\n",
       "      <td>-0.074</td>\n",
       "      <td>level</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        2025Q4(과거 끝)  2035Q4(S1)     변환\n",
       "유가            -9.763       2.469   diff\n",
       "세계수요           1.022      -0.001  level\n",
       "실질금리           0.939       2.781  level\n",
       "실질환율           3.610      -0.109   diff\n",
       "고용_취업자         0.024       0.216   dlog\n",
       "우리경기           0.102      -0.074  level"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "eh = F.read_exog_hist(C.EXOG_HIST)\n",
    "es = F.read_exog_scen(C.EXOG_SCEN)\n",
    "lv = F.read_exog_levels(C.EXOG_LEVELS)\n",
    "st = F.to_stationary(lv)\n",
    "d_hist = (st[(st[\"시나리오\"] == \"S1\") & (st[\"분기\"] < \"2026Q1\")].set_index(\"분기\")[F.EXOG] - eh).abs().max().max()\n",
    "d_scen = max((g.set_index(\"분기\")[F.EXOG] - es[s]).abs().max().max() for s, g in st[st[\"분기\"] >= \"2026Q1\"].groupby(\"시나리오\"))\n",
    "print(f\"과거값 {eh.shape} · 시나리오 {len(es)}개 × {es['S1'].shape} · 수준값 {lv.shape}\")\n",
    "print(f\"재변환 최대 차이: 과거 {d_hist:.1e} · 시나리오 {d_scen:.1e}\")\n",
    "assert d_hist < 1e-4 and d_scen < 1e-4, \"수준값과 정상형 파일의 변환 관계가 맞지 않는다\"\n",
    "pd.DataFrame({\"2025Q4(과거 끝)\": eh.loc[\"2025Q4\"], \"2035Q4(S1)\": es[\"S1\"].loc[\"2035Q4\"], \"변환\": pd.Series(F.TF)})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b13fc1ae",
   "metadata": {},
   "source": [
    "## 4. 유가 원천 — `LOIL_us`와 FRED 두 계열\n",
    "\n",
    "BOKDPM 자료 수집 문서(`used_data_sources.json`)는 미국 유가를 FRED `POILWTIUSDM`(WTI)로 적고, 관측자료 파일의 `LOIL_us`는\n",
    "WTI 분기 평균의 100·ln과 정확히 같다(아래 첫 줄). 그런데 VARX 외생 유가의 과거 구간(외생 수준값의 2000Q1–2025Q4)은 BOKDPM 추정 결과의\n",
    "평활값이며, 이 값은 WTI가 아니라 **두바이유(`POILDUBUSDM`) 분기 평균의 100·ln과 일치한다**(아래 둘째·셋째 줄).\n",
    "BOKDPM 추정 당시 Dynare가 읽은 자료 파일에는 두바이유가 들어 있었고, 이후 자료 파일을 WTI로 다시 만들었으나 모형을 다시 추정하지 않은 결과로 판단된다.\n",
    "이 차이가 전망에 주는 영향은 `02_분석.ipynb` 6절에서 계산한다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "f28e6626",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T08:04:34.297171Z",
     "iopub.status.busy": "2026-10-08T08:04:34.297171Z",
     "iopub.status.idle": "2026-10-08T08:04:34.631763Z",
     "shell.execute_reply": "2026-10-08T08:04:34.631763Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style type=\"text/css\">\n",
       "</style>\n",
       "<table id=\"T_0ee82\">\n",
       "  <thead>\n",
       "    <tr>\n",
       "      <th class=\"blank level0\" >&nbsp;</th>\n",
       "      <th id=\"T_0ee82_level0_col0\" class=\"col_heading level0 col0\" >최대 |차이|</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th class=\"index_name level0\" >비교</th>\n",
       "      <th class=\"blank col0\" >&nbsp;</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th id=\"T_0ee82_level0_row0\" class=\"row_heading level0 row0\" >BOKDPM 관측 LOIL_us − 100·ln(WTI)</th>\n",
       "      <td id=\"T_0ee82_row0_col0\" class=\"data row0 col0\" >0.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_0ee82_level0_row1\" class=\"row_heading level0 row1\" >VARX 유가(평활, 과거) − 100·ln(두바이)</th>\n",
       "      <td id=\"T_0ee82_row1_col0\" class=\"data row1 col0\" >0.000050</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th id=\"T_0ee82_level0_row2\" class=\"row_heading level0 row2\" >VARX 유가(평활, 과거) − 100·ln(WTI)</th>\n",
       "      <td id=\"T_0ee82_row2_col0\" class=\"data row2 col0\" >30.309548</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n"
      ],
      "text/plain": [
       "<pandas.io.formats.style.Styler at 0x15a946a58e0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "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>WTI_100ln</th>\n",
       "      <th>두바이_100ln</th>\n",
       "      <th>VARX유가_100ln</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>2000Q1</th>\n",
       "      <td>336.129</td>\n",
       "      <td>319.426</td>\n",
       "      <td>319.426</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2008Q2</th>\n",
       "      <td>482.011</td>\n",
       "      <td>476.330</td>\n",
       "      <td>476.330</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2012Q1</th>\n",
       "      <td>463.339</td>\n",
       "      <td>475.324</td>\n",
       "      <td>475.324</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2020Q2</th>\n",
       "      <td>333.305</td>\n",
       "      <td>349.514</td>\n",
       "      <td>349.514</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2025Q4</th>\n",
       "      <td>408.842</td>\n",
       "      <td>415.253</td>\n",
       "      <td>415.253</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "        WTI_100ln  두바이_100ln  VARX유가_100ln\n",
       "2000Q1    336.129    319.426       319.426\n",
       "2008Q2    482.011    476.330       476.330\n",
       "2012Q1    463.339    475.324       475.324\n",
       "2020Q2    333.305    349.514       349.514\n",
       "2025Q4    408.842    415.253       415.253"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "wti_m, dub_m = F.read_fred_csv(C.WTI_CSV), F.read_fred_csv(C.DUB_CSV)\n",
    "obs = pd.read_csv(C.BOKDPM_OBS)\n",
    "chk = F.loil_observation_check(obs, wti_m)\n",
    "\n",
    "s1 = lv[lv[\"시나리오\"] == \"S1\"].set_index(\"분기\")[\"유가\"].reindex(F.SAMPLE)\n",
    "wq = F.quarterly_100ln(wti_m, start=\"2000-01-01\"); wq.index = wq.index.astype(str)\n",
    "dq = F.quarterly_100ln(dub_m, start=\"2000-01-01\"); dq.index = dq.index.astype(str)\n",
    "src = pd.DataFrame([\n",
    "    {\"비교\": \"BOKDPM 관측 LOIL_us − 100·ln(WTI)\", \"최대 |차이|\": chk[\"차이\"].abs().max()},\n",
    "    {\"비교\": \"VARX 유가(평활, 과거) − 100·ln(두바이)\", \"최대 |차이|\": (s1 - dq.reindex(F.SAMPLE)).abs().max()},\n",
    "    {\"비교\": \"VARX 유가(평활, 과거) − 100·ln(WTI)\", \"최대 |차이|\": (s1 - wq.reindex(F.SAMPLE)).abs().max()},\n",
    "]).set_index(\"비교\")\n",
    "assert src.iloc[0, 0] < 1e-6 and src.iloc[1, 0] < 1e-3, \"유가 원천 대조 결과가 예상과 다르다\"\n",
    "oilq = pd.DataFrame({\"WTI_100ln\": wq, \"두바이_100ln\": dq, \"VARX유가_100ln\": s1}).loc[\"2000Q1\":]\n",
    "oilq.to_csv(os.path.join(C.INTERIM, \"oil_quarterly_100ln.csv\"), encoding=\"utf-8-sig\")\n",
    "display(src.style.format(\"{:.6f}\"))\n",
    "oilq.loc[[\"2000Q1\", \"2008Q2\", \"2012Q1\", \"2020Q2\", \"2025Q4\"]]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bfd89fb5",
   "metadata": {},
   "source": [
    "## 5. FRED 재수집과 대조(ONLINE=1)\n",
    "\n",
    "`ONLINE=1`이면 FRED에서 두 계열을 다시 받아 동결본과 겹치는 달의 값을 대조하고, 원 시산 표본(1999-10–2026-07)으로 로그차분 상관을 다시 계산한다.\n",
    "FRED 키는 환경변수 `API_KEY_FRED`에서만 읽으며, 키가 없으면 공개 CSV 주소를 쓴다. 기본 실행(ONLINE=0)은 이 절을 건너뛴다.\n",
    "FRED가 과거 값을 개정하면 차이가 나타날 수 있으며, 이때도 검증은 동결본 기준으로 진행한다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "9edf3f55",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T08:04:34.633265Z",
     "iopub.status.busy": "2026-10-08T08:04:34.632765Z",
     "iopub.status.idle": "2026-10-08T08:04:34.636767Z",
     "shell.execute_reply": "2026-10-08T08:04:34.636767Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ONLINE=0: FRED 재수집을 건너뛰고 동결본만 사용한다.\n"
     ]
    }
   ],
   "source": [
    "if C.ONLINE:\n",
    "    rows, fetched = [], {}\n",
    "    for sid, frozen in [(C.WTI_SERIES, wti_m), (C.DUB_SERIES, dub_m)]:\n",
    "        s = F.fetch_fred(sid, C.API_KEY_FRED or None)\n",
    "        fetched[sid] = s\n",
    "        s.rename(sid).to_frame().to_csv(os.path.join(C.INTERIM, f\"fred_{sid}_online.csv\"), encoding=\"utf-8-sig\")\n",
    "        ov = s.index.intersection(frozen.index)\n",
    "        rows.append({\"계열\": sid, \"받은 기간\": f\"{s.index.min():%Y-%m}–{s.index.max():%Y-%m}\", \"겹친 달\": len(ov),\n",
    "                     \"새 달\": int((s.index > frozen.index.max()).sum()), \"겹친 달 최대 |차이|\": float((s[ov] - frozen[ov]).abs().max())})\n",
    "    online = pd.DataFrame(rows).set_index(\"계열\")\n",
    "    r_on = F.oil_benchmark(fetched[C.WTI_SERIES], fetched[C.DUB_SERIES], end=C.FROZEN_FRED_END)[\"corr\"]\n",
    "    r_fz = F.oil_benchmark(wti_m, dub_m)[\"corr\"]\n",
    "    print(f\"로그차분 상관(1999-10–2026-07): 재수집 {r_on:.4f} · 동결본 {r_fz:.4f}\")\n",
    "    print(\"겹친 달 값이 모두 같다.\" if online[\"겹친 달 최대 |차이|\"].max() < 1e-8 else \"주의: FRED가 과거 값을 개정했다(검증은 동결본 기준).\")\n",
    "    display(online)\n",
    "else:\n",
    "    print(\"ONLINE=0: FRED 재수집을 건너뛰고 동결본만 사용한다.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "609012bf",
   "metadata": {},
   "source": [
    "## 6. 산출물\n",
    "\n",
    "다음 노트북(`02_분석.ipynb`)은 `data/inputs/`의 동결 입력을 직접 읽는다. 이 노트북의 산출물은 점검 기록이다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "4daedc97",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-10-08T08:04:34.637769Z",
     "iopub.status.busy": "2026-10-08T08:04:34.637769Z",
     "iopub.status.idle": "2026-10-08T08:04:34.640469Z",
     "shell.execute_reply": "2026-10-08T08:04:34.640469Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "input_inventory.csv                    1,138 bytes\n",
      "oil_quarterly_100ln.csv                5,771 bytes\n"
     ]
    }
   ],
   "source": [
    "for f in sorted(os.listdir(C.INTERIM)):\n",
    "    p = os.path.join(C.INTERIM, f)\n",
    "    print(f\"{f:<34} {os.path.getsize(p):>9,d} bytes\")"
   ]
  }
 ],
 "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
}
