1
$\begingroup$

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
$\endgroup$
  • 1
    $\begingroup$ Check your objective function (note: not "object function"). You are minimizing prx - quote as opposed to abs(prx - quote) or (prx - quote)**2. Alternatively, don't minimize this function but run a root search - e.g. brentq from scipy.optimize. $\endgroup$ – LocalVolatility Mar 12 at 17:20
  • $\begingroup$ you're right @LocalVolatility; now I feel silly. It works after I just put abs(prx - quote). A follow-up, ... why do you recommend using brent for scipy.optimize instead of minimize? Are they not similar anyway? The reason I used this set-up is because I have an existing coded frame-work to calibrate SABR parameters. $\endgroup$ – Kiann Mar 12 at 22:50
  • $\begingroup$ Happens to all of us. You can either answer your own question to close it or alternatively delete it. $\endgroup$ – LocalVolatility Mar 12 at 22:52
  • 1
    $\begingroup$ Regarding root search vs. minimization - a root search can be expressed as a minimization but generally not vice versa. Root search e.g. knows that it bracketed the root when two values have different signs. Minimization generally can’t use the value of the true minimum as its usually unknown. Maybe a good follow-up question..? $\endgroup$ – LocalVolatility Mar 12 at 22:58
2
$\begingroup$

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
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.