GitHub

@@ -28,22 +28,24 @@ kernelspec:

2828

:depth: 2

2929

```

303031+

```{include} _admonition/gpu.md

32+

```

33+3134

In addition to what's in Anaconda, this lecture will need the following libraries:

32353336

```{code-cell} ipython3

3437

:tags: [hide-output]

353836-

!pip install quantecon

37-

!pip install --upgrade yfinance

39+

!pip install --upgrade quantecon yfinance

3840

```

39414042

## Overview

414342-

{doc}`Previously <intro:ar1_processes>` we learned about linear scalar-valued stochastic processes (AR(1) models).

44+

Previously in {doc}`intro:ar1_processes`, we learned about linear scalar-valued stochastic processes (AR(1) models).

43454446

Now we generalize these linear models slightly by allowing the multiplicative coefficient to be stochastic.

454746-

Such processes are known as Kesten processes after German--American mathematician Harry Kesten (1931--2019)

48+

Such processes are known as Kesten processes after German--American mathematician Harry Kesten (1931--2019).

47494850

Although simple to write down, Kesten processes are interesting for at least two reasons:

4951

@@ -58,20 +60,13 @@ Let's start with some imports:

5860

import matplotlib.pyplot as plt

5961

import numpy as np

6062

import quantecon as qe

61-

```

62-63-

The following two lines are only added to avoid a `FutureWarning` caused by

64-

compatibility issues between pandas and matplotlib.

65-66-

```{code-cell} ipython3

67-

from pandas.plotting import register_matplotlib_converters

68-

register_matplotlib_converters()

63+

import yfinance as yf

6964

```

70657166

Additional technical background related to this lecture can be found in the

72-

monograph of {cite}`buraczewski2016stochastic`.

67+

monograph by {cite}`buraczewski2016stochastic`.

736874-

## Kesten Processes

69+

## Kesten processes

75707671

```{index} single: Kesten processes; heavy tails

7772

```

@@ -97,7 +92,7 @@ In particular, we will assume that

9792

* $\{a_t\}_{t \geq 1}$ is a nonnegative IID stochastic process and

9893

* $\{\eta_t\}_{t \geq 1}$ is another nonnegative IID stochastic process, independent of the first.

9994100-

### Example: GARCH Volatility

95+

### Example: GARCH volatility

1019610297

The GARCH model is common in financial applications, where time series such as asset returns exhibit time varying volatility.

10398

@@ -107,18 +102,16 @@ Composite Index for the period 1st January 2006 to 1st November 2019.

107102

(ndcode)=

108103109104

```{code-cell} ipython3

110-

import yfinance as yf

111-112-

s = yf.download('^IXIC', '2006-1-1', '2019-11-1', auto_adjust=False)['Adj Close']

105+

s = yf.download(

106+

"^IXIC", "2006-1-1", "2019-11-1", auto_adjust=False

107+

)["Adj Close"]

113108114109

r = s.pct_change()

115110116111

fig, ax = plt.subplots()

117-118112

ax.plot(r, alpha=0.7)

119-120-

ax.set_ylabel('returns', fontsize=12)

121-

ax.set_xlabel('date', fontsize=12)

113+

ax.set_ylabel("returns", fontsize=12)

114+

ax.set_xlabel("date", fontsize=12)

122115123116

plt.show()

124117

```

@@ -150,7 +143,7 @@ where $\{\zeta_t\}$ is again IID and independent of $\{\xi_t\}$.

150143151144

The volatility sequence $\{\sigma_t^2 \}$, which drives the dynamics of returns, is a Kesten process.

152145153-

### Example: Wealth Dynamics

146+

### Example: wealth dynamics

154147155148

Suppose that a given household saves a fixed fraction $s$ of its current wealth in every period.

156149

@@ -171,7 +164,7 @@ is a Kesten process.

171164172165

### Stationarity

173166174-

In earlier lectures, such as the one on {doc}`AR(1) processes <intro:ar1_processes>`, we introduced the notion of a stationary distribution.

167+

In earlier lectures, such as the one on {doc}`intro:ar1_processes`, we introduced the notion of a stationary distribution.

175168176169

In the present context, we can define a stationary distribution as follows:

177170

@@ -203,7 +196,7 @@ current state is drawn from $F^*$.

203196204197

The equality in {eq}`kp_stationary` states that this distribution is unchanged.

205198206-

### Cross-Sectional Interpretation

199+

### Cross-sectional interpretation

207200208201

There is an important cross-sectional interpretation of stationary distributions, discussed previously but worth repeating here.

209202

@@ -241,7 +234,7 @@ next period as it is this period.

241234242235

