import%20marimo%0A%0A__generated_with%20%3D%20%220.18.4%22%0Aapp%20%3D%20marimo.App()%0A%0Awith%20app.setup%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20import%20matplotlib%20as%20mpl%0A%20%20%20%20import%20matplotlib.pyplot%20as%20plt%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20seaborn%20as%20sns%0A%0A%20%20%20%20sns.set(font_scale%3D1.5)%0A%20%20%20%20sns.set_style(%22whitegrid%22)%0A%20%20%20%20sns.set_palette(%22deep%22)%0A%20%20%20%20mpl.rc(%22figure%22%2C%20figsize%3D(9%2C%205)%2C%20dpi%3D100)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20from%20chebpy%20import%20Quasimatrix%2C%20chebfun%0A%20%20%20%20return%20Quasimatrix%2C%20chebfun%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20from%20math%20import%20erfc%20as%20_erfc%0A%0A%20%20%20%20_verfc%20%3D%20np.vectorize(_erfc)%0A%0A%20%20%20%20def%20norm_cdf(x)%3A%0A%20%20%20%20%20%20%20%20%22%22%22Standard%20normal%20CDF%20(no%20scipy%20needed).%22%22%22%0A%20%20%20%20%20%20%20%20return%200.5%20*%20_verfc(-np.asarray(x%2C%20dtype%3Dfloat)%20%2F%20np.sqrt(2))%0A%20%20%20%20return%20(norm_cdf%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Variance%20Swap%20Replication%0A%0A%20%20%20%20A%20**variance%20swap**%20pays%20the%20difference%20between%20realised%20variance%0A%20%20%20%20and%20a%20fixed%20strike%20%24K_%7B%5Ctext%7Bvar%7D%7D%24.%20%20A%20celebrated%20result%20of%20Carr%0A%20%20%20%20and%20Madan%20(1998)%20shows%20that%20the%20fair%20strike%20can%20be%20computed%20from%20a%0A%20%20%20%20static%20portfolio%20of%20out-of-the-money%20European%20calls%20and%20puts%3A%0A%0A%20%20%20%20%24%24%0A%20%20%20%20K_%7B%5Ctext%7Bvar%7D%7D%20%3D%20%5Cfrac%7B2%7D%7BT%7D%5C!%5Cleft%5B%5Cint_0%5E%7BF%7D%20%5Cfrac%7BP(K)%7D%7BK%5E2%7D%5C%2CdK%20%2B%20%5Cint_F%5E%7B%5Cinfty%7D%20%5Cfrac%7BC(K)%7D%7BK%5E2%7D%5C%2CdK%5Cright%5D%0A%20%20%20%20%24%24%0A%0A%20%20%20%20where%20%24F%24%20is%20the%20forward%20price%2C%20%24T%24%20the%20maturity%2C%20and%20%24C(K)%24%2C%20%24P(K)%24%0A%20%20%20%20are%20European%20call%20and%20put%20prices%20as%20functions%20of%20strike.%0A%0A%20%20%20%20The%20key%20identity%20underlying%20this%20formula%20is%20a%20**payoff%20decomposition**%3A%0A%0A%20%20%20%20%24%24%0A%20%20%20%20-%5Clog%5C!%5Cfrac%7BS_T%7D%7BF%7D%20%3D%20-%5Cfrac%7BS_T%20-%20F%7D%7BF%7D%20%2B%20%5Cint_0%5E%7BF%7D%20%5Cfrac%7B(K%20-%20S_T)%5E%2B%7D%7BK%5E2%7D%5C%2CdK%20%2B%20%5Cint_F%5E%7B%5Cinfty%7D%20%5Cfrac%7B(S_T%20-%20K)%5E%2B%7D%7BK%5E2%7D%5C%2CdK%0A%20%20%20%20%24%24%0A%0A%20%20%20%20This%20notebook%20uses%20ChebPy%20**Chebfuns**%20and%20**quasimatrices**%20to%0A%20%20%20%20turn%20these%20integrals%20into%20exact%20arithmetic%2C%20drawing%20on%20the%20same%0A%20%20%20%20hat-function%20fitting%20strategy%20demonstrated%20in%20the%20quasimatrix%0A%20%20%20%20notebook.%0A%0A%20%20%20%20%3E%20P.%20Carr%20%26%20D.%20Madan%2C%20%22Towards%20a%20theory%20of%20volatility%20trading%22%2C%0A%20%20%20%20%3E%20*Volatility%3A%20New%20Estimation%20Techniques%20for%20Pricing%20Derivatives*%2C%0A%20%20%20%20%3E%20Risk%20Books%2C%201998.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%201.%20Black%E2%80%93Scholes%20option%20prices%20as%20Chebfuns%0A%0A%20%20%20%20We%20begin%20by%20constructing%20the%20Black-Scholes%20call%20and%20put%20prices%20as%0A%20%20%20%20Chebfun%20objects%20%E2%80%94%20smooth%20functions%20of%20the%20strike%20%24K%24%20on%20a%20truncated%0A%20%20%20%20domain%20%24%5BK_%7B%5Cmin%7D%2C%20K_%7B%5Cmax%7D%5D%24.%0A%0A%20%20%20%20%7C%20Parameter%20%7C%20Value%20%7C%0A%20%20%20%20%7C-----------%7C-------%7C%0A%20%20%20%20%7C%20Spot%20%24S_0%24%20%7C%20100%20%7C%0A%20%20%20%20%7C%20Forward%20%24F%24%20%7C%20100%20(zero%20rates)%20%7C%0A%20%20%20%20%7C%20Volatility%20%24%5Csigma%24%20%7C%2020%20%25%20%7C%0A%20%20%20%20%7C%20Maturity%20%24T%24%20%7C%201%20year%20%7C%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20S0%20%3D%20100.0%0A%20%20%20%20F%20%3D%20100.0%0A%20%20%20%20sigma%20%3D%200.20%0A%20%20%20%20T%20%3D%201.0%0A%20%20%20%20return%20F%2C%20S0%2C%20T%2C%20sigma%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(F%2C%20S0%2C%20T%2C%20chebfun%2C%20norm_cdf%2C%20sigma)%3A%0A%20%20%20%20K_lo%2C%20K_hi%20%3D%2020.0%2C%20300.0%0A%0A%20%20%20%20def%20_bs_call(K)%3A%0A%20%20%20%20%20%20%20%20K%20%3D%20np.asarray(K%2C%20dtype%3Dfloat)%0A%20%20%20%20%20%20%20%20d1%20%3D%20(np.log(S0%20%2F%20K)%20%2B%200.5%20*%20sigma**2%20*%20T)%20%2F%20(sigma%20*%20np.sqrt(T))%0A%20%20%20%20%20%20%20%20d2%20%3D%20d1%20-%20sigma%20*%20np.sqrt(T)%0A%20%20%20%20%20%20%20%20return%20S0%20*%20norm_cdf(d1)%20-%20K%20*%20norm_cdf(d2)%0A%0A%20%20%20%20def%20_bs_put(K)%3A%0A%20%20%20%20%20%20%20%20K%20%3D%20np.asarray(K%2C%20dtype%3Dfloat)%0A%20%20%20%20%20%20%20%20d1%20%3D%20(np.log(S0%20%2F%20K)%20%2B%200.5%20*%20sigma**2%20*%20T)%20%2F%20(sigma%20*%20np.sqrt(T))%0A%20%20%20%20%20%20%20%20d2%20%3D%20d1%20-%20sigma%20*%20np.sqrt(T)%0A%20%20%20%20%20%20%20%20return%20K%20*%20norm_cdf(-d2)%20-%20S0%20*%20norm_cdf(-d1)%0A%0A%20%20%20%20C%20%3D%20chebfun(_bs_call%2C%20%5BF%2C%20K_hi%5D)%0A%20%20%20%20P%20%3D%20chebfun(_bs_put%2C%20%5BK_lo%2C%20F%5D)%0A%0A%20%20%20%20print(f%22Call%20C(K)%20on%20%5B%7BF%7D%2C%20%7BK_hi%7D%5D%20%20%E2%80%94%20length%20%7Blen(C)%7D%22)%0A%20%20%20%20print(f%22Put%20%20P(K)%20on%20%5B%7BK_lo%7D%2C%20%7BF%7D%5D%20%E2%80%94%20length%20%7Blen(P)%7D%22)%0A%20%20%20%20return%20C%2C%20K_hi%2C%20K_lo%2C%20P%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(C%2C%20F%2C%20K_hi%2C%20K_lo%2C%20P)%3A%0A%20%20%20%20_fig%2C%20(_ax1%2C%20_ax2)%20%3D%20plt.subplots(1%2C%202%2C%20figsize%3D(14%2C%205))%0A%0A%20%20%20%20_kp%20%3D%20np.linspace(K_lo%2C%20F%2C%20300)%0A%20%20%20%20_ax1.plot(_kp%2C%20P(_kp)%2C%20linewidth%3D2%2C%20color%3D%22C3%22)%0A%20%20%20%20_ax1.set_title(%22OTM%20Put%20Price%20%24P(K)%24%22)%0A%20%20%20%20_ax1.set_xlabel(%22Strike%20%24K%24%22)%0A%20%20%20%20_ax1.set_ylabel(%22Price%22)%0A%20%20%20%20_ax1.grid(True)%0A%0A%20%20%20%20_kc%20%3D%20np.linspace(F%2C%20K_hi%2C%20300)%0A%20%20%20%20_ax2.plot(_kc%2C%20C(_kc)%2C%20linewidth%3D2%2C%20color%3D%22C0%22)%0A%20%20%20%20_ax2.set_title(%22OTM%20Call%20Price%20%24C(K)%24%22)%0A%20%20%20%20_ax2.set_xlabel(%22Strike%20%24K%24%22)%0A%20%20%20%20_ax2.grid(True)%0A%0A%20%20%20%20plt.tight_layout()%0A%20%20%20%20plt.show()%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%202.%20The%20Carr%E2%80%93Madan%20integral%0A%0A%20%20%20%20The%20fair%20variance%20strike%20is%0A%0A%20%20%20%20%24%24%0A%20%20%20%20K_%7B%5Ctext%7Bvar%7D%7D%20%3D%20%5Cfrac%7B2%7D%7BT%7D%5C!%5Cleft%5B%5Cint_%7BK_%7B%5Cmin%7D%7D%5E%7BF%7D%20%5Cfrac%7BP(K)%7D%7BK%5E2%7D%5C%2CdK%20%2B%20%5Cint_F%5E%7BK_%7B%5Cmax%7D%7D%20%5Cfrac%7BC(K)%7D%7BK%5E2%7D%5C%2CdK%5Cright%5D%0A%20%20%20%20%24%24%0A%0A%20%20%20%20Since%20%24C(K)%24%20and%20%24P(K)%24%20are%20Chebfuns%2C%20dividing%20by%20the%20identity%0A%20%20%20%20function%20%24K%5E2%24%20and%20calling%20%60.sum()%60%20evaluates%20each%20integral%20to%0A%20%20%20%20machine%20precision.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(C%2C%20F%2C%20K_hi%2C%20K_lo%2C%20P%2C%20T%2C%20chebfun%2C%20sigma)%3A%0A%20%20%20%20K_put%20%3D%20chebfun(%22x%22%2C%20%5BK_lo%2C%20F%5D)%0A%20%20%20%20K_call%20%3D%20chebfun(%22x%22%2C%20%5BF%2C%20K_hi%5D)%0A%0A%20%20%20%20put_integrand%20%3D%20P%20%2F%20K_put**2%0A%20%20%20%20call_integrand%20%3D%20C%20%2F%20K_call**2%0A%0A%20%20%20%20I_put%20%3D%20put_integrand.sum()%0A%20%20%20%20I_call%20%3D%20call_integrand.sum()%0A%0A%20%20%20%20K_var%20%3D%20(2.0%20%2F%20T)%20*%20(I_put%20%2B%20I_call)%0A%20%20%20%20exact%20%3D%20sigma**2%0A%0A%20%20%20%20print(f%22Put%20%20integral%20%3A%20%7Bfloat(I_put)%3A.10f%7D%22)%0A%20%20%20%20print(f%22Call%20integral%20%3A%20%7Bfloat(I_call)%3A.10f%7D%22)%0A%20%20%20%20print(f%22K_var%20(ChebPy)%3A%20%7Bfloat(K_var)%3A.10f%7D%22)%0A%20%20%20%20print(f%22%CF%83%C2%B2%20(exact)%20%20%20%20%3A%20%7Bexact%3A.10f%7D%22)%0A%20%20%20%20print(f%22Relative%20error%3A%20%7Babs(float(K_var)%20-%20exact)%20%2F%20exact%3A.2e%7D%22)%0A%20%20%20%20return%20call_integrand%2C%20put_integrand%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(F%2C%20K_hi%2C%20K_lo%2C%20call_integrand%2C%20put_integrand)%3A%0A%20%20%20%20_fig%2C%20_ax%20%3D%20plt.subplots(figsize%3D(10%2C%204))%0A%0A%20%20%20%20_kp%20%3D%20np.linspace(K_lo%20%2B%201%2C%20F%2C%20300)%0A%20%20%20%20_kc%20%3D%20np.linspace(F%2C%20K_hi%2C%20300)%0A%20%20%20%20_ax.fill_between(_kp%2C%20put_integrand(_kp)%2C%20alpha%3D0.3%2C%20color%3D%22C3%22%2C%20label%3D%22%24P(K)%2FK%5E2%24%20%20(puts)%22)%0A%20%20%20%20_ax.plot(_kp%2C%20put_integrand(_kp)%2C%20color%3D%22C3%22%2C%20linewidth%3D2)%0A%20%20%20%20_ax.fill_between(_kc%2C%20call_integrand(_kc)%2C%20alpha%3D0.3%2C%20color%3D%22C0%22%2C%20label%3D%22%24C(K)%2FK%5E2%24%20%20(calls)%22)%0A%20%20%20%20_ax.plot(_kc%2C%20call_integrand(_kc)%2C%20color%3D%22C0%22%2C%20linewidth%3D2)%0A%20%20%20%20_ax.axvline(F%2C%20color%3D%22k%22%2C%20linestyle%3D%22--%22%2C%20linewidth%3D1%2C%20label%3D%22%24F%24%22)%0A%20%20%20%20_ax.set_xlabel(%22Strike%20%24K%24%22)%0A%20%20%20%20_ax.set_ylabel(%22Integrand%22)%0A%20%20%20%20_ax.set_title(%22Carr%E2%80%93Madan%20integrand%3A%20option%20price%20%2F%20%24K%5E2%24%22)%0A%20%20%20%20_ax.legend()%0A%20%20%20%20_ax.grid(True)%0A%0A%20%20%20%20plt.tight_layout()%0A%20%20%20%20plt.show()%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%203.%20Payoff%20replication%20%E2%80%94%20the%20hat-function%20connection%0A%0A%20%20%20%20The%20Carr%E2%80%93Madan%20identity%20works%20at%20the%20**payoff**%20level%2C%20not%20just%0A%20%20%20%20through%20prices.%20%20For%20any%20realisation%20%24S_T%24%2C%20the%20log%20payoff%20decomposes%0A%20%20%20%20into%20a%20forward%20piece%20plus%20option%20payoffs%20integrated%20over%20strikes%3A%0A%0A%20%20%20%20%24%24%0A%20%20%20%20-%5Clog%5C!%5Cfrac%7BS_T%7D%7BF%7D%20%3D%20-%5Cfrac%7BS_T%20-%20F%7D%7BF%7D%20%2B%20%5Cint_0%5E%7BF%7D%20%5Cfrac%7B(K%20-%20S_T)%5E%2B%7D%7BK%5E2%7D%5C%2CdK%20%2B%20%5Cint_F%5E%7B%5Cinfty%7D%20%5Cfrac%7B(S_T%20-%20K)%5E%2B%7D%7BK%5E2%7D%5C%2CdK%0A%20%20%20%20%24%24%0A%0A%20%20%20%20In%20practice%20we%20discretise%20the%20strike%20axis.%20%20Each%20option%20payoff%20is%20a%0A%20%20%20%20**piecewise-linear%20ramp**%20in%20%24S_T%24.%20Note%20that%20a%20linear%20change%20of%0A%20%20%20%20basis%20produces%20the%20**triangular%20hat%20function**%20from%20the%20quasimatrix%0A%20%20%20%20notebook.%20To%20option%20traders%20this%20basis%20has%20the%20interpretation%20of%20a%0A%20%20%20%20portfolio%20of%20butterflies.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(F%2C%20chebfun)%3A%0A%20%20%20%20S_lo%2C%20S_hi%20%3D%2030.0%2C%20250.0%0A%0A%20%20%20%20log_payoff%20%3D%20chebfun(lambda%20s%3A%20-np.log(s%20%2F%20F)%2C%20%5BS_lo%2C%20S_hi%5D)%0A%20%20%20%20fwd_payoff%20%3D%20chebfun(lambda%20s%3A%20-(s%20-%20F)%20%2F%20F%2C%20%5BS_lo%2C%20S_hi%5D)%0A%20%20%20%20residual%20%3D%20log_payoff%20-%20fwd_payoff%0A%0A%20%20%20%20print(f%22log%20payoff%20length%3A%20%7Blen(log_payoff)%7D%22)%0A%20%20%20%20residual%0A%20%20%20%20return%20S_hi%2C%20S_lo%2C%20fwd_payoff%2C%20log_payoff%2C%20residual%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(F%2C%20S_hi%2C%20S_lo%2C%20fwd_payoff%2C%20log_payoff%2C%20residual)%3A%0A%20%20%20%20_fig%2C%20(_ax1%2C%20_ax2)%20%3D%20plt.subplots(1%2C%202%2C%20figsize%3D(14%2C%205))%0A%0A%20%20%20%20_ss%20%3D%20np.linspace(S_lo%2C%20S_hi%2C%20500)%0A%20%20%20%20_ax1.plot(_ss%2C%20log_payoff(_ss)%2C%20linewidth%3D2%2C%20label%3Dr%22%24-%5Clog(S_T%2FF)%24%22)%0A%20%20%20%20_ax1.plot(_ss%2C%20fwd_payoff(_ss)%2C%20%22--%22%2C%20linewidth%3D1.5%2C%20label%3Dr%22%24-(S_T%20-%20F)%2FF%24%22)%0A%20%20%20%20_ax1.axvline(F%2C%20color%3D%22k%22%2C%20linestyle%3D%22%3A%22%2C%20linewidth%3D0.8)%0A%20%20%20%20_ax1.legend()%0A%20%20%20%20_ax1.set_xlabel(%22%24S_T%24%22)%0A%20%20%20%20_ax1.set_title(%22Log%20payoff%20and%20forward%20component%22)%0A%20%20%20%20_ax1.grid(True)%0A%0A%20%20%20%20_ax2.plot(_ss%2C%20residual(_ss)%2C%20linewidth%3D2%2C%20color%3D%22C2%22)%0A%20%20%20%20_ax2.axvline(F%2C%20color%3D%22k%22%2C%20linestyle%3D%22%3A%22%2C%20linewidth%3D0.8)%0A%20%20%20%20_ax2.set_xlabel(%22%24S_T%24%22)%0A%20%20%20%20_ax2.set_title(%22Residual%20to%20replicate%20with%20options%22)%0A%20%20%20%20_ax2.grid(True)%0A%0A%20%20%20%20plt.tight_layout()%0A%20%20%20%20plt.show()%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%20Building%20the%20option%20payoff%20quasimatrix%0A%0A%20%20%20%20We%20will%20work%20with%20the%20classic%20put%20and%20call%20basis%20directly.%20To%20do%20this%0A%20%20%20%20we%20assemble%20a%20quasimatrix%20of%20OTM%20option%20payoffs%20and%20replicate%20the%0A%20%20%20%20log%20contract.%0A%0A%20%20%20%20We%20pick%20%24n%24%20equally%20spaced%20strikes%20spanning%20%24%5BS_%7B%5Cmin%7D%2C%20S_%7B%5Cmax%7D%5D%24.%0A%20%20%20%20For%20each%20strike%20%24K_i%24%3A%0A%0A%20%20%20%20-%20If%20%24K_i%20%5Cle%20F%24%3A%20include%20the%20put%20payoff%20%24(K_i%20-%20S_T)%5E%2B%24%0A%20%20%20%20-%20If%20%24K_i%20%3E%20F%24%3A%20include%20the%20call%20payoff%20%24(S_T%20-%20K_i)%5E%2B%24%0A%0A%20%20%20%20Stacked%20into%20a%20quasimatrix%2C%20the%20columns%20are%20piecewise-linear%20ramps%20%E2%80%94%0A%20%20%20%20the%20continuous%20analogues%20of%20the%20hat-function%20basis%20from%20the%0A%20%20%20%20quasimatrix%20notebook.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(F%2C%20Quasimatrix%2C%20S_hi%2C%20S_lo%2C%20chebfun)%3A%0A%20%20%20%20n_strikes%20%3D%2016%0A%20%20%20%20strikes%20%3D%20np.linspace(50.0%2C%20200.0%2C%20n_strikes)%0A%20%20%20%20all_bkpts%20%3D%20sorted(%7BS_lo%2C%20*list(strikes)%2C%20S_hi%7D)%0A%0A%20%20%20%20_cols%20%3D%20%5B%5D%0A%20%20%20%20for%20_Ki%20in%20strikes%3A%0A%20%20%20%20%20%20%20%20if%20_Ki%20%3C%3D%20F%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_cols.append(chebfun(lambda%20s%2C%20_K%3D_Ki%3A%20np.maximum(_K%20-%20s%2C%200.0)%2C%20all_bkpts))%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_cols.append(chebfun(lambda%20s%2C%20_K%3D_Ki%3A%20np.maximum(s%20-%20_K%2C%200.0)%2C%20all_bkpts))%0A%0A%20%20%20%20Q_pay%20%3D%20Quasimatrix(_cols)%0A%20%20%20%20print(f%22Payoff%20quasimatrix%20shape%3A%20%7BQ_pay.shape%7D%22)%0A%20%20%20%20return%20Q_pay%2C%20n_strikes%2C%20strikes%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(F%2C%20Q_pay)%3A%0A%20%20%20%20_fig%2C%20_ax%20%3D%20plt.subplots(figsize%3D(10%2C%203))%0A%20%20%20%20Q_pay.plot(ax%3D_ax)%0A%20%20%20%20_ax.axvline(F%2C%20color%3D%22k%22%2C%20linestyle%3D%22--%22%2C%20linewidth%3D1%2C%20label%3D%22%24F%24%20%20(forward)%22)%0A%20%20%20%20_ax.set_xlabel(%22%24S_T%24%22)%0A%20%20%20%20_ax.set_title(%22OTM%20option%20payoffs%20%E2%80%94%20columns%20of%20the%20quasimatrix%22)%0A%20%20%20%20_ax.legend()%0A%20%20%20%20_ax.grid(True)%0A%20%20%20%20_fig%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%20Least-squares%20weights%20via%20%60Quasimatrix.solve()%60%0A%0A%20%20%20%20The%20Carr%E2%80%93Madan%20formula%20tells%20us%20to%20weight%20each%20option%20by%0A%20%20%20%20%24%5CDelta%20K%20%2F%20K_i%5E2%24.%20%20Multiplying%20the%20quasimatrix%20by%20this%20weight%0A%20%20%20%20vector%20gives%20an%20approximation%20to%20the%20residual%20log%20payoff.%20%20At%2016%0A%20%20%20%20strikes%20the%20Carr%E2%80%93Madan%20replication%20is%20somewhat%20close%20but%20a%20long%0A%20%20%20%20way%20from%20perfect.%20%20The%20problem%20is%20domain%20truncation%20error%20%E2%80%94%0A%20%20%20%20reflecting%20the%20fact%20that%20we%20cannot%20use%20strikes%20all%20the%20way%20down%0A%20%20%20%20to%20zero%20and%20all%20the%20way%20up%20to%20infinity%20in%20practice%20%E2%80%94%20which%0A%20%20%20%20distorts%20the%20ability%20of%20the%20%241%2FK%5E2%24%20weights%20to%20replicate%0A%20%20%20%20accurately.%0A%0A%20%20%20%20Instead%20of%20the%20theoretical%20Carr%E2%80%93Madan%20weights%2C%20we%20can%20let%20ChebPy%0A%20%20%20%20find%20the%20**optimal**%20weights%20that%20minimise%0A%20%20%20%20%24%5C%7C%20Q%5C%2C%5Cmathbf%7Bw%7D%20-%20g%20%5C%7C_2%24%2C%20exactly%20as%20in%20the%20hat-function%0A%20%20%20%20least-squares%20fit%20from%20the%20quasimatrix%20notebook.%20%20This%20does%20much%0A%20%20%20%20better%20under%20realistic%20strike-range%20truncation.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(F%2C%20Q_pay%2C%20S_hi%2C%20S_lo%2C%20n_strikes%2C%20residual%2C%20strikes)%3A%0A%20%20%20%20_dK%20%3D%20strikes%5B1%5D%20-%20strikes%5B0%5D%0A%20%20%20%20w_cm%20%3D%20_dK%20%2F%20strikes**2%0A%20%20%20%20rep_cm%20%3D%20Q_pay%20%40%20w_cm%0A%0A%20%20%20%20w_ls%20%3D%20Q_pay.solve(residual)%0A%20%20%20%20rep_ls%20%3D%20Q_pay%20%40%20w_ls%0A%0A%20%20%20%20_err_cm%20%3D%20float((residual%20-%20rep_cm).norm(2))%0A%20%20%20%20_err_ls%20%3D%20float((residual%20-%20rep_ls).norm(2))%0A%0A%20%20%20%20_ss%20%3D%20np.linspace(S_lo%2C%20S_hi%2C%20500)%0A%0A%20%20%20%20_fig%2C%20(_ax1%2C%20_ax2)%20%3D%20plt.subplots(%0A%20%20%20%20%20%20%20%202%2C%0A%20%20%20%20%20%20%20%201%2C%0A%20%20%20%20%20%20%20%20figsize%3D(10%2C%209)%2C%0A%20%20%20%20%20%20%20%20height_ratios%3D(2%2C%201)%2C%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20Top%3A%20replication%20comparison%20(three%20lines)%0A%20%20%20%20_ax1.plot(_ss%2C%20residual(_ss)%2C%20linewidth%3D2%2C%20label%3D%22Approximation%20Target%22)%0A%20%20%20%20_ax1.plot(%0A%20%20%20%20%20%20%20%20_ss%2C%0A%20%20%20%20%20%20%20%20rep_cm(_ss)%2C%0A%20%20%20%20%20%20%20%20%22--%22%2C%0A%20%20%20%20%20%20%20%20linewidth%3D3%2C%0A%20%20%20%20%20%20%20%20color%3D%22C3%22%2C%0A%20%20%20%20%20%20%20%20label%3Drf%22Carr%E2%80%93Madan%20(%24%5Cpropto%201%2FK%5E2%24)%20%5B%24L%5E2%24%20err%20%3D%20%7B_err_cm%3A.3f%7D%5D%22%2C%0A%20%20%20%20)%0A%20%20%20%20_ax1.plot(%0A%20%20%20%20%20%20%20%20_ss%2C%20rep_ls(_ss)%2C%20%22--%22%2C%20linewidth%3D3%2C%20color%3D%22C2%22%2C%20label%3Df%22Chebfun%20least-squares%20%20%5B%24L%5E2%24%20err%20%3D%20%7B_err_ls%3A.3f%7D%5D%22%0A%20%20%20%20)%0A%20%20%20%20_ax1.axvline(F%2C%20color%3D%22k%22%2C%20linestyle%3D%22%3A%22%2C%20linewidth%3D0.8)%0A%20%20%20%20_ax1.legend()%0A%20%20%20%20_ax1.set_xlabel(%22%24S_T%24%22)%0A%20%20%20%20_ax1.set_title(f%22Discrete%20replication%20(%7Bn_strikes%7D%20strikes)%22)%0A%20%20%20%20_ax1.grid(True)%0A%0A%20%20%20%20%23%20Bottom%3A%20grouped%20bar%20chart%20of%20weights%0A%20%20%20%20_bar_w%20%3D%200.3%20*%20_dK%0A%20%20%20%20_ax2.bar(%0A%20%20%20%20%20%20%20%20strikes%20-%20_bar_w%20%2F%202%2C%0A%20%20%20%20%20%20%20%20w_cm%2C%0A%20%20%20%20%20%20%20%20width%3D_bar_w%2C%0A%20%20%20%20%20%20%20%20color%3D%22C3%22%2C%0A%20%20%20%20%20%20%20%20alpha%3D0.7%2C%0A%20%20%20%20%20%20%20%20edgecolor%3D%22C3%22%2C%0A%20%20%20%20%20%20%20%20label%3Dr%22Carr%E2%80%93Madan%20(%24%5Cpropto%201%2FK%5E2%24)%22%2C%0A%20%20%20%20)%0A%20%20%20%20_ax2.bar(%0A%20%20%20%20%20%20%20%20strikes%20%2B%20_bar_w%20%2F%202%2C%20w_ls%2C%20width%3D_bar_w%2C%20color%3D%22C2%22%2C%20alpha%3D0.7%2C%20edgecolor%3D%22C2%22%2C%20label%3D%22Chebfun%20least-squares%22%0A%20%20%20%20)%0A%20%20%20%20_ax2.set_xlabel(%22Strike%20%24K%24%22)%0A%20%20%20%20_ax2.set_ylabel(%22Weight%22)%0A%20%20%20%20_ax2.set_title(%22Portfolio%20weights%22)%0A%20%20%20%20_ax2.legend()%0A%20%20%20%20_ax2.grid(True%2C%20axis%3D%22y%22)%0A%0A%20%20%20%20plt.tight_layout()%0A%20%20%20%20plt.show()%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
155c93df342ee73d48d06a81827ba233