{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true,
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "## Tutorial 2: Monetary Policy Analysis"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "KU Leuven Minicourse\n",
    "\n",
    "Adrien Auclert\n",
    "\n",
    "Spring 2023"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This tutorial walks us through setting up the monetary policy models that we covered in lecture."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "What we'll do in a nutshell:\n",
    "\n",
    "   1. Set up the model using blocks \n",
    "   2. Get the steady state\n",
    "   3. Get the impulse responses to `r_ante` shocks, compare to the RA model\n",
    "   4. Use Jacobians to decompose into direct/indirect effects\n",
    "   5. Repeat steps 1-3 for our alternative models"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " We'll start by importing our usual three packages."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "\n",
    "\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We'll also import a `py` file that has some convenience plotting functions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import tutorial_closed_econ_monetary as tutil"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "Here is our predefined `calibration` dictionary for this session."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "calibration = {'eis': 0.5,     # EIS\n",
    "               'rho_e': 0.92,  # Persistence of idiosyncratic productivity shocks\n",
    "               'sd_e': 0.92,   # Standard deviation of idiosyncratic productivity shocks\n",
    "               'Y': 1.,        # Output\n",
    "               'r_ante': 0.01, # target real interest rate\n",
    "               'min_a': -1,    # Minimum asset level on the grid\n",
    "               'max_a': 1_000, # Maximum asset level on the grid\n",
    "               'n_a': 500,     # Number of asset grid points\n",
    "               'n_e': 11}      # Number of productivity grid points"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "Because we already went over the canonical HANK model in Tutorial 1, here we'll instead follow what is actually a more common workflow in SSJ: load a predefined `HetBlock` and start working with that directly. \n",
    "\n",
    "We do this by preloading the household block and grid function from `sj.hetblocks.hh_sim` "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, we define the income input. For our baseline model, income is just $e\\times Y$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We're ready to define our simple `HetBlock`, with `make_grids` and `income` as `hetinputs`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In addition, we have the two blocks we discussed in lecture: one that takes us from the exogenous, ex-ante `r` set by monetary policy to the (ex-post) `r` faced by households, and a market clearing block. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We'll also define an RA model for comparison"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "@sj.solved(unknowns={'C': 1, 'A': 1}, targets=[\"euler\", \"budget_constraint\"], solver=\"broyden_custom\")\n",
    "def household_ra_simple(C, A, Y, eis, beta, r):\n",
    "    euler = (beta * (1 + r(1))) ** (-eis) * C(1) - C\n",
    "    budget_constraint = (1 + r) * A(-1) + Y - C - A\n",
    "    return euler, budget_constraint\n",
    "\n",
    "ra = sj.create_model([household_ra_simple, ex_post_rate, mkt_clearing_simple], name=\"Representative Agent Model\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we define a dict for our three models: the HA model, the HA model with zero liquidity (an instance of `ha_simple` that we'll calibrate to have epsilon liquidity) and the RA model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "ss = {}\n",
    "models = {'ha': ha_simple, 'ha_zl': ha_simple, 'ra': ra}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Calibration"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Before we start calibrating, let's ask SSJ to draw the DAG of this model for us. A very useful way of understanding what is going on!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This looks like the DAG from the class, except that we haven't specified the inputs, exogenous variables, or targets. That's actually how the model is stored on the computer, and then we need to give SSJ in we'll need to do this to solve the full model."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's proceed to solving for the steady state. We know that we need to solve for $\\beta$ to hit the goods market clearing condition or (even better in practice) the asset market clearing condition. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we don't remember what we called $\\beta$, we look for it in the `inputs` of the model. Recall that these are not ouputs of any block, so they give us the candidate unknowns and exogenous variables. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So we found `beta`. Similarly, if we don't remember what we call our market clearing conditions, we look for them among the model's `outputs`. These are not inputs into any block, so they give us the candidate targets of our model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we get the steady state by calling the `solve_steady_state` function, giving it a reasonable range for `beta` to look over. We can check out the outcome steady state dict by calling `toplevel`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that the calibration function filled up all the variables that are implicitly defined in the DAG, such as  `r`. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To get the calibration of the ZL model, we just reset the minimal asset level on the grid and recalibrate. Of course, we'll get a much lower $\\beta$ to clear the asset market at the same `r` but with much lower liquidity. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, we calibrate the RA model. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "calibration_ra = calibration.copy()\n",
    "calibration_ra['beta'] = 1 / (1 + calibration_ra['r_ante'])\n",
    "ss['ra'] = models['ra'].solve_steady_state(calibration_ra, {'C': 1., 'A': 0.8}, {'budget_constraint': 0., 'asset_mkt': 0.},\n",
    "                                           dissolve=['household_ra_simple'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "## Transition Dynamics: comparing HA and RA\n",
    "\n",
    "Now that we have a steady state, let's compute some simple impulse responses.\n",
    "\n",
    "To do this, we'll want to use `solve_impulse_linear`. If we don't remember the syntax, we can always look for the help. \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We learn that we need to give this the model's steady state, and then `unknowns`, `targets`, and `inputs` (ie shocks). \n",
    "\n",
    "Again, `unknows` and `inputs` are among the model's inputs, and `targets` among the outputs.\n",
    "\n",
    "Inspecting the above, `r_ante` is our shock, `Y` is our unknown, and `asset_market` our natural target.\n",
    "\n",
    "If we want, we can visualize the dependencies including unknowns and targets using `drawdag` again called with optional arguments. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we are ready to look at the response of output to a decrease in the interest rate, with a per-period persistence of $0.7$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    },
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "# Find the linear impulse responses to an \"r\" shock\n",
    "T = 300\n",
    "dr = -0.01 * 0.7 ** np.arange(T)\n",
    "irf = {}\n",
    "for m in ['ha', 'ha_zl', 'ra']:\n",
    "    irf[m] = ...\n",
    "\n",
    "tutil.figure_1(irf, ss, dr)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This gives us the figures in the lecture: HA>RA in the baseline calibration, but HA=RA under zero liquidity. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "## Direct and indirect effects of monetary policy\n",
    "\n",
    "Now let's decompose the total response into its constituent effects discussed in class - the \"direct\" effect from the `r` and the \"indirect\" effect from the `Y`, ie the endogenous response of labor demand in GE to increased consumption, resulting in more labor income and increased consumption, etc."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For this, we need to call the `jacobian` routine of SSJ, which gives us the Jacobians of any block's outputs with respect to its inputs. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can see what we need to give `jacobian` by calling the help again"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here we see that `.jacobian` takes arguments (in order):\n",
    "1. `SteadyStateDict` (the output of a call to `.steady_state`, or `.solve_steady_state`),\n",
    "2. a `list` of inputs the user wants to calculate the Jacobian with respect to\n",
    "\n",
    "Now we're ready to calculate the direct effect of a change in the interest rate on consumption. \n",
    "\n",
    "Note that, as the DAG shows us, going from the primitive `r` to the household block requires first going through the `ex_post_rate` block to get the ex-post rate from the ex-ante rate. What we really want is this combined Jacobian."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A simple way to do this is to form a `CombinedBlock`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can now take the Jacobian of this block with respect to its two inputs `Y` and `r_ante`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We use this to get the direct/indirect decomposition. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "dC, dC_dr, dC_dY = {}, {}, {}\n",
    "dC['ha'] = irf['ha']['C']\n",
    "dC_dr['ha'] = J['C']['r_ante']  @ dr\n",
    "dC_dY['ha'] = J['C']['Y'] @ dC['ha']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Check that this the decomposition sums to the total: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.allclose(dC_dr['ha']+dC_dY['ha'], dC['ha'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, we'll redo the same calculations in the RA model (getting the income effect from the IMPCs), and then compare the direct/indirect decomposition in both models. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "# Re-do the same calculations for the RA model\n",
    "dC['ra'] = irf['ra']['C']\n",
    "\n",
    "Mra = tutil.compute_ra_impcs(ss['ra']['beta'], T=T)\n",
    "dC_dY['ra'] = Mra @ dC['ra']\n",
    "dC_dr['ra'] = dC['ra'] - dC_dY['ra']\n",
    "\n",
    "\n",
    "tutil.figure_2(dC, dC_dr, dC_dY)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We get back to the result from the lecture notes. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "## Cyclical income risk\n",
    "\n",
    "Before we introduce cyclical income risk, let's look at the baseline case of an impulse response to a future, anticipated interest rate cut (forward guidance) with our baseline HA model with acyclical income risk."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A simple way to do this is to get the general equilibrium Jacobian of the model, which we obtain with `solve_jacobian`. Then, the $s$th column of that Jacobian gives us the impulse response to a forward-guidance `r` shock at date $s$. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "# Model jacobian in ra and ha models\n",
    "T = 300\n",
    "G = {}\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's plot this using `Tshock = 10`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Tshock = 10\n",
    "fig = plt.subplots(1, 1, figsize=(6, 5))\n",
    "plt.plot(..., label='RA')\n",
    "plt.plot(..., label='HA')\n",
    "plt.axhline(y=0, color='#808080', linestyle=':')\n",
    "plt.title('Impulse response on output to forward guidance')\n",
    "plt.xlabel(r\"Year $(t)$\")\n",
    "plt.ylabel('% deviation from ss')\n",
    "plt.legend(framealpha=0)\n",
    "plt.tight_layout()\n",
    "#plt.savefig('Export/FG_RA_HA_incomeinc_lec2.pdf', format='pdf', transparent=True)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As discussed in class, the standard HA model with acyclical income risk does not solve the forward guidance puzzle. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "To go beyond this, we want to move away from income being just $e \\times Y$, so that low-`e` agents are more or less sensitive to `Y`. \n",
    "\n",
    "To do this, we'll change the `income` hetinput. We'll use the following specification from Auclert \\& Rognlie (2018)\n",
    "\n",
    "$$ y_{it} = Y \\cdot \\frac{e_{it}^{1 + \\zeta \\log(Y)}}{\\mathbb{E}[e_{it}^{1 + \\zeta \\log(Y)}]}$$\n",
    "\n",
    "Call this new function `income_cyclical`. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "def income_cyclical(Y,e_grid, e_pdf, zeta):\n",
    "    y = Y * e_grid ** (1 + zeta * np.log(Y)) / np.vdot(e_grid ** (1 + zeta * np.log(Y)), e_pdf)\n",
    "    return y"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This `hetinput` maps `zeta`, a variable that scales the degree of cyclicality of income risk, to `y` the post-tax labor income of households. When `zeta` = 0, income risk is acyclical but when `zeta` >/< 0, income risk is pro-/counter-cyclical\n",
    "\n",
    "Note that this function requires the pdf of `e`. Since the standard SSJ implementation of `make_grids` doesn't this to us, we rewrite it here with this extra output."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def make_grids_pdf(rho_e, sd_e, n_e, min_a, max_a, n_a):\n",
    "    e_grid, e_pdf, Pi = sj.grids.markov_rouwenhorst(rho_e, sd_e, n_e)\n",
    "    a_grid = sj.grids.asset_grid(min_a, max_a, n_a)\n",
    "    return e_grid, e_pdf, Pi, a_grid"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We now define a new `hetblock` and a new `model` with this new functionality added. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ss['ha_cyc'] = ..."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`zeta` should not change the steady state, we check this. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "np.isclose(ss['ha_cyc']['beta'], ss['ha']['beta'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "# Model jacobian in ha models with cyclical risk\n",
    "for zeta, mod in zip([-0.5, 0.5], ['ha_counter', 'ha_pro']):\n",
    "    ss_cyc = ss['ha_cyc'].copy()\n",
    "    ss_cyc['zeta'] = zeta    # income risk does not change the steady state\n",
    "    G[mod] = ha_cyc.solve_jacobian(ss_cyc, ['Y'], ['asset_mkt'], ['r_ante'], T=T)\n",
    "\n",
    "tutil.figure_4(G)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "## Maturity structure\n",
    "\n",
    "As we saw in lecture, allowing for varying maturity/duration of assets can affect the equilibrium response of consumption to interest rate shocks. \n",
    "\n",
    "Recall the pricing (no-arbitrage) equation for Calvo bonds\n",
    "\n",
    "$$\n",
    "1 + r_t^{ante} = \\frac{1 + \\delta q_{t+1}}{q_t}\n",
    "$$\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "We implement this equation as a `SolvedBlock` that returns `q` taking `r_ante` and `delta` as inputs\n",
    "\n",
    "Recall from the fiscal tutorial that the syntax `q(1)` here would denote $q_{t+1}$, given `q` denotes $q_t$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "@sj.solved(unknowns={'q': (0.1, 25)}, targets=['qres'], solver=\"brentq\")\n",
    "def longbonds_price(q, r_ante, delta):\n",
    "    qres = q - (1 + delta * q(+1)) / (1 + r_ante)\n",
    "    return qres"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "Additionally, we will create another block, which yields the ex-post return of the Calvo bonds. The reason we need this additional block is in this model there are valuation effects, i.e. when a shock occurs at period $t = 0$, it re-values the bond's price $q$ but because this shock was unanticipated as of $t = -1$, the actual realized return `r_post` will differ from the expected return as of $t = -1$, i.e. the $t = -1$ ex-ante rate"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "@sj.simple\n",
    "def ex_post_longbonds_rate(q, delta):\n",
    "    r = (1 + delta * q)/q(-1) - 1\n",
    "    return r"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "Let's now create a new model object called `long`, solve for its steady state and recreate the figure we saw in lecture"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "calibration_long = calibration.copy()\n",
    "calibration_long['delta'] = 0.95\n",
    "\n",
    "models['long'] = sj.create_model([household_simple, ex_post_longbonds_rate, longbonds_price, mkt_clearing_simple], name=\"HA model with long-duration bonds\")\n",
    "ss['long'] = models['long'].solve_steady_state(calibration_long, {'beta': (0.75, 0.9)}, ['asset_mkt'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we want, we can get the DAG of this model and double-check it vs the lecture"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "irf_long = {}\n",
    "for mod in ['ha', 'ra', 'long']:\n",
    "    hh_name = 'household_ra' if mod == 'ra' else 'household'\n",
    "    irf_long[mod] = ...\n",
    "\n",
    "tutil.figure_5(irf_long, ss)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "## Nominal assets - the \"Fisher\" effect\n",
    "\n",
    "If instead of real assets households held nominal assets then in spite of following a real rate rule, inflation will matter due to a valuation effect on nominal debt due to inflation surprises, which we call the \"Fisher effect\"\n",
    "\n",
    "First, we will build out the nominal side of the model, writing down a block to represent the New Keynesian Phillips Curve"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "@sj.simple\n",
    "def nkpc(pi, Y, C, theta_w, vphi, frisch, markup_ss, eis, beta):\n",
    "    kappa_w = (1 - theta_w) * (1 - beta * theta_w)/theta_w\n",
    "    piw = pi\n",
    "    piwres = kappa_w * (vphi * (Y)**(1/frisch) - 1/markup_ss * C**(-1/eis)) + beta * piw(1) - piw\n",
    "    return piwres, piw\n",
    "\n",
    "\n",
    "@sj.simple\n",
    "def ex_post_nom_asset_rate(r_ante, pi):\n",
    "    i = r_ante + pi(1)\n",
    "    r = i(-1) - pi\n",
    "    return r\n",
    "\n",
    "calibration_nom_asset = calibration.copy()\n",
    "calibration_nom_asset['pi'] = 0.  # look at the zero-inflation steady state\n",
    "calibration_nom_asset['markup_ss'] = 1.015\n",
    "calibration_nom_asset['theta_w'] = 0.66\n",
    "calibration_nom_asset['frisch'] = 0.5\n",
    "\n",
    "models['nom_assets'] = ...\n",
    "ss['nom_assets'] = ..."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The DAG now here, as promised: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "# Compute IRF in different models\n",
    "G, irf_Y, irf_r_post = {}, {}, {}\n",
    "theta_list = [1 - 1e-10, 0.8, 0.66]\n",
    "for i, theta_w in enumerate(theta_list):\n",
    "    calibration_theta = calibration_nom_asset.copy()\n",
    "    calibration_theta[\"theta_w\"] = theta_w\n",
    "    ss_nom = models['nom_assets'].solve_steady_state(calibration_theta, {'beta': 0.8, 'vphi': 0.8}, ['asset_mkt', 'piwres'])\n",
    "    irf_here = models['nom_assets'].solve_impulse_linear(ss_nom, ['Y', 'pi'], ['asset_mkt', 'piwres'], {'r_ante': dr})\n",
    "    irf_Y[i], irf_r_post[i] = irf_here[\"Y\"], irf_here[\"r\"]\n",
    "\n",
    "tutil.figure_6(theta_list, irf_Y, irf_r_post)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Exercise 1: obtaining the solution using the GE Jacobian"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "Instead of `solve_impulse_linear`, calculate the general equilibrium Jacobian using the `solve_jacobian` routine. Then, calculate the output response to the interest rate shock `dr`, and check that you get the same solution. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "pycharm": {
     "name": "#%% md\n"
    }
   },
   "source": [
    "**Exercise 2**: Instead of using a `CombinedBlock` to get the direct effect of monetary policy through `r`, do the same by manually chaining the relevant Jacobians along the DAG using the matrix multiply (`@`) operator. \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Exercise 3**: Code up the fiscal policy model from class, replicate the result about the ranking of the output effect of monetary policy vs that of the fiscal effect on demand.\n",
    "\n",
    "Take the initial steady state levels of $G = 0.2$ and $B = 0.5$ and set the adjustment coefficients $\\phi_G = \\phi_T = 0.1$.\n",
    "Also, set the shock to the ex-ante interest rate to have an impact effect of 0.1 percentage point and a persistence of $0.7$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Exercise 4**: Code up the investment model from class, replicate the result on the complementarity between HA and investment, using a *unit* shock to the ex-ante real interest rate with persistence $0.7$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Exercise 5**: Code up the Taylor rule model from class, replicate the result on the effect of a monetary policy shock (a shock to the Taylor rule) depending on $\\phi$"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.8.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
