I'm using Julia's Zygote.jl
package for its auto-diff abilities to calculate option greeks (i.e. the derivatives of the option price relative to the parameters). See below which calculates greeks for a call option using Black Scholes. It takes 40 seconds on my laptop to run. Am I doing something wrong that would cause it to take this much time?
My guess is that the hard part comes when Zygote
has to differentiate through Distributions
, but I'm not sure.
using Distributions
using Zygote
function bs_call(theta)
s = theta[1]
k = theta[2]
r = theta[3]
t = theta[4]
sigma = theta[5]
vol = sigma * sqrt(t)
d1 = (log(s / k) + (r + 0.5 * sigma ^ 2) * t) / vol
d2 = d1 - vol
n = Normal()
price = cdf(n, d1) * s - cdf(n, d2) * k * exp(-1.0 * r * t)
price
end
function main()
theta = [100, 110, .20, 1.0, .50]
println(bs_call(theta))
println(bs_call'(theta))
end
main()
Edit: using SpecialFunctions
(to build a cdf
function from erf
) instead of Distributions
gets me down to 25 seconds. See below:
using SpecialFunctions
using Zygote
function cdf(x)
0.5 * (1 + erf(x / sqrt(2)))
end
function bs_call(theta)
s = theta[1]
k = theta[2]
r = theta[3]
t = theta[4]
sigma = theta[5]
vol = sigma * sqrt(t)
d1 = (log(s / k) + (r + 0.5 * sigma ^ 2) * t) / vol
d2 = d1 - vol
price = cdf(d1) * s - cdf(d2) * k * exp(-1.0 * r * t)
price
end
function main()
theta = [100.0, 110.0, .20, 1.0, .50]
println(bs_call(theta))
println(bs_call'(theta))
end
main()
Given your main
function, you might be executing this in a script. In Julia,you are far better off starting a session (in the REPL, VSCode, Jupyter notebook, or other environment) and running multiple workloads from the same session. As Antonello suggests in a comment, your first call will be dominated by compile time, but the later calls (with the same argument types) simply use the compiled code and can be a completely different experience from the first one.
Some workflow tips can be found in https://docs.julialang.org/en/v1/manual/workflow-tips/.