Pricing and Calibration Case Study
This case study walks through a realistic derivatives workflow:
Price vanilla options and validate correctness
Compute Greeks with automatic differentiation
Calibrate SABR to a volatility smile
Price exotics with Monte Carlo and sanity-check results
Connect results to the benchmark methodology
The goal is not just to show features, but to demonstrate trustworthy outputs and reproducible performance.
Reproduce This Case Study
julia --project=. demos/options_pricing_demo.jlScenario Setup
We use a simple equity-style setup to keep the math interpretable:
Spot S = 100
Strike K = 100
Time to expiry T = 1 year
Risk-free rate r = 5%
Volatility sigma = 20%
1) Analytical Pricing and Validation
We price a European call/put using Black-Scholes and verify put-call parity.
using QuantNova
S, K, T, r, sigma = 100.0, 100.0, 1.0, 0.05, 0.20
call_price = black_scholes(S, K, T, r, sigma, :call)
put_price = black_scholes(S, K, T, r, sigma, :put)
parity = call_price - put_price - S + K * exp(-r * T)
println("Call Price: $call_price")
println("Put Price: $put_price")
println("Put-Call Parity: $parity")Example output:
Call Price: 10.4506
Put Price: 5.5735
Put-Call Parity: 0.000000Validation check: parity approximately 0 confirms consistent pricing.
2) Greeks via Automatic Differentiation
We compute all major Greeks in a single AD pass.
state = MarketState(
prices = Dict("SPX" => S),
rates = Dict("USD" => r),
volatilities = Dict("SPX" => sigma),
timestamp = 0.0
)
option = EuropeanOption("SPX", K, T, :call)
# AD-based Greeks
greeks = compute_greeks(option, state)
println(greeks)Example output:
Greeks:
delta = 0.637
gamma = 0.019
vega = 0.375
theta = -6.414
rho = 0.532Validation check: values align with analytical Black-Scholes Greeks for the same inputs.
3) SABR Calibration to a Smile
We calibrate SABR to a synthetic volatility smile with realistic downside skew.
strikes = [85.0, 90.0, 95.0, 100.0, 105.0, 110.0, 115.0]
vols = [0.28, 0.25, 0.22, 0.20, 0.19, 0.185, 0.18]
quotes = [OptionQuote(k, T, 0.0, :call, v) for (k, v) in zip(strikes, vols)]
smile_data = SmileData(T, 100.0, r, quotes)
result = calibrate_sabr(smile_data; beta=0.5)
println(result)Example output:
Calibrated Parameters:
alpha = 1.8578
beta = 0.5 (fixed)
rho = -0.4514
nu = 1.3211
RMSE = 0.28%Validation check: low RMSE indicates the model fits the smile well.
4) Monte Carlo Pricing and Sanity Checks
We price exotics and validate MC results against analytic prices where possible.
dynamics = GBMDynamics(r, sigma)
# European call
mc = mc_price(S, T, EuropeanCall(K), dynamics; npaths=50000, nsteps=50)
bs = black_scholes(S, K, T, r, sigma, :call)
println("MC: $mc.price +/- $mc.stderr")
println("BS: $bs")Example output:
MC: 10.46 +/- 0.04
BS: 10.45Validation check: MC estimate should be within a few standard errors of the analytic price.
We also price an American option with Longstaff-Schwartz and verify it exceeds the European price.
5) Performance Benchmarks
Performance claims are based on reproducible benchmark scripts. The full methodology, parameters, and run commands are documented here:
Takeaways
Analytical pricing matches known identities (put-call parity).
AD-based Greeks agree with analytical values.
SABR calibration achieves low error on a realistic smile.
Monte Carlo results are consistent with analytic prices within error bounds.
Performance methodology is explicit and reproducible.