Directory
- Quantlib financial computing-a tool for solving mathematical tools
- Overview
- Non-Newton algorithm (derivative not required)
- Newton algorithm (derivative required)
Unless otherwise specified, all the programs in this article are python3 code.
Quantlib financial computing-a tool for solving mathematical tools
Load Module
import QuantLib as qlimport scipyfrom scipy.stats import normprint(ql.__version__)
1.12
Overview
Quantlib provides multiple types of one-dimensional solver for solving the root of a Single-Parameter Function,
\ [F (x) = 0 \]
Where \ (F: r \ to r \) is a function in the real number field.
Quantlib provides the following solver types:
Brent
Bisection
Secant
Ridder
Newton
(A member function is required.derivative
, Calculates the derivative)
FalsePosition
The constructor uses default constructor and does not accept parameters. For example,Brent
The constructor instance construction statement ismySolv = Brent()
.
Call Method
Member functions of the Solversolve
There are two call methods:
solve(f, accuracy, guess, step)solve(f, accuracy, guess, xMin, xMax)
f
: Single-Parameter Function or function object. The return value is a floating point number.
accuracy
: Floating Point Number, indicating the resolution accuracy \ (\ Epsilon \), used to stop calculation. Suppose \ (x_ I \) is the exact root solution,
- When \ (| f (x) | <\ Epsilon \);
- Or \ (| X-X_ I | <\ Epsilon.
guess
: Floating point number, the initial guess value of the root.
step
: Floating point number. In the first call method, the range of the root is not limited. The algorithm needs to search for a range by itself.step
Specifies the search algorithm step.
xMin
,xMax
: Floating Point Number, left and right range
The most classic application of the root solver in quantum finance is the implied volatility. Given option price \ (P \) and other parameters \ (s_0 \), \ (k \), \ (r_d \), \ (r_f \), \ (\ Tau \), we need to calculate the fluctuation \ (\ Sigma \) to meet
\ [F (\ sigma) = \ mathrm {blackscholesprice} (s_0, K, r_d, r_f, \ Sigma, \ Tau, \ PHI)-P = 0 \]
Where \ (\ Phi = 1 \) in the black-schles function represents the call option; \ (\ Phi =? 1 \) represents the put option.
Non-Newton algorithm (derivative not required)
The following example shows how to add a multi-parameter function as a single-Parameter Function and use the quantlib solver to calculate the implied fluctuation.
Example 1
# Blackscholesprice (spot, strike, RD, RF, vol, Tau, Phi): domdf = scipy. exp (-rd * Tau) fordf = scipy. exp (-RF * Tau) FWD = spot * fordf/domdf stddev = vol * scipy. SQRT (Tau) dp = (scipy. log (FWD/strike) + 0.5 * stddev)/stddev dm = (scipy. log (FWD/strike)-0.5 * stddev)/stddev res = Phi * domdf * (FWD * norm. CDF (PHI * DP)-strike * norm. CDF (PHI * DM) return res # packaging function def impliedvolproblem (spot, strike, RD, RF, Tau, Phi, price): def inner_func (V ): return blackscholesprice (spot, strike, RD, RF, V, Tau, Phi)-Price Return inner_funcdef testsolver1 (): # setup of market parameters Spot = 100.0 strike = 110.0 RD = 0.002 Rf = 0.01 Tau = 0.5 Phi = 1 Vol = 0.1423 # calculate corresponding black schles Price = blackscholesprice (spot, strike, RD, RF, vol, Tau, Phi) # setup a solver mysolv1 = QL. bisection () mysolv2 = QL. brent () mysolv3 = QL. ridder () Accuracy = 0.00001 guess = 0.25 min = 0.0 max = 1.0 myvolfunc = impliedvolproblem (spot, strike, RD, RF, Tau, Phi, price) RES1 = mysolv1.solve (myvolfunc, accuracy, guess, Min, max) RES2 = mysolv2.solve (myvolfunc, accuracy, guess, Min, max) RES3 = mysolv3.solve (myvolfunc, accuracy, guess, Min, max) print ('{0: <35} {1 }'. format ('input Volatility: ', vol) print (' {0: <35} {1 }'. format ('implied volatility bisection: ', RES1) print (' {0: <35} {1 }'. format ('implied volatility BRENT: ', RES2) print (' {0: <35} {1 }'. format ('implied volatility Ridder: ', RES3) testsolver1 ()
# Input Volatility: 0.1423# Implied Volatility Bisection: 0.14229583740234375# Implied Volatility Brent: 0.14230199334812577# Implied Volatility Ridder: 0.1422999996313447
Newton algorithm (derivative required)
The Newton algorithm requires the root solver to provide \ (f (\ sigma) \) derivative \ (\ frac {\ partial f} {\ partial \ Sigma} \) (that is, Vega ). The following example shows how to add the derivative to solve the implicit fluctuation. For this reason, we need a class. On the one hand, we need to provide a function object, and on the other hand, we need to provide a member function.derivative
.
Example 2
class BlackScholesClass: def __init__(self, spot, strike, rd, rf, tau, phi, price): self.spot_ = spot self.strike_ = strike self.rd_ = rd self.rf_ = rf self.phi_ = phi self.tau_ = tau self.price_ = price self.sqrtTau_ = scipy.sqrt(tau) self.d_ = norm self.domDf_ = scipy.exp(-self.rd_ * self.tau_) self.forDf_ = scipy.exp(-self.rf_ * self.tau_) self.fwd_ = self.spot_ * self.forDf_ / self.domDf_ self.logFwd_ = scipy.log(self.fwd_ / self.strike_) def blackScholesPrice(self, spot, strike, rd, rf, vol, tau, phi): domDf = scipy.exp(-rd * tau) forDf = scipy.exp(-rf * tau) fwd = spot * forDf / domDf stdDev = vol * scipy.sqrt(tau) dp = (scipy.log(fwd / strike) + 0.5 * stdDev * stdDev) / stdDev dm = (scipy.log(fwd / strike) - 0.5 * stdDev * stdDev) / stdDev res = phi * domDf * (fwd * norm.cdf(phi * dp) - strike * norm.cdf(phi * dm)) return res def impliedVolProblem(self, spot, strike, rd, rf, vol, tau, phi, price): return self.blackScholesPrice( spot, strike, rd, rf, vol, tau, phi) - price def __call__(self, x): return self.impliedVolProblem( self.spot_, self.strike_, self.rd_, self.rf_, x, self.tau_, self.phi_, self.price_) def derivative(self, x): # vega stdDev = x * self.sqrtTau_ dp = (self.logFwd_ + 0.5 * stdDev * stdDev) / stdDev return self.spot_ * self.forDf_ * self.d_.pdf(dp) * self.sqrtTau_def testSolver2(): # setup of market parameters spot = 100.0 strike = 110.0 rd = 0.002 rf = 0.01 tau = 0.5 phi = 1 vol = 0.1423 # calculate corresponding Black Scholes price price = blackScholesPrice( spot, strike, rd, rf, vol, tau, phi) solvProblem = BlackScholesClass( spot, strike, rd, rf, tau, phi, price) mySolv = ql.Newton() accuracy = 0.00001 guess = 0.10 step = 0.001 res = mySolv.solve( solvProblem, accuracy, guess, step) print(‘{0:<20}{1}‘.format(‘Input Volatility:‘, vol)) print(‘{0:<20}{1}‘.format(‘Implied Volatility:‘, res))testSolver2()
# Input Volatility: 0.1423# Implied Volatility: 0.14230000000000048
The use of derivative significantly improves the accuracy.
Quantlib financial computing-a tool for solving mathematical tools