{
"cells": [
{
"cell_type": "markdown",
"id": "c2329268",
"metadata": {},
"source": [
"\n",
""
]
},
{
"cell_type": "markdown",
"id": "4498d48d",
"metadata": {},
"source": [
"# Asset Pricing II: The Lucas Asset Pricing Model\n",
"\n",
"\n",
""
]
},
{
"cell_type": "markdown",
"id": "a724fbdf",
"metadata": {},
"source": [
"## Contents\n",
"\n",
"- [Asset Pricing II: The Lucas Asset Pricing Model](#Asset-Pricing-II:-The-Lucas-Asset-Pricing-Model) \n",
" - [Overview](#Overview) \n",
" - [The Lucas Model](#The-Lucas-Model) \n",
" - [Exercises](#Exercises) "
]
},
{
"cell_type": "markdown",
"id": "1eb877fa",
"metadata": {},
"source": [
"## Overview\n",
"\n",
"As stated in an [earlier lecture](https://python-intro.quantecon.org/markov_asset.html), an asset is a claim on a stream of prospective payments.\n",
"\n",
"What is the correct price to pay for such a claim?\n",
"\n",
"The elegant asset pricing model of Lucas [[Lucas, 1978](https://python-advanced.quantecon.org/zreferences.html#id174)] attempts to answer this question in an equilibrium setting with risk-averse agents.\n",
"\n",
"While we mentioned some consequences of Lucas’ model [earlier](https://python.quantecon.org/markov_asset.html#risk-neutral-pricing), it is now time to work through the model more carefully and try to understand where the fundamental asset pricing equation comes from.\n",
"\n",
"A side benefit of studying Lucas’ model is that it provides a beautiful illustration of model building in general and equilibrium pricing in competitive models in particular.\n",
"\n",
"Another difference to our [first asset pricing lecture](https://python-intro.quantecon.org/markov_asset.html) is that the state space and shock will be continuous rather than discrete.\n",
"\n",
"Let’s start with some imports:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f8dd2dba",
"metadata": {
"hide-output": false
},
"outputs": [],
"source": [
"import numpy as np\n",
"from numba import njit, prange\n",
"from scipy.stats import lognorm\n",
"import matplotlib.pyplot as plt"
]
},
{
"cell_type": "markdown",
"id": "a44fa3eb",
"metadata": {},
"source": [
"## The Lucas Model\n",
"\n",
"\n",
"\n",
"Lucas studied a pure exchange economy with a representative consumer (or household), where\n",
"\n",
"- *Pure exchange* means that all endowments are exogenous. \n",
"- *Representative* consumer means that either \n",
" - there is a single consumer (sometimes also referred to as a household), or \n",
" - all consumers have identical endowments and preferences \n",
"\n",
"\n",
"Either way, the assumption of a representative agent means that prices adjust to eradicate desires to trade.\n",
"\n",
"This makes it very easy to compute competitive equilibrium prices."
]
},
{
"cell_type": "markdown",
"id": "794a8a51",
"metadata": {},
"source": [
"### Basic Setup\n",
"\n",
"Let’s review the setup."
]
},
{
"cell_type": "markdown",
"id": "33683090",
"metadata": {},
"source": [
"#### Assets\n",
"\n",
"\n",
"\n",
"There is a single “productive unit” that costlessly generates a sequence of consumption goods $ \\{y_t\\}_{t=0}^{\\infty} $.\n",
"\n",
"Another way to view $ \\{y_t\\}_{t=0}^{\\infty} $ is as a *consumption endowment* for this economy.\n",
"\n",
"We will assume that this endowment is Markovian, following the exogenous process\n",
"\n",
"$$\n",
"y_{t+1} = G(y_t, \\xi_{t+1})\n",
"$$\n",
"\n",
"Here $ \\{ \\xi_t \\} $ is an IID shock sequence with known distribution $ \\phi $ and $ y_t \\geq 0 $.\n",
"\n",
"An asset is a claim on all or part of this endowment stream.\n",
"\n",
"The consumption goods $ \\{y_t\\}_{t=0}^{\\infty} $ are nonstorable, so holding assets is the only way to transfer wealth into the future.\n",
"\n",
"For the purposes of intuition, it’s common to think of the productive unit as a “tree” that produces fruit.\n",
"\n",
"Based on this idea, a “Lucas tree” is a claim on the consumption endowment."
]
},
{
"cell_type": "markdown",
"id": "22d88269",
"metadata": {},
"source": [
"#### Consumers\n",
"\n",
"\n",
"\n",
"A representative consumer ranks consumption streams $ \\{c_t\\} $ according to the time separable utility functional\n",
"\n",
"\n",
"\n",
"$$\n",
"\\mathbb{E} \\sum_{t=0}^\\infty \\beta^t u(c_t) \\tag{34.1}\n",
"$$\n",
"\n",
"Here\n",
"\n",
"- $ \\beta \\in (0,1) $ is a fixed discount factor. \n",
"- $ u $ is a strictly increasing, strictly concave, continuously differentiable period utility function. \n",
"- $ \\mathbb{E} $ is a mathematical expectation. "
]
},
{
"cell_type": "markdown",
"id": "47dfd13d",
"metadata": {},
"source": [
"### Pricing a Lucas Tree\n",
"\n",
"\n",
"\n",
"What is an appropriate price for a claim on the consumption endowment?\n",
"\n",
"We’ll price an *ex-dividend* claim, meaning that\n",
"\n",
"- the seller retains this period’s dividend \n",
"- the buyer pays $ p_t $ today to purchase a claim on \n",
" - $ y_{t+1} $ and \n",
" - the right to sell the claim tomorrow at price $ p_{t+1} $ \n",
"\n",
"\n",
"Since this is a competitive model, the first step is to pin down consumer\n",
"behavior, taking prices as given.\n",
"\n",
"Next, we’ll impose equilibrium constraints and try to back out prices.\n",
"\n",
"In the consumer problem, the consumer’s control variable is the share $ \\pi_t $ of the claim held in each period.\n",
"\n",
"Thus, the consumer problem is to maximize [(34.1)](#equation-lt-uf) subject to\n",
"\n",
"$$\n",
"c_t + \\pi_{t+1} p_t \\leq \\pi_t y_t + \\pi_t p_t\n",
"$$\n",
"\n",
"along with $ c_t \\geq 0 $ and $ 0 \\leq \\pi_t \\leq 1 $ at each $ t $.\n",
"\n",
"The decision to hold share $ \\pi_t $ is actually made at time $ t-1 $.\n",
"\n",
"But this value is inherited as a state variable at time $ t $, which explains the choice of subscript."
]
},
{
"cell_type": "markdown",
"id": "e638dffa",
"metadata": {},
"source": [
"#### The Dynamic Program\n",
"\n",
"\n",
"\n",
"We can write the consumer problem as a dynamic programming problem.\n",
"\n",
"Our first observation is that prices depend on current information, and current information is really just the endowment process up until the current period.\n",
"\n",
"In fact, the endowment process is Markovian, so that the only relevant\n",
"information is the current state $ y \\in \\mathbb R_+ $ (dropping the time subscript).\n",
"\n",
"This leads us to guess an equilibrium where price is a function $ p $ of $ y $.\n",
"\n",
"Remarks on the solution method\n",
"\n",
"- Since this is a competitive (read: price taking) model, the consumer will take this function $ p $ as given. \n",
"- In this way, we determine consumer behavior given $ p $ and then use equilibrium conditions to recover $ p $. \n",
"- This is the standard way to solve competitive equilibrium models. \n",
"\n",
"\n",
"Using the assumption that price is a given function $ p $ of $ y $, we write the value function and constraint as\n",
"\n",
"$$\n",
"v(\\pi, y) = \\max_{c, \\pi'}\n",
" \\left\\{\n",
" u(c) + \\beta \\int v(\\pi', G(y, z)) \\phi(dz)\n",
" \\right\\}\n",
"$$\n",
"\n",
"subject to\n",
"\n",
"\n",
"\n",
"$$\n",
"c + \\pi' p(y) \\leq \\pi y + \\pi p(y) \\tag{34.2}\n",
"$$\n",
"\n",
"We can invoke the fact that utility is increasing to claim equality in [(34.2)](#equation-preltbe) and hence eliminate the constraint, obtaining\n",
"\n",
"\n",
"\n",
"$$\n",
"v(\\pi, y) = \\max_{\\pi'}\n",
" \\left\\{\n",
" u[\\pi (y + p(y)) - \\pi' p(y) ] + \\beta \\int v(\\pi', G(y, z)) \\phi(dz)\n",
" \\right\\} \\tag{34.3}\n",
"$$\n",
"\n",
"The solution to this dynamic programming problem is an optimal policy expressing either $ \\pi' $ or $ c $ as a function of the state $ (\\pi, y) $.\n",
"\n",
"- Each one determines the other, since $ c(\\pi, y) = \\pi (y + p(y))- \\pi' (\\pi, y) p(y) $ "
]
},