forked from QuantConnect/Research
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
## Description Replying to this issue Related Issue How to extract Implied volatility from Quantconnect QuantConnect#47 Types of changes -[ ] Bug fix (non-breaking change which fixes an issue) -[ ] Refactor (non-breaking change which improves implementation) -[ ] New feature (non-breaking change which adds functionality) -[ x] Non-functional change (xml comments/documentation/etc) Checklist: -[ x] My code follows the code style of this project. -[ x] I have read the CONTRIBUTING [document](https://github.com/QuantConnect/Lean/blob/master/CONTRIBUTING.md). -[ x] My branch follows the naming convention bug-<issue#>-<description> or feature-<issue#>-<description>
- Loading branch information
1 parent
71c0ff8
commit 59417e5
Showing
1 changed file
with
266 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,266 @@ | ||
{ | ||
"nbformat": 4, | ||
"nbformat_minor": 0, | ||
"metadata": { | ||
"colab": { | ||
"provenance": [] | ||
}, | ||
"kernelspec": { | ||
"name": "python3", | ||
"display_name": "Python 3" | ||
}, | ||
"language_info": { | ||
"name": "python" | ||
} | ||
}, | ||
"cells": [ | ||
{ | ||
"cell_type": "markdown", | ||
"source": [ | ||
"![QuantConnect Logo](https://cdn.quantconnect.com/web/i/icon.png)\n", | ||
"<hr>" | ||
], | ||
"metadata": { | ||
"id": "6iZIAgxW8Ari" | ||
} | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"source": [ | ||
"# Dupire model\n", | ||
"$C\\left(S_0, K, T\\right)=\\int_K^{\\infty} d S_T \\varphi\\left(S_T, T ; S_0\\right)\\left(S_T-K\\right)$\n", | ||
"### risk-neutral density function φ of the final spot $S_T$\n", | ||
"$\\varphi\\left(K, T ; S_0\\right)=\\frac{\\partial^2 C}{\\partial K^2}$\n", | ||
"## Drift\n", | ||
"$\\mu = r_t − D_t$\n", | ||
"## local volatility\n", | ||
"$\\sigma^2\\left(K, T, S_0\\right)=\\frac{\\frac{\\partial C}{\\partial T}}{\\frac{1}{2} K^2 \\frac{\\partial^2 C}{\\partial K^2}}$\n", | ||
"## undiscounted option price C in terms of the strike price K:\n", | ||
"### which is the Dupire equation when the underlying stock has risk-neutral drift μ.\n", | ||
"$\\frac{\\partial C}{\\partial T}=\\frac{\\sigma^2 K^2}{2} \\frac{\\partial^2 C}{\\partial K^2}+\\left(r_t-D_t\\right)\\left(C-K \\frac{\\partial C}{\\partial K}\\right)$\n", | ||
"# BSM model\n", | ||
"$Call=S N\\left(d_1\\right)-K e^{-r \\tau} N\\left(d_2\\right)$ \n", | ||
"\\\n", | ||
"$d_1=\\frac{\\ln (S / K)+\\left(r-y+\\sigma^2 / 2\\right) \\tau}{\\sigma \\sqrt{\\tau}}$\n", | ||
"\\\n", | ||
"$d_2=\\frac{\\ln (S / K)+\\left(r-y-\\sigma^2 / 2\\right) \\tau}{\\sigma \\sqrt{\\tau}}=d_1-\\sigma \\sqrt{\\tau}$\n", | ||
"\n", | ||
"## PDF of Normal \n", | ||
"$f(x)=\\frac{1}{\\sigma \\sqrt{2 \\pi}} e^{-\\frac{1}{2}\\left(\\frac{x-\\mu}{\\sigma}\\right)^2}$\n", | ||
"\n", | ||
"## CDF of Normal\n", | ||
"$N(x)=\\frac{1}{\\sqrt{2 \\pi}} \\int_{-\\infty}^x e^{-z^2 / 2} d z$\n" | ||
], | ||
"metadata": { | ||
"id": "fqocn6D58JDW" | ||
} | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"source": [ | ||
"# Importing Libraries" | ||
], | ||
"metadata": { | ||
"id": "LeIimwLE8FOu" | ||
} | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"source": [ | ||
"import sympy as smp \n", | ||
"import numpy as np\n", | ||
"from sympy.stats import P, E, variance, Die, Normal\n", | ||
"from sympy import simplify\n" | ||
], | ||
"metadata": { | ||
"id": "dk37ioq7wpBX" | ||
}, | ||
"execution_count": 2, | ||
"outputs": [] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"source": [ | ||
"\n", | ||
"# Risk-neutral density function φ of the final spot $S_T$\n", | ||
"$\\varphi\\left(K, T ; S_0\\right)=\\frac{\\partial^2 C}{\\partial K^2}$" | ||
], | ||
"metadata": { | ||
"id": "BOs4Ha4_9Go_" | ||
} | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"source": [ | ||
"#define the function symbols\n", | ||
"Call,varphi,N = smp.symbols('Call varphi N', cls=smp.Function) \n", | ||
"S_0,K,T,S_T,D,r,sigma,tau ,d_1,d_2 = smp.symbols('S_0 K T S_T D r sigma tau d_1 d_2',real=True) \n", | ||
"\n", | ||
"\n", | ||
"Call = smp.symbols('Call', cls=smp.Function) \n", | ||
"f_d_1,f_d_2,F_d_1,F_d_2 = smp.symbols('f_d_1 f_d_2 F_d_1 F_d_2' , cls=smp.Function) \n", | ||
"mu_google,sigma_google= smp.symbols('mu_google sigma_google',real=True) \n", | ||
"f_d_1 = Normal('d_1', mu_google, sigma_google)\n", | ||
"f_d_2 = Normal('d_2', mu_google, sigma_google)\n", | ||
"\n", | ||
"\n", | ||
"F_d_1 = simplify(P(f_d_1>tau))\n", | ||
"F_d_2 = simplify(P(f_d_2>tau))\n", | ||
"Call = S_T * F_d_1 - K * smp.exp(-r*tau) * F_d_2\n", | ||
"\n", | ||
"\n", | ||
"varphi = smp.diff(Call,K,2)\n", | ||
"\n", | ||
"#varphi.subs([(Call,Call),(K,int(K))]).evalf()" | ||
], | ||
"metadata": { | ||
"id": "0G1VQsJF8M0M" | ||
}, | ||
"execution_count": 4, | ||
"outputs": [] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"source": [ | ||
"\n", | ||
"## Undiscounted option price C in terms of the strike price K:\n", | ||
"### which is the Dupire equation when the underlying stock has risk-neutral drift μ.\n", | ||
"$\\frac{\\partial C}{\\partial T}=\\frac{\\sigma^2 K^2}{2} \\frac{\\partial^2 C}{\\partial K^2}+\\left(r_t-D_t\\right)\\left(C-K \\frac{\\partial C}{\\partial K}\\right)$" | ||
], | ||
"metadata": { | ||
"id": "ORnbFO8A8XAJ" | ||
} | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"source": [ | ||
"dCdT = sigma**2 * K / 2 * varphi + (r - D)* (Call - K * smp.diff(Call,K))\n", | ||
"dCdT" | ||
], | ||
"metadata": { | ||
"colab": { | ||
"base_uri": "https://localhost:8080/", | ||
"height": 137 | ||
}, | ||
"id": "Xf5AbHNS8XT6", | ||
"outputId": "fec23eba-66b6-4c57-8588-38d7d839f9ff" | ||
}, | ||
"execution_count": 5, | ||
"outputs": [ | ||
{ | ||
"output_type": "execute_result", | ||
"data": { | ||
"text/plain": [ | ||
"S_T*(-D + r)*Piecewise((erf(sqrt(2)*(mu_google - tau)/(2*sigma_google))/2 + 1/2, (Abs(arg(sigma_google)) < pi/4) | ((Abs(arg(sigma_google)) <= pi/4) & (Abs(2*arg(mu_google) - 4*arg(sigma_google) + 2*arg((mu_google - tau)/mu_google) + 2*pi) < pi))), (sqrt(2)*Integral(exp(-(_z - mu_google + tau)**2/(2*sigma_google**2)), (_z, 0, oo))/(2*sqrt(pi)*sigma_google), True))" | ||
], | ||
"text/latex": "$\\displaystyle S_{T} \\left(- D + r\\right) \\left(\\begin{cases} \\frac{\\operatorname{erf}{\\left(\\frac{\\sqrt{2} \\left(\\mu_{google} - \\tau\\right)}{2 \\sigma_{google}} \\right)}}{2} + \\frac{1}{2} & \\text{for}\\: \\left(\\left|{\\arg{\\left(\\sigma_{google} \\right)}}\\right| \\leq \\frac{\\pi}{4} \\wedge \\left|{2 \\arg{\\left(\\mu_{google} \\right)} - 4 \\arg{\\left(\\sigma_{google} \\right)} + 2 \\arg{\\left(\\frac{\\mu_{google} - \\tau}{\\mu_{google}} \\right)} + 2 \\pi}\\right| < \\pi\\right) \\vee \\left|{\\arg{\\left(\\sigma_{google} \\right)}}\\right| < \\frac{\\pi}{4} \\\\\\frac{\\sqrt{2} \\int\\limits_{0}^{\\infty} e^{- \\frac{\\left(z - \\mu_{google} + \\tau\\right)^{2}}{2 \\sigma_{google}^{2}}}\\, dz}{2 \\sqrt{\\pi} \\sigma_{google}} & \\text{otherwise} \\end{cases}\\right)$" | ||
}, | ||
"metadata": {}, | ||
"execution_count": 5 | ||
} | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"source": [ | ||
"## Local volatility\n", | ||
"$\\sigma^2\\left(K, T, S_0\\right)=\\frac{\\frac{\\partial C}{\\partial T}}{\\frac{1}{2} K^2 \\frac{\\partial^2 C}{\\partial K^2}}$\n", | ||
"\n", | ||
"The right-hand side of this equation can be computed from known European option prices. So, given a complete set of European option prices for all strikes and expirations, local volatilities are given uniquely by equation above.\n", | ||
"We can view this equation as a definition of the local volatility function regardless of what kind of process (stochastic volatility for example) actually governs the evolution of volatility." | ||
], | ||
"metadata": { | ||
"id": "tLMrOdpH8ci9" | ||
} | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"source": [ | ||
"local_volatility = smp.sqrt( dCdT / (1/2*K**2 * varphi))\n", | ||
"local_volatility" | ||
], | ||
"metadata": { | ||
"colab": { | ||
"base_uri": "https://localhost:8080/", | ||
"height": 141 | ||
}, | ||
"id": "6-ut-zDY8aMU", | ||
"outputId": "7696d056-f450-4e39-e466-d3f270052379" | ||
}, | ||
"execution_count": 6, | ||
"outputs": [ | ||
{ | ||
"output_type": "execute_result", | ||
"data": { | ||
"text/plain": [ | ||
"sqrt(zoo*S_T*(-D + r)*Piecewise((erf(sqrt(2)*(mu_google - tau)/(2*sigma_google))/2 + 1/2, (Abs(arg(sigma_google)) < pi/4) | ((Abs(arg(sigma_google)) <= pi/4) & (Abs(2*arg(mu_google) - 4*arg(sigma_google) + 2*arg((mu_google - tau)/mu_google) + 2*pi) < pi))), (sqrt(2)*Integral(exp(-(_z - mu_google + tau)**2/(2*sigma_google**2)), (_z, 0, oo))/(2*sqrt(pi)*sigma_google), True)))" | ||
], | ||
"text/latex": "$\\displaystyle \\sqrt{\\tilde{\\infty} S_{T} \\left(- D + r\\right) \\left(\\begin{cases} \\frac{\\operatorname{erf}{\\left(\\frac{\\sqrt{2} \\left(\\mu_{google} - \\tau\\right)}{2 \\sigma_{google}} \\right)}}{2} + \\frac{1}{2} & \\text{for}\\: \\left(\\left|{\\arg{\\left(\\sigma_{google} \\right)}}\\right| \\leq \\frac{\\pi}{4} \\wedge \\left|{2 \\arg{\\left(\\mu_{google} \\right)} - 4 \\arg{\\left(\\sigma_{google} \\right)} + 2 \\arg{\\left(\\frac{\\mu_{google} - \\tau}{\\mu_{google}} \\right)} + 2 \\pi}\\right| < \\pi\\right) \\vee \\left|{\\arg{\\left(\\sigma_{google} \\right)}}\\right| < \\frac{\\pi}{4} \\\\\\frac{\\sqrt{2} \\int\\limits_{0}^{\\infty} e^{- \\frac{\\left(z - \\mu_{google} + \\tau\\right)^{2}}{2 \\sigma_{google}^{2}}}\\, dz}{2 \\sqrt{\\pi} \\sigma_{google}} & \\text{otherwise} \\end{cases}\\right)}$" | ||
}, | ||
"metadata": {}, | ||
"execution_count": 6 | ||
} | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"source": [ | ||
"# Using real values from QuantBook to calculate Local Volatility" | ||
], | ||
"metadata": { | ||
"id": "JSHTTZeU9ONk" | ||
} | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"source": [ | ||
"# QuantBook Analysis Tool \n", | ||
"# For more information see [https://www.quantconnect.com/docs/v2/our-platform/research/getting-started]\n", | ||
"import datetime\n", | ||
"qb = QuantBook()\n", | ||
"option = qb.AddOption(\"GOOG\") \n", | ||
"option.SetFilter(-2, 2, 0, 90)\n", | ||
"history = qb.GetOptionHistory(option.Symbol, datetime(2023, 5, 7), datetime(2023, 5, 26))\n", | ||
"\n", | ||
"all_history = history.GetAllData()\n", | ||
"expiries = history.GetExpiryDates() \n", | ||
"strikes = history.GetStrikes()\n", | ||
"\n", | ||
"goog = qb.AddEquity(\"GOOG\")\n", | ||
"goog_df = qb.History(qb.Securities.Keys, datetime(2023, 5, 7), datetime(2023, 5, 26), Resolution.Daily)\n", | ||
"goog_df = goog_df['close'].unstack(level=0)\n", | ||
"S_T = goog_df.values[-1]\n", | ||
"all_history.head()" | ||
], | ||
"metadata": { | ||
"id": "pYG7bmQv88tI" | ||
}, | ||
"execution_count": null, | ||
"outputs": [] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"source": [ | ||
"#BSM model of Call option\n", | ||
"import math\n", | ||
"import sympy.stats\n", | ||
"\n", | ||
"####################### parameters #########################\n", | ||
"tau = 3/220 #expiry the 5/19\n", | ||
"r = 0.0341 \n", | ||
"D = 0.0\n", | ||
"K = strikes[-1]\n", | ||
"sigma = goog_df.std()\n", | ||
"S_T = float(goog_df.values[-1])\n", | ||
"mu_goog = goog_df.mean()\n", | ||
"#Calculate numerically the local volatility\n", | ||
"local_volatility.subs([(S_T,S_T),(K,int(K)),(tau,tau),(D,D),(sigma_google,sigma),(mu_google,mu_google)]).evalf()\n" | ||
], | ||
"metadata": { | ||
"id": "lcZxAl8K8msU" | ||
}, | ||
"execution_count": null, | ||
"outputs": [] | ||
} | ||
] | ||
} |