|
| 1 | +""" |
| 2 | +Valuation for finite-horizon American call options in discrete time. |
| 3 | +
|
| 4 | +""" |
| 5 | + |
| 6 | +include("s_approx.jl") |
| 7 | +using QuantEcon, LinearAlgebra, IterTools |
| 8 | + |
| 9 | +"Creates an instance of the option model with log S_t = Z_t + W_t." |
| 10 | +function create_american_option_model(; |
| 11 | + n=100, μ=10.0, # Markov state grid size and mean value |
| 12 | + ρ=0.98, ν=0.2, # persistence and volatility for Markov state |
| 13 | + s=0.3, # volatility parameter for W_t |
| 14 | + r=0.01, # interest rate |
| 15 | + K=10.0, T=200) # strike price and expiration date |
| 16 | + t_vals = collect(1:T+1) |
| 17 | + mc = tauchen(n, ρ, ν) |
| 18 | + z_vals, Q = mc.state_values .+ μ, mc.p |
| 19 | + w_vals, φ, β = [-s, s], [0.5, 0.5], 1 / (1 + r) |
| 20 | + e(t, i_w, i_z) = (t ≤ T) * (z_vals[i_z] + w_vals[i_w] - K) |
| 21 | + return (; t_vals, z_vals, w_vals, Q, φ, T, β, K, e) |
| 22 | +end |
| 23 | + |
| 24 | +"The continuation value operator." |
| 25 | +function C(h, model) |
| 26 | + (; t_vals, z_vals, w_vals, Q, φ, T, β, K, e) = model |
| 27 | + Ch = similar(h) |
| 28 | + z_idx, w_idx = eachindex(z_vals), eachindex(w_vals) |
| 29 | + for (t, i_z) in product(t_vals, z_idx) |
| 30 | + out = 0.0 |
| 31 | + for (i_w′, i_z′) in product(w_idx, z_idx) |
| 32 | + t′ = min(t + 1, T + 1) |
| 33 | + out += max(e(t′, i_w′, i_z′), h[t′, i_z′]) * |
| 34 | + Q[i_z, i_z′] * φ[i_w′] |
| 35 | + end |
| 36 | + Ch[t, i_z] = β * out |
| 37 | + end |
| 38 | + return Ch |
| 39 | +end |
| 40 | + |
| 41 | +"Compute the continuation value function by successive approx." |
| 42 | +function compute_cvf(model) |
| 43 | + h_init = zeros(length(model.t_vals), length(model.z_vals)) |
| 44 | + h_star = successive_approx(h -> C(h, model), h_init) |
| 45 | + return h_star |
| 46 | +end |
| 47 | + |
| 48 | + |
| 49 | +# Plots |
| 50 | + |
| 51 | +using PyPlot |
| 52 | +using LaTeXStrings |
| 53 | +PyPlot.matplotlib[:rc]("text", usetex=true) # allow tex rendering |
| 54 | +fontsize=16 |
| 55 | + |
| 56 | + |
| 57 | +function plot_contours(; savefig=false, |
| 58 | + figname="./figures/american_option_1.pdf") |
| 59 | + |
| 60 | + model = create_american_option_model() |
| 61 | + (; t_vals, z_vals, w_vals, Q, φ, T, β, K, e) = model |
| 62 | + h_star = compute_cvf(model) |
| 63 | + |
| 64 | + fig, axes = plt.subplots(3, 1, figsize=(7, 11)) |
| 65 | + z_idx, w_idx = eachindex(z_vals), eachindex(w_vals) |
| 66 | + H = zeros(length(w_vals), length(z_vals)) |
| 67 | + |
| 68 | + for (ax_index, t) in zip(1:3, (1, 195, 199)) |
| 69 | + |
| 70 | + ax = axes[ax_index, 1] |
| 71 | + |
| 72 | + for (i_w, i_z) in product(w_idx, z_idx) |
| 73 | + H[i_w, i_z] = e(t, i_w, i_z) - h_star[t, i_z] |
| 74 | + end |
| 75 | + |
| 76 | + cs1 = ax.contourf(w_vals, z_vals, transpose(H), alpha=0.5) |
| 77 | + ctr1 = ax.contour(w_vals, z_vals, transpose(H), levels=[0.0]) |
| 78 | + plt.clabel(ctr1, inline=1, fontsize=13) |
| 79 | + plt.colorbar(cs1, ax=ax) #, format="%.6f") |
| 80 | + |
| 81 | + ax.set_title(L"t=" * "$t", fontsize=fontsize) |
| 82 | + ax.set_xlabel(L"w", fontsize=fontsize) |
| 83 | + ax.set_ylabel(L"z", fontsize=fontsize) |
| 84 | + |
| 85 | + end |
| 86 | + |
| 87 | + fig.tight_layout() |
| 88 | + if savefig |
| 89 | + fig.savefig(figname) |
| 90 | + end |
| 91 | + plt.show() |
| 92 | +end |
| 93 | + |
| 94 | + |
| 95 | +function plot_strike(; savefig=false, |
| 96 | + fontsize=12, |
| 97 | + figname="./figures/american_option_2.pdf") |
| 98 | + |
| 99 | + model = create_american_option_model() |
| 100 | + (; t_vals, z_vals, w_vals, Q, φ, T, β, K, e) = model |
| 101 | + h_star = compute_cvf(model) |
| 102 | + |
| 103 | + # Built Markov chains for simulation |
| 104 | + z_mc = MarkovChain(Q, z_vals) |
| 105 | + P_φ = zeros(length(w_vals), length(w_vals)) |
| 106 | + for i in eachindex(w_vals) # Build IID chain |
| 107 | + P_φ[i, :] = φ |
| 108 | + end |
| 109 | + w_mc = MarkovChain(P_φ, w_vals) |
| 110 | + |
| 111 | + |
| 112 | + y_min = minimum(z_vals) + minimum(w_vals) |
| 113 | + y_max = maximum(z_vals) + maximum(w_vals) |
| 114 | + fig, axes = plt.subplots(3, 1, figsize=(7, 12)) |
| 115 | + |
| 116 | + for ax in axes |
| 117 | + |
| 118 | + # Generate price series |
| 119 | + z_draws = simulate_indices(z_mc, T, init=Int(length(z_vals) / 2 - 10)) |
| 120 | + w_draws = simulate_indices(w_mc, T) |
| 121 | + s_vals = z_vals[z_draws] + w_vals[w_draws] |
| 122 | + |
| 123 | + # Find the exercise date, if any. |
| 124 | + exercise_date = T + 1 |
| 125 | + for t in 1:T |
| 126 | + if e(t, w_draws[t], z_draws[t]) ≥ h_star[w_draws[t], z_draws[t]] |
| 127 | + exercise_date = t |
| 128 | + end |
| 129 | + end |
| 130 | + |
| 131 | + @assert exercise_date ≤ T "Option not exercised." |
| 132 | + |
| 133 | + # Plot |
| 134 | + ax.set_ylim(y_min, y_max) |
| 135 | + ax.set_xlim(1, T) |
| 136 | + ax.fill_between(1:T, ones(T) * K, ones(T) * y_max, alpha=0.2) |
| 137 | + ax.plot(1:T, s_vals, label=L"S_t") |
| 138 | + ax.plot((exercise_date,), (s_vals[exercise_date]), "ko") |
| 139 | + ax.vlines((exercise_date,), 0, (s_vals[exercise_date]), ls="--", colors="black") |
| 140 | + ax.legend(loc="upper left", fontsize=fontsize) |
| 141 | + ax.text(-10, 11, "in the money", fontsize=fontsize, rotation=90) |
| 142 | + ax.text(-10, 7.2, "out of the money", fontsize=fontsize, rotation=90) |
| 143 | + ax.text(exercise_date-20, 6, #s_vals[exercise_date]+0.8, |
| 144 | + "exercise date", fontsize=fontsize) |
| 145 | + ax.set_xticks((1, T)) |
| 146 | + ax.set_yticks((y_min, y_max)) |
| 147 | + end |
| 148 | + |
| 149 | + if savefig |
| 150 | + fig.savefig(figname) |
| 151 | + end |
| 152 | + plt.show() |
| 153 | +end |
0 commit comments