{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 논문 1 · 챕터 4 — DSGE 시나리오 경로\n",
    "\n",
    "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/sdkparkforbi/gva-scenario-2025-2035/blob/main/ch4/code/ch4_dsge.ipynb)\n",
    "\n",
    "> **Google Colab에서 바로 실행**됩니다. 배지를 누른 뒤 `런타임 → 모두 실행`.\n",
    "> 데이터는 아래 셀에서 자동으로 내려받고, 그림은 셀 실행 시 바로 표시됩니다.\n",
    "\n",
    "각 셀에는 **셀 번호 · 셀 제목**이 달려 있고, 코드에는 줄별 **주석**을 넣었습니다."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 0. 준비 — Colab 환경과 데이터"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 1 · 한글 폰트 (koreanize-matplotlib) =====\n",
    "# 그림의 한글이 깨지지 않도록 koreanize-matplotlib 를 설치하고 불러옵니다.\n",
    "# import 하는 순간 matplotlib 한글 폰트가 자동 설정됩니다.\n",
    "_font_ok = False\n",
    "try:\n",
    "    import koreanize_matplotlib                       # 이미 설치돼 있으면 바로 사용\n",
    "    _font_ok = True\n",
    "except Exception:\n",
    "    try:\n",
    "        import subprocess, sys\n",
    "        subprocess.run([sys.executable,'-m','pip','install','-q','koreanize-matplotlib'], check=False)\n",
    "        import koreanize_matplotlib                   # 설치 후 다시 불러오기\n",
    "        _font_ok = True\n",
    "    except Exception:\n",
    "        _font_ok = False\n",
    "if not _font_ok:                                      # 폴백: 나눔폰트 직접 설치·등록\n",
    "    import subprocess\n",
    "    subprocess.run(['apt-get','-qq','-y','install','fonts-nanum'], check=False)\n",
    "    import matplotlib.font_manager as fm, matplotlib.pyplot as plt\n",
    "    _p = '/usr/share/fonts/truetype/nanum/NanumGothic.ttf'\n",
    "    try:\n",
    "        fm.fontManager.addfont(_p)\n",
    "        plt.rcParams['font.family'] = fm.FontProperties(fname=_p).get_name()  # 실제 등록 이름 사용\n",
    "    except Exception: pass\n",
    "import matplotlib.pyplot as plt\n",
    "plt.rcParams['axes.unicode_minus'] = False           # 마이너스 기호 깨짐 방지\n",
    "print('한글 폰트 준비 완료')"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 2 · 데이터 다운로드 =====\n",
    "# 이 챕터의 입력 데이터를 서버에서 로컬 data/ 폴더로 내려받습니다.\n",
    "import os, urllib.request, urllib.parse\n",
    "BASE = \"https://aiforalab.com/gva-scenario-2025-2035/ch4/data/\"          # 서버의 이 챕터 데이터 폴더\n",
    "FILES = [\"dsge_exog_paths.csv\", \"dsge_macro_paths.csv\", \"kosis_생산연령인구.csv\"]\n",
    "os.makedirs(\"data\", exist_ok=True)\n",
    "for f in FILES:                                      # 한글 파일명은 URL 인코딩 후 요청\n",
    "    urllib.request.urlretrieve(BASE + urllib.parse.quote(f), os.path.join('data', f))\n",
    "print('다운로드 완료:', len(FILES), '개 ->', sorted(os.listdir('data')))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. 방법 구현과 계산"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 3 · 라이브러리 임포트·설정 =====\n",
    "import os, json\n",
    "import numpy as np, pandas as pd\n",
    "import matplotlib\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 4 · Colab 경로: 데이터=data/, 그림=figures/ =====\n",
    "# ----- Colab 경로: 데이터=data/, 그림=figures/ -----\n",
    "import os\n",
    "DATA = \"data\"; PREP = \"data\"; FIG = \"figures\"; OUT = \"figures\"\n",
    "os.makedirs(\"data\", exist_ok=True); os.makedirs(\"figures\", exist_ok=True)\n",
    "\n",
    "plt.rcParams[\"savefig.dpi\"]=130"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 5 · BOKDPM calibration =====\n",
    "# --- BOKDPM calibration ---\n",
    "b1,b2,b3,b4,b6=0.50,0.10,0.10,0.30,0.10\n",
    "lam1,lam2,lam3=0.55,0.15,0.65\n",
    "g1,g2,g3=0.50,1.50,0.50\n",
    "a1,a2=0.80,0.30\n",
    "om4,om7,om8=0.25,0.50,0.20\n",
    "rrbar,pietar=1.5,2.0\n",
    "H,BUF=40,24; N=H+BUF\n",
    "SCN=[\"S1\",\"S2\",\"S3\",\"S4\",\"S5\"]\n",
    "SCN_KO={\"S1\":\"기준\",\"S2\":\"공급제약+충격\",\"S3\":\"제약완화\",\"S4\":\"기술도약\",\"S5\":\"지정학\"}"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 6 · 함수 정의: labor_force_growth =====\n",
    "def labor_force_growth():\n",
    "    pop=pd.read_csv(os.path.join(DATA,\"kosis_생산연령인구.csv\"),encoding=\"utf-8-sig\").set_index(\"연도\")\n",
    "    col={\"중위\":\"중위_천명\",\"고위\":\"고위_천명\",\"저위\":\"저위_천명\"}; out={}\n",
    "    for a,c in col.items():\n",
    "        ann={y:np.log(pop.loc[y,c]/pop.loc[y-1,c])*100 for y in range(2026,2036)}\n",
    "        out[a]=np.array([ann[2026+h//4]/4 for h in range(H)])\n",
    "    return out"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 7 · 함수 정의: scenario_inputs =====\n",
    "def scenario_inputs(s):\n",
    "    z=np.zeros(N); dec=lambda k:np.clip(1-np.arange(N)/k,0,1)\n",
    "    yus=z.copy(); oil=z.copy(); tcost=z.copy(); dshk=z.copy()\n",
    "    if s==\"S2\": yus=-1.2*dec(12); oil=-8.0*dec(8); tcost=+1.5*dec(12); dshk=-1.0*dec(12)\n",
    "    if s==\"S4\": yus=+0.8*dec(20); dshk=+0.6*np.ones(N)*dec(28)\n",
    "    if s==\"S5\": oil=+15.0*dec(8); tcost=+2.0*dec(10); yus=-0.6*dec(10); dshk=-0.8*dec(10)\n",
    "    return yus,oil,tcost,dshk"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 8 · 함수 정의: solve_korea_block =====\n",
    "def solve_korea_block(yus,oil,dshk,iters=400):\n",
    "    Y=np.zeros(N); PIE=np.full(N,pietar); RS=np.full(N,rrbar+pietar); S=np.zeros(N)\n",
    "    for _ in range(iters):\n",
    "        Y0,PIE0,RS0,S0=Y.copy(),PIE.copy(),RS.copy(),S.copy()\n",
    "        for t in range(N):\n",
    "            yl=Y[t-1] if t>0 else 0.0; yf=Y[t+1] if t+1<N else 0.0\n",
    "            rsl=RS[t-1] if t>0 else rrbar+pietar\n",
    "            rgap_l=(RS[t-1] if t>0 else rrbar+pietar)-pietar-rrbar\n",
    "            Y[t]=(b1*yl+b2*yf-b3*rgap_l+b4*yus[t]-b6*oil[t]/10+dshk[t])\n",
    "            pif=PIE[t+1] if t+1<N else pietar; pil=PIE[t-1] if t>0 else pietar\n",
    "            PIE[t]=lam3*pietar+(1-lam3)*(lam1*pif+(1-lam1)*pil+lam2*4*yl)\n",
    "            pie_e=PIE[t+1] if t+1<N else pietar\n",
    "            RS[t]=g1*rsl+(1-g1)*(rrbar+pietar+g2*(pie_e-pietar)+g3*4*Y[t])\n",
    "            sf=S[t+1] if t+1<N else 0.0; sl=S[t-1] if t>0 else 0.0\n",
    "            rgap=RS[t]-pietar-rrbar\n",
    "            S[t]=(1-0.1)*(om7*sf+(1-om7)*sl-om4*rgap)+om8*(-Y[t])\n",
    "        if max(np.abs(Y-Y0).max(),np.abs(RS-RS0).max(),np.abs(S-S0).max())<1e-8: break\n",
    "    RR=RS-PIE\n",
    "    return Y[:H],PIE[:H],RS[:H],RR[:H],S[:H]"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 9 · 계산 =====\n",
    "lfg=labor_force_growth()\n",
    "lab_assump={\"S1\":\"중위\",\"S2\":\"저위\",\"S3\":\"고위\",\"S4\":\"중위\",\"S5\":\"중위\"}\n",
    "partic=np.linspace(0.05,0.0,H)\n",
    "quarters=[f\"{2026+h//4}Q{h%4+1}\" for h in range(H)]\n",
    "rows,macro=[],[]\n",
    "for s in SCN:\n",
    "    yus,oil_sh,tcost,dshk=scenario_inputs(s)\n",
    "    Y,PIE,RS,RR,S=solve_korea_block(yus,oil_sh,dshk)\n",
    "    UNRgap=np.zeros(H)\n",
    "    for t in range(H): UNRgap[t]=a1*(UNRgap[t-1] if t>0 else 0)-a2*Y[t]\n",
    "    dUNR=np.diff(np.concatenate([[0],UNRgap]))\n",
    "    oil_g=0.5+oil_sh[:H]/8*4\n",
    "    trd_g=1.0+0.8*yus[:H]-0.6*tcost[:H]\n",
    "    d_rrate=np.diff(np.concatenate([[RR[0]],RR]))\n",
    "    rfx_g=np.diff(np.concatenate([[0],S]))\n",
    "    lab_g=lfg[lab_assump[s]]+partic-dUNR\n",
    "    for h in range(H):\n",
    "        rows.append([s,quarters[h],round(oil_g[h],3),round(trd_g[h],3),round(d_rrate[h],3),round(rfx_g[h],3),round(lab_g[h],3)])\n",
    "        macro.append([s,quarters[h],round(Y[h],3),round(PIE[h],3),round(RS[h],3),round(RR[h],3),round(S[h],3),round(UNRgap[h],3)])\n",
    "ex=pd.DataFrame(rows,columns=[\"시나리오\",\"분기\",\"oil_g\",\"trd_g\",\"d_rrate\",\"rfx_g\",\"lab_g\"])\n",
    "mac=pd.DataFrame(macro,columns=[\"시나리오\",\"분기\",\"산출갭Y\",\"인플레PIE\",\"정책금리RS\",\"실질금리RR\",\"실질환율갭S\",\"실업갭UNR\"])\n",
    "ex.to_csv(os.path.join(DATA,\"dsge_exog_paths.csv\"),index=False,encoding=\"utf-8-sig\")\n",
    "mac.to_csv(os.path.join(DATA,\"dsge_macro_paths.csv\"),index=False,encoding=\"utf-8-sig\")"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 10 · 콘솔 =====\n",
    "# --- 콘솔 ---\n",
    "print(\"챕터4 — DSGE(BOKDPM 포트) 시나리오 경로 생성 완료\")\n",
    "print(f\"{'시나리오':<16}{'2030 산출갭':>10}{'10년 고용Σ':>10}{'10년 실질금리Δ':>13}\")\n",
    "for s in SCN:\n",
    "    m=mac[mac['시나리오']==s]; e=ex[ex['시나리오']==s]\n",
    "    print(f\"{s+' '+SCN_KO[s]:<16}{m['산출갭Y'].iloc[16]:>10.2f}{e['lab_g'].sum():>10.2f}{e['d_rrate'].sum():>13.2f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. 결과와 그림"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 11 · fig1: 5 외생 경로 (BVARX 입력) =====\n",
    "# --- fig1: 5 외생 경로 (BVARX 입력) ---\n",
    "COLS=[(\"oil_g\",\"유가 증가율\"),(\"trd_g\",\"교역량 증가율\"),(\"d_rrate\",\"실질금리 변화\"),(\"rfx_g\",\"실질환율 변화\"),(\"lab_g\",\"고용 증가율\")]\n",
    "CLR={\"S1\":\"#333\",\"S2\":\"#c0392b\",\"S3\":\"#1f4e79\",\"S4\":\"#2e7d32\",\"S5\":\"#b8892f\"}\n",
    "t=[pd.Period(q,\"Q\").to_timestamp(how=\"end\") for q in quarters]\n",
    "fig,axes=plt.subplots(2,3,figsize=(15,7.5))\n",
    "fig.suptitle(\"그림 1. DSGE가 생성한 시나리오별 외생 경로 (BVARX 입력 5종, 2026–2035)\",fontsize=14,fontweight=\"bold\")\n",
    "for ax,(c,lab) in zip(axes.ravel(),COLS):\n",
    "    for s in SCN:\n",
    "        d=ex[ex['시나리오']==s]\n",
    "        ax.plot(t,d[c].values,color=CLR[s],lw=1.6,label=f\"{s} {SCN_KO[s]}\")\n",
    "    ax.axhline(0,color=\"grey\",lw=.6); ax.set_title(lab); ax.grid(alpha=.25); ax.margins(x=.02)\n",
    "axes.ravel()[-1].axis(\"off\")\n",
    "axes.ravel()[0].legend(loc=\"center left\",bbox_to_anchor=(3.25,-.4),fontsize=10,frameon=False)\n",
    "fig.tight_layout(rect=[0,0,1,.95]); fig.savefig(os.path.join(FIG,\"fig1_exog_paths.png\"),bbox_inches=\"tight\"); plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 12 · fig2: 거시 산출갭 + 고용 =====\n",
    "# --- fig2: 거시 산출갭 + 고용 ---\n",
    "fig,ax=plt.subplots(1,2,figsize=(13,4.6))\n",
    "fig.suptitle(\"그림 2. DSGE 거시 블록 — 시나리오별 산출갭과 고용\",fontweight=\"bold\")\n",
    "for s in SCN:\n",
    "    m=mac[mac['시나리오']==s]; e=ex[ex['시나리오']==s]\n",
    "    ax[0].plot(t,m['산출갭Y'].values,color=CLR[s],lw=1.6,label=f\"{s} {SCN_KO[s]}\")\n",
    "    ax[1].plot(t,e['lab_g'].cumsum().values,color=CLR[s],lw=1.6)\n",
    "ax[0].axhline(0,color=\"grey\",lw=.6); ax[0].set_title(\"(a) 산출갭 (%)\"); ax[0].legend(fontsize=8); ax[0].grid(alpha=.25)\n",
    "ax[1].set_title(\"(b) 누적 고용증가 (%) — 인구 가정 차이\"); ax[1].grid(alpha=.25)\n",
    "fig.tight_layout(rect=[0,0,1,.93]); fig.savefig(os.path.join(FIG,\"fig2_macro.png\"),bbox_inches=\"tight\"); plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {},
   "execution_count": null,
   "outputs": [],
   "source": [
    "# ===== 셀 13 · 결과 출력 =====\n",
    "json.dump({s:{\"lab_sum\":round(float(ex[ex['시나리오']==s]['lab_g'].sum()),2),\n",
    "              \"Y2030\":round(float(mac[mac['시나리오']==s]['산출갭Y'].iloc[16]),2)} for s in SCN},\n",
    "          open(os.path.join(DATA,\"ch4_results.json\"),\"w\",encoding=\"utf-8\"),ensure_ascii=False,indent=2)\n",
    "print(\"저장: ch4/data/(dsge_exog_paths.csv, dsge_macro_paths.csv), ch4/figures/(fig1,fig2)\")"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "name": "ch4_dsge.ipynb",
   "provenance": []
  },
  "kernelspec": {
   "name": "python3",
   "display_name": "Python 3"
  },
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}