
{
"cell_type": "markdown",
"id": "a88dd0f0",
"metadata": {},
"source": [
"#### Next Steps\n",
"\n",
"What we need to do now is determine equilibrium prices.\n",
"\n",
"It seems that to obtain these, we will have to\n",
"\n",
"1. Solve this two-dimensional dynamic programming problem for the optimal policy. \n",
"1. Impose equilibrium constraints. \n",
"1. Solve out for the price function $ p(y) $ directly. \n",
"\n",
"\n",
"However, as Lucas showed, there is a related but more straightforward way to do this."
]
},
{
"cell_type": "markdown",
"id": "be61a7d7",
"metadata": {},
"source": [
"#### Equilibrium Constraints\n",
"\n",
"\n",
"\n",
"Since the consumption good is not storable, in equilibrium we must have $ c_t = y_t $ for all $ t $.\n",
"\n",
"In addition, since there is one representative consumer (alternatively, since\n",
"all consumers are identical), there should be no trade in equilibrium.\n",
"\n",
"In particular, the representative consumer owns the whole tree in every period, so $ \\pi_t = 1 $ for all $ t $.\n",
"\n",
"Prices must adjust to satisfy these two constraints."
]
},
{
"cell_type": "markdown",
"id": "762466e7",
"metadata": {},
"source": [
"#### The Equilibrium Price Function\n",
"\n",
"\n",
"\n",
"Now observe that the first-order condition for [(34.3)](#equation-ltbe) can be written as\n",
"\n",
"$$\n",
"u'(c) p(y) = \\beta \\int v_1'(\\pi', G(y, z)) \\phi(dz)\n",
"$$\n",
"\n",
"where $ v'_1 $ is the derivative of $ v $ with respect to its first argument.\n",
"\n",
"To obtain $ v'_1 $ we can simply differentiate the right-hand side of\n",
"[(34.3)](#equation-ltbe) with respect to $ \\pi $, yielding\n",
"\n",
"$$\n",
"v'_1(\\pi, y) = u'(c) (y + p(y))\n",
"$$\n",
"\n",
"Next, we impose the equilibrium constraints while combining the last two\n",
"equations to get\n",
"\n",
"\n",
"\n",
"$$\n",
"p(y) = \\beta \\int \\frac{u'[G(y, z)]}{u'(y)} [G(y, z) + p(G(y, z))] \\phi(dz) \\tag{34.4}\n",
"$$\n",
"\n",
"In sequential rather than functional notation, we can also write this as\n",
"\n",
"\n",
"\n",
"$$\n",
"p_t = \\mathbb{E}_t \\left[ \\beta \\frac{u'(c_{t+1})}{u'(c_t)} ( y_{t+1} + p_{t+1} ) \\right] \\tag{34.5}\n",
"$$\n",
"\n",
"This is the famous consumption-based asset pricing equation.\n",
"\n",
"Before discussing it further we want to solve out for prices."
]
},
{
"cell_type": "markdown",
"id": "f5849754",
"metadata": {},
"source": [
"### Solving the Model\n",
"\n",
"\n",
"\n",
"Equation [(34.4)](#equation-lteeq) is a *functional equation* in the unknown function $ p $.\n",
"\n",
"The solution is an equilibrium price function $ p^* $.\n",
"\n",
"Let’s look at how to obtain it."
]
},