Since $y$ was chosen arbitrarily, the distribution is unchanged.

243236244-

### Conditions for Stationarity

237+

### Conditions for stationarity

245238246239

The Kesten process $X_{t+1} = a_{t+1} X_t + \eta_{t+1}$ does not always

247240

have a stationary distribution.

@@ -270,16 +263,16 @@ As one application of this result, we see that the wealth process

270263

{eq}`wealth_dynam` will have a unique stationary distribution whenever

271264

labor income has finite mean and $\mathbb E \ln R_t + \ln s < 0$.

272265273-

## Heavy Tails

266+

## Heavy tails

274267275268

Under certain conditions, the stationary distribution of a Kesten process has

276269

a Pareto tail.

277270278-

(See our {doc}`earlier lecture <intro:heavy_tails>` on heavy-tailed distributions for background.)

271+

(See our lecture {doc}`intro:heavy_tails` for background.)

279272280273

This fact is significant for economics because of the prevalence of Pareto-tailed distributions.

281274282-

### The Kesten--Goldie Theorem

275+

### The Kesten--Goldie theorem

283276284277

To state the conditions under which the stationary distribution of a Kesten process has a Pareto tail, we first recall that a random variable is called **nonarithmetic** if its distribution is not concentrated on $\{\dots, -2t, -t, 0, t, 2t, \ldots \}$ for any $t \geq 0$.

285278

@@ -333,39 +326,39 @@ If this occurs for several concurrent periods, the effects compound each other,

333326334327

This leads to spikes in the time series, which fill out the extreme right hand tail of the distribution.

335328336-

The spikes in the time series are visible in the following simulation, which generates of 10 paths when $a_t$ and $b_t$ are lognormal.

329+

The spikes in the time series are visible in the following simulation, which generates 10 paths when $a_t$ and $b_t$ are lognormal.

337330338331

```{code-cell} ipython3

339332

μ = -0.5

340333

σ = 1.0

341334342-

def kesten_ts(ts_length=100):

335+

def kesten_ts(rng, ts_length=100):

343336

x = np.zeros(ts_length)

344-

for t in range(ts_length-1):

345-

a = np.exp(μ + σ * np.random.randn())

346-

b = np.exp(np.random.randn())

337+

for t in range(ts_length - 1):

338+

a = np.exp(μ + σ * rng.standard_normal())

339+

b = np.exp(rng.standard_normal())

347340

x[t+1] = a * x[t] + b

348341

return x

349342350343

fig, ax = plt.subplots()

351344352345

num_paths = 10

353-

np.random.seed(12)

346+

rng = np.random.default_rng(12)

354347355348

for i in range(num_paths):

356-

ax.plot(kesten_ts())

349+

ax.plot(kesten_ts(rng))

357350358-

ax.set(xlabel='time', ylabel='$X_t$')

351+

ax.set(xlabel="time", ylabel="$X_t$")

359352

plt.show()

360353

```

361354362-

## Application: Firm Dynamics

355+

## Application: firm dynamics

363356364-

As noted in our {doc}`lecture on heavy tails <intro:heavy_tails>`, for common measures of firm size such as revenue or employment, the US firm size distribution exhibits a Pareto tail (see, e.g., {cite}`axtell2001zipf`, {cite}`gabaix2016power`).

357+

As noted in our lecture {doc}`intro:heavy_tails`, for common measures of firm size such as revenue or employment, the US firm size distribution exhibits a Pareto tail (see, e.g., {cite}`axtell2001zipf`, {cite}`gabaix2016power`).

365358366359

Let us try to explain this rather striking fact using the Kesten--Goldie Theorem.

367360368-

### Gibrat's Law

361+

### Gibrat's law

369362370363

It was postulated many years ago by Robert Gibrat {cite}`gibrat1931inegalites` that firm size evolves according to a simple rule whereby size next period is proportional to current size.

371364

@@ -412,7 +405,7 @@ In the exercises you are asked to show that {eq}`firm_dynam` is more

412405

consistent with the empirical findings presented above than Gibrat's law in

413406

{eq}`firm_dynam_gb`.

414407415-

### Heavy Tails

408+

### Heavy tails

416409417410

So what has this to do with Pareto tails?

418411

@@ -460,22 +453,22 @@ Here is one solution:

460453

years = 15

461454

days = years * 250

462455463-

def garch_ts(ts_length=days):

456+

def garch_ts(rng, ts_length=days):

464457

σ2 = 0

465458

r = np.zeros(ts_length)

466-

for t in range(ts_length-1):

467-

ξ = np.random.randn()

459+

for t in range(ts_length - 1):

460+

ξ = rng.standard_normal()

468461

σ2 = α_0 + σ2 * (α_1 * ξ**2 + β)

469-

r[t] = np.sqrt(σ2) * np.random.randn()

462+

r[t] = np.sqrt(σ2) * rng.standard_normal()

470463

return r

471464472465

fig, ax = plt.subplots()

473466474-

np.random.seed(12)

467+

rng = np.random.default_rng(12)

475468476-

ax.plot(garch_ts(), alpha=0.7)

469+

ax.plot(garch_ts(rng), alpha=0.7)

477470478-

ax.set(xlabel='time', ylabel='$\\sigma_t^2$')

471+

ax.set(xlabel="time", ylabel="$\\sigma_t^2$")

479472

plt.show()

480473

```

481474

@@ -673,41 +666,59 @@ s_init = 1.0 # initial condition for each firm

673666

```

674667675668

Here's one solution.

676-

First we generate the observations:

669+670+

We need to simulate a large cross-section of firms, so this is a good use case for JAX.

671+672+

We store the firm parameters in a `NamedTuple` and write the update rule {eq}`firm_dynam_ee` for a single firm as a pure function.

673+674+

```{code-cell} ipython3

675+

import jax

676+

import jax.numpy as jnp

677+

from functools import partial

678+

from typing import NamedTuple

679+680+681+

class Firm(NamedTuple):

682+

μ_a: float

683+

σ_a: float

684+

μ_b: float

685+

σ_b: float

686+

μ_e: float

687+

σ_e: float

688+

s_bar: float

689+690+691+

def update(s, key, firm):

692+

"Update a single firm's size s, given a fresh random key."

693+

a_key, b_key, e_key = jax.random.split(key, 3)

694+

a = jnp.exp(firm.μ_a + firm.σ_a * jax.random.normal(a_key))

695+

b = jnp.exp(firm.μ_b + firm.σ_b * jax.random.normal(b_key))

696+

e = jnp.exp(firm.μ_e + firm.σ_e * jax.random.normal(e_key))

697+

return jnp.where(s < firm.s_bar, e, a * s + b)

698+

```

699+700+

We simulate one firm for `T` periods with `lax.scan`, then use `vmap` to run all `M` firms in parallel.

701+702+

```{code-cell} ipython3

703+

@partial(jax.jit, static_argnames=("T", "M"))

704+

def generate_draws(firm, s_init=1.0, T=500, M=1_000_000, seed=0):

705+

keys = jax.random.split(jax.random.PRNGKey(seed), M)

706+707+

def sim_firm(key):

708+

step_keys = jax.random.split(key, T)

709+

s_final, _ = jax.lax.scan(

710+

lambda s, k: (update(s, k, firm), None), s_init, step_keys

711+

)

712+

return s_final

713+714+

return jax.vmap(sim_firm)(keys)

715+

```

716+717+

Now we build the firm from the parameters above and generate the draws.

677718678719

```{code-cell} ipython3

679-

from numba import jit, prange

680-

from numpy.random import randn

681-682-683-

@jit(parallel=True)

684-

def generate_draws(μ_a=-0.5,

685-

σ_a=0.1,

686-

μ_b=0.0,

687-

σ_b=0.5,

688-

μ_e=0.0,

689-

σ_e=0.5,

690-

s_bar=1.0,

691-

T=500,

692-

M=1_000_000,

693-

s_init=1.0):

694-695-

draws = np.empty(M)

696-

for m in prange(M):

697-

s = s_init

698-

for t in range(T):

699-

if s < s_bar:

700-

new_s = np.exp(μ_e + σ_e * randn())

701-

else:

702-

a = np.exp(μ_a + σ_a * randn())

703-

b = np.exp(μ_b + σ_b * randn())

704-

new_s = a * s + b

705-

s = new_s

706-

draws[m] = s

707-708-

return draws

709-710-

data = generate_draws()

720+

firm = Firm(μ_a, σ_a, μ_b, σ_b, μ_e, σ_e, s_bar)

721+

data = generate_draws(firm, s_init=s_init, T=T, M=M)

711722

```

712723713724

Now we produce the rank-size plot:

@@ -716,7 +727,7 @@ Now we produce the rank-size plot:

716727

fig, ax = plt.subplots()

717728718729

rank_data, size_data = qe.rank_size(data, c=0.01)

719-

ax.loglog(rank_data, size_data, 'o', markersize=3.0, alpha=0.5)

730+

ax.loglog(rank_data, size_data, "o", markersize=3.0, alpha=0.5)

720731

ax.set_xlabel("log rank")

721732

ax.set_ylabel("log size")

722733

Read the original on github.com ↗