Skip to content

Commit

Permalink
Fixed: Issue QuantConnect#47
Browse files Browse the repository at this point in the history
## 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
leo-ai-for-trading authored May 27, 2023
1 parent 71c0ff8 commit 59417e5
Showing 1 changed file with 266 additions and 0 deletions.
266 changes: 266 additions & 0 deletions Local_Stochastic_Volatility_Model.ipynb
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": []
}
]
}

0 comments on commit 59417e5

Please sign in to comment.