
{
"cell_type": "markdown",
"id": "a77dbff5",
"metadata": {},
"source": [
"#### Setting up the Problem\n",
"\n",
"Instead of solving for it directly we’ll follow Lucas’ indirect approach, first setting\n",
"\n",
"\n",
"\n",
"$$\n",
"f(y) := u'(y) p(y) \\tag{34.6}\n",
"$$\n",
"\n",
"so that [(34.4)](#equation-lteeq) becomes\n",
"\n",
"\n",
"\n",
"$$\n",
"f(y) = h(y) + \\beta \\int f[G(y, z)] \\phi(dz) \\tag{34.7}\n",
"$$\n",
"\n",
"Here $ h(y) := \\beta \\int u'[G(y, z)] G(y, z) \\phi(dz) $ is a function that\n",
"depends only on the primitives.\n",
"\n",
"Equation [(34.7)](#equation-lteeq2) is a functional equation in $ f $.\n",
"\n",
"The plan is to solve out for $ f $ and convert back to $ p $ via [(34.6)](#equation-ltffp).\n",
"\n",
"To solve [(34.7)](#equation-lteeq2) we’ll use a standard method: convert it to a fixed point problem.\n",
"\n",
"First, we introduce the operator $ T $ mapping $ f $ into $ Tf $ as defined by\n",
"\n",
"\n",
"\n",
"$$\n",
"(Tf)(y) = h(y) + \\beta \\int f[G(y, z)] \\phi(dz) \\tag{34.8}\n",
"$$\n",
"\n",
"In what follows, we refer to $ T $ as the Lucas operator.\n",
"\n",
"The reason we do this is that a solution to [(34.7)](#equation-lteeq2) now corresponds to a\n",
"function $ f^* $ satisfying $ (Tf^*)(y) = f^*(y) $ for all $ y $.\n",
"\n",
"In other words, a solution is a *fixed point* of $ T $.\n",
"\n",
"This means that we can use fixed point theory to obtain and compute the solution."
]
},
{
"cell_type": "markdown",
"id": "d5070f7e",
"metadata": {},
"source": [
"#### A Little Fixed Point Theory\n",
"\n",
"\n",
"\n",
"Let $ cb\\mathbb{R}_+ $ be the set of continuous bounded functions $ f \\colon \\mathbb{R}_+ \\to \\mathbb{R}_+ $.\n",
"\n",
"We now show that\n",
"\n",
"1. $ T $ has exactly one fixed point $ f^* $ in $ cb\\mathbb{R}_+ $. \n",
"1. For any $ f \\in cb\\mathbb{R}_+ $, the sequence $ T^k f $ converges\n",
" uniformly to $ f^* $. \n",
"\n",
"\n",
">**Note**\n",
">\n",
">If you find the mathematics heavy going you can take 1–2 as given and skip to the [next section](#lt-comp-eg)\n",
"\n",
"Recall the [Banach contraction mapping theorem](https://en.wikipedia.org/wiki/Banach_fixed-point_theorem).\n",
"\n",
"It tells us that the previous statements will be true if we can find an\n",
"$ \\alpha < 1 $ such that\n",
"\n",
"\n",
"\n",
"$$\n",
"\\| Tf - Tg \\| \\leq \\alpha \\| f - g \\|,\n",
"\\qquad \\forall \\, f, g \\in cb\\mathbb{R}_+ \\tag{34.9}\n",
"$$\n",
"\n",
"Here $ \\|h\\| := \\sup_{x \\in \\mathbb{R}_+} |h(x)| $.\n",
"\n",
"To see that [(34.9)](#equation-ltbc) is valid, pick any $ f,g \\in cb\\mathbb{R}_+ $ and any $ y \\in \\mathbb{R}_+ $.\n",
"\n",
"Observe that, since integrals get larger when absolute values are moved to the\n",
"inside,\n",
"\n",
"$$\n",
"\\begin{aligned}\n",
" |Tf(y) - Tg(y)|\n",
" & = \\left| \\beta \\int f[G(y, z)] \\phi(dz)\n",
" -\\beta \\int g[G(y, z)] \\phi(dz) \\right|\n",
" \\\\\n",
" & \\leq \\beta \\int \\left| f[G(y, z)] - g[G(y, z)] \\right| \\phi(dz)\n",
" \\\\\n",
" & \\leq \\beta \\int \\| f - g \\| \\phi(dz)\n",
" \\\\\n",
" & = \\beta \\| f - g \\|\n",
"\\end{aligned}\n",
"$$\n",
"\n",
"Since the right-hand side is an upper bound, taking the sup over all $ y $\n",
"on the left-hand side gives [(34.9)](#equation-ltbc) with $ \\alpha := \\beta $.\n",
"\n",
"\n",
""
]
},