Finding Implied Volatility by Minimizing Option Pricing Error
Summary
The document describes a common setup for recovering implied volatility: calculate an option price from a volatility parameter and fixed market inputs, then use an optimizer to match that model price to a quoted price. The example uses a normal option pricing function and SciPy’s minimize routine. Its objective mistakenly returns the signed difference between model and market prices. Since the optimizer minimizes that value, it can keep lowering the objective by driving volatility to an extreme negative value instead of finding a price match.
The accepted answer corrects the objective by minimizing the absolute pricing error, so zero error is the target. The example illustrates why an optimizer needs a loss function that represents the intended calibration goal. It does not compare optimization methods or address practical safeguards such as volatility bounds, starting values, or alternative root-finding routines. The code discussion also contains unrelated implementation details, so the core lesson is about formulating the objective correctly.
Key ideas
- Implied volatility can be estimated by comparing a model option price with its market quote.
- Minimizing a signed price difference can drive the optimizer toward an extreme parameter value.
- The example fixes this by minimizing the magnitude of the pricing error.
- The objective function must encode the intended goal of matching model and market prices.
- The document does not assess parameter bounds or compare alternative numerical methods.
Tags
Full text
# python scipy optimize minimize arguments for Implied Volatility
# python scipy optimize minimize arguments for Implied Volatility
I am having some trouble getting the 'correct' solution to a function where I am trying to utilize `scipy.optimize.minimize`.
In the code below, I create a function `bs_nor()`, and set up an objective function, `objfunc_vol`. I declare the initial guess `x0 = 0.01`; and the other constants within the argument (`args = ()`).
I use `scipy minimize`, where I want to recover the implied-vol given by `sigma`, and the other parameters, `args`, are constants.
```
import xlrd
import numpy as np
import math
import pandas as pd
import matplotlib.pyplot as plt
import scipy as sp
import scipy.stats as stats
from scipy.optimize import minimize
from IPython.display import display
class option:
def bs_nor(F = 1.0, K = 1.05, time = 1.0, sigma = 0.015, bs_type = 'call'):
d1 = (F-K)/(sigma * np.sqrt(time))
if (bs_type == 'call') or (bs_type == 'c') or (bs_type == 'Call'):
opt = (F-K) * stats.norm.cdf(d1, 0.0, 1.0) + sigma * np.sqrt(time/(2.0*math.pi)) * np.exp(-0.5 * d1**2)
else:
opt = (K-F) * stas.norm.cdf(-d1, 0.0, 1.0) + sigma * np.sqrt(time/(2.0*math.pi)) * np.exp(-0.5 * d1**2)
return np.array([opt, d1, stats.norm.cdf(d1, 0.0, 1.0)])
def objfunc_vol(param = np.array([0.15]), F = 1.0, K = 1.0, time = 1.0, mode = 'normal', quote = 0.5):
sigma = param[0]
if (mode == 'normal') or (mode == 'norm') or (mode == 'n') :
prx = option.bs_nor(F, K, time, sigma, bs_type = 'call')[0]
else:
prx = option.bs_log(F, K, time, sigma, bs_type = 'call')[0]
diff = prx - quote
return diff
options={'maxiter': 200}
x0 = 0.0255
res_vol = minimize(option.objfunc_vol, x0, args = (0.03057, -0.02, 1.0 ,'normal', 0.05079), method ='SLSQP', options = options)
print(res_vol)
option.bs_nor(F = 0.03057, K = -0.02, time = 1.0, sigma = 0.025507, bs_type = 'call')[0]
```
I know the solution should be 0.0255; given the constants I enter. However, scipy optimize does not seem to be giving me the correct answer. It is giving me -1.15528343e+08 instead of 0.0255.
What am I specifying it wrong?
```
fun: -46089140.7916228
jac: array([ 0.])
message: 'Optimization terminated successfully.'
nfev: 42
nit: 14
njev: 14
status: 0
success: True
x: array([ -1.15528343e+08])
0.050796884487313197
```
## Answer by Kiann (score 3)
https://quant.stackexchange.com/a/44550
Error was in the diff line, where it should be the modulus of the difference. We want the difference to be zero, instead of minimum (which is -infinity). Thanks to 'LocalVolatility'.
```
def objfunc_vol(param = np.array([0.15]), F = 1.0, K = 1.0, time = 1.0, mode = 'normal', quote = 0.5):
sigma = param[0]
if (mode == 'normal') or (mode == 'norm') or (mode == 'n') :
prx = option.bs_nor(F, K, time, sigma, bs_type = 'call')[0]
else:
prx = option.bs_log(F, K, time, sigma, bs_type = 'call')[0]
diff = abs(prx - quote)
return diff
```Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.