
{
"cell_type": "markdown",
"id": "f553e41d",
"metadata": {},
"source": [
"### Computation – An Example\n",
"\n",
"\n",
"\n",
"The preceding discussion tells that we can compute $ f^* $ by picking any arbitrary $ f \\in cb\\mathbb{R}_+ $ and then iterating with $ T $.\n",
"\n",
"The equilibrium price function $ p^* $ can then be recovered by $ p^*(y) = f^*(y) / u'(y) $.\n",
"\n",
"Let’s try this when $ \\ln y_{t+1} = \\alpha \\ln y_t + \\sigma \\epsilon_{t+1} $ where $ \\{\\epsilon_t\\} $ is IID and standard normal.\n",
"\n",
"Utility will take the isoelastic form $ u(c) = c^{1-\\gamma}/(1-\\gamma) $, where $ \\gamma > 0 $ is the coefficient of relative risk aversion.\n",
"\n",
"We will set up a `LucasTree` class to hold parameters of the model"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e3dcd727",
"metadata": {
"hide-output": false
},
"outputs": [],
"source": [
"class LucasTree:\n",
" \"\"\"\n",
" Class to store parameters of the Lucas tree model.\n",
"\n",
" \"\"\"\n",
"\n",
" def __init__(self,\n",
" γ=2, # CRRA utility parameter\n",
" β=0.95, # Discount factor\n",
" α=0.90, # Correlation coefficient\n",
" σ=0.1, # Volatility coefficient\n",
" grid_size=100):\n",
"\n",
" self.γ, self.β, self.α, self.σ = γ, β, α, σ\n",
"\n",
" # Set the grid interval to contain most of the mass of the\n",
" # stationary distribution of the consumption endowment\n",
" ssd = self.σ / np.sqrt(1 - self.α**2)\n",
" grid_min, grid_max = np.exp(-4 * ssd), np.exp(4 * ssd)\n",
" self.grid = np.linspace(grid_min, grid_max, grid_size)\n",
" self.grid_size = grid_size\n",
"\n",
" # Set up distribution for shocks\n",
" self.ϕ = lognorm(σ)\n",
" self.draws = self.ϕ.rvs(500)\n",
"\n",
" self.h = np.empty(self.grid_size)\n",
" for i, y in enumerate(self.grid):\n",
" self.h[i] = β * np.mean((y**α * self.draws)**(1 - γ))"
]
},
{
"cell_type": "markdown",
"id": "8f9f6505",
"metadata": {},
"source": [
"The following function takes an instance of the `LucasTree` and generates a\n",
"jitted version of the Lucas operator"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f2331c93",
"metadata": {
"hide-output": false
},
"outputs": [],
"source": [
"def operator_factory(tree, parallel_flag=True):\n",
"\n",
" \"\"\"\n",
" Returns approximate Lucas operator, which computes and returns the\n",
" updated function Tf on the grid points.\n",
"\n",
" tree is an instance of the LucasTree class\n",
"\n",
" \"\"\"\n",
"\n",
" grid, h = tree.grid, tree.h\n",
" α, β = tree.α, tree.β\n",
" z_vec = tree.draws\n",
"\n",
" @njit(parallel=parallel_flag)\n",
" def T(f):\n",
" \"\"\"\n",
" The Lucas operator\n",
" \"\"\"\n",
"\n",
" # Turn f into a function\n",
" Af = lambda x: np.interp(x, grid, f)\n",
"\n",
" Tf = np.empty_like(f)\n",
" # Apply the T operator to f using Monte Carlo integration\n",
" for i in prange(len(grid)):\n",
" y = grid[i]\n",
" Tf[i] = h[i] + β * np.mean(Af(y**α * z_vec))\n",
"\n",
" return Tf\n",
"\n",
" return T"
]
},
{
"cell_type": "markdown",
"id": "012eac7b",
"metadata": {},
"source": [
"To solve the model, we write a function that iterates using the Lucas operator\n",
"to find the fixed point."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b825b9c0",
"metadata": {
"hide-output": false
},
"outputs": [],
"source": [
"def solve_model(tree, tol=1e-6, max_iter=500):\n",
" \"\"\"\n",
" Compute the equilibrium price function associated with Lucas\n",
" tree\n",
"\n",
" * tree is an instance of LucasTree\n",
"\n",
" \"\"\"\n",
" # Simplify notation\n",
" grid, grid_size = tree.grid, tree.grid_size\n",
" γ = tree.γ\n",
"\n",
" T = operator_factory(tree)\n",
"\n",
" i = 0\n",
" f = np.ones_like(grid) # Initial guess of f\n",
" error = tol + 1\n",
" while error > tol and i < max_iter:\n",
" Tf = T(f)\n",
" error = np.max(np.abs(Tf - f))\n",
" f = Tf\n",
" i += 1\n",
"\n",
" price = f * grid**γ # Back out price vector\n",
"\n",
" return price"
]
},
{
"cell_type": "markdown",
"id": "cc81c002",
"metadata": {},
"source": [
"Solving the model and plotting the resulting price function"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "42a72ded",
"metadata": {
"hide-output": false
},
"outputs": [],
"source": [
"tree = LucasTree()\n",
"price_vals = solve_model(tree)\n",
"\n",
"fig, ax = plt.subplots(figsize=(10, 6))\n",
"ax.plot(tree.grid, price_vals, label='$p*(y)$')\n",
"ax.set_xlabel('$y$')\n",
"ax.set_ylabel('price')\n",
"ax.legend()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"id": "0de46b78",
"metadata": {},
"source": [
"We see that the price is increasing, even if we remove all serial correlation from the endowment process.\n",
"\n",
"The reason is that a larger current endowment reduces current marginal\n",
"utility.\n",
"\n",
"The price must therefore rise to induce the household to consume the entire endowment (and hence satisfy the resource constraint).\n",
"\n",
"What happens with a more patient consumer?\n",
"\n",
"Here the orange line corresponds to the previous parameters and the green line is price when $ \\beta = 0.98 $.\n",
"\n",
"\n",
"\n",
"![https://python-advanced.quantecon.org/_static/lecture_specific/lucas_model/solution_mass_ex2.png](https://python-advanced.quantecon.org/_static/lecture_specific/lucas_model/solution_mass_ex2.png)\n",
"\n",
" \n",
"We see that when consumers are more patient the asset becomes more valuable, and the price of the Lucas tree shifts up.\n",
"\n",
"Exercise 1 asks you to replicate this figure."
]
},
{
"cell_type": "markdown",
"id": "58c7dda3",
"metadata": {},
"source": [
"## Exercises\n",
"\n",
"\n",
""
]
},
{
"cell_type": "markdown",
"id": "2b5d1166",
"metadata": {},
"source": [
"## Exercise 34.1\n",
"\n",
"Replicate [the figure](#mass-lt-cb) to show how discount factors affect prices."
]
},
{
"cell_type": "markdown",
"id": "2ee2bf05",
"metadata": {},
"source": [
"## Solution to[ Exercise 34.1](https://python-advanced.quantecon.org/#lucas_ex1)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0b0406c5",
"metadata": {
"hide-output": false
},
"outputs": [],
"source": [
"fig, ax = plt.subplots(figsize=(10, 6))\n",
"\n",
"for β in (.95, 0.98):\n",
" tree = LucasTree(β=β)\n",
" grid = tree.grid\n",
" price_vals = solve_model(tree)\n",
" label = rf'$\\beta = {β}$'\n",
" ax.plot(grid, price_vals, lw=2, alpha=0.7, label=label)\n",
"\n",
"ax.legend(loc='upper left')\n",
"ax.set(xlabel='$y$', ylabel='price', xlim=(min(grid), max(grid)))\n",
"plt.show()"
]
}
],
"metadata": {
"date": 1714698771.3513288,
"filename": "lucas_model.md",
"kernelspec": {
"display_name": "Python",
"language": "python3",
"name": "python3"
},
"title": "Asset Pricing II: The Lucas Asset Pricing Model"
},
"nbformat": 4,
"nbformat_minor": 5
}