Skip to content

Commit 367e57f

Browse files
committed
Added calibration adr and adr to website docs
1 parent 88bc657 commit 367e57f

8 files changed

+436
-82
lines changed

docs/adr/adr-005-greeks-calculation-design.yaml

Lines changed: 0 additions & 54 deletions
This file was deleted.

docs/adr/adr-005-greeks.yaml

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
adr_id: 005
2+
title: "Greeks Calculation Design"
3+
status: Accepted
4+
date: 2025-04-01
5+
replaces:
6+
- adr-005-greeks-calculation-design
7+
context: |
8+
In ADR-004, we introduced the SciML-style `solve(problem, method)` pattern, replacing the previous `Pricer{P, M, S}` design.
9+
10+
This alignment with SciML requires a compatible approach for calculating Greeks (sensitivities).
11+
Greeks measure how a derivative's price changes with respect to input variables such as:
12+
- Spot price (delta, gamma)
13+
- Volatility (vega)
14+
- Interest rates (rho)
15+
- Time (theta)
16+
17+
We need a design for Greeks calculation that:
18+
1. Maintains consistency with the SciML pattern
19+
2. Leverages Julia's AD ecosystem
20+
3. Provides flexibility in calculation methods
21+
4. Enables targeted sensitivity analysis
22+
23+
decision: |
24+
- Introduce a dedicated `GreekProblem` type for computing Greeks:
25+
```julia
26+
struct GreekProblem{P, L}
27+
pricing_problem::P # The underlying pricing problem
28+
lens::L # Accessor lens indicating what to differentiate with respect to
29+
end
30+
```
31+
32+
- Use Accessors.jl for lens-based targeting of specific parameters:
33+
```julia
34+
# Create a pricing problem
35+
problem = PricingProblem(payoff, market_inputs)
36+
37+
# Calculate delta (sensitivity to spot price) using the @optic macro
38+
spot_lens = @optic _.market_inputs.spot
39+
delta_problem = GreekProblem(problem, spot_lens)
40+
```
41+
42+
- Support multiple calculation methods that subtype `AbstractGreekMethod`:
43+
```julia
44+
# Base types
45+
abstract type AbstractGreekMethod end
46+
struct ForwardAD <: AbstractGreekMethod end
47+
struct FiniteDifference <: AbstractGreekMethod end
48+
struct Analytical <: AbstractGreekMethod end
49+
```
50+
51+
- Use the consistent `solve` interface with method dispatch:
52+
```julia
53+
# Calculate delta using forward-mode automatic differentiation
54+
delta_solution = solve(delta_problem, ForwardAD(), BlackScholesAnalytic())
55+
delta = delta_solution.greek
56+
57+
# Calculate gamma with finite differences
58+
gamma_config = FiniteDifferenceConfig(order=2, step=1e-4)
59+
gamma_solution = solve(delta_problem, FiniteDifference(gamma_config), BlackScholesAnalytic())
60+
gamma = gamma_solution.greek
61+
```
62+
63+
- Return a `GreekSolution` struct with the computed sensitivity and metadata:
64+
```julia
65+
struct GreekSolution{T, M}
66+
greek::T # The computed sensitivity value
67+
metadata::M # Additional information (e.g., error estimates)
68+
end
69+
```
70+
71+
- Leverage Julia's AD ecosystem (ForwardDiff.jl, Zygote.jl) for implementation:
72+
```julia
73+
# Example internal implementation using ForwardDiff
74+
function solve(prob::GreekProblem, ::ForwardAD, method)
75+
f = x -> begin
76+
# Create a modified problem with the parameter changed
77+
new_problem = lens_modify(prob.pricing_problem, prob.lens, x)
78+
# Solve the modified problem
79+
solution = solve(new_problem, method)
80+
return solution.price
81+
end
82+
83+
# Get the current parameter value
84+
x0 = lens_get(prob.pricing_problem, prob.lens)
85+
86+
# Compute the derivative
87+
derivative = ForwardDiff.derivative(f, x0)
88+
89+
return GreekSolution(derivative, nothing)
90+
end
91+
```
92+
93+
consequences:
94+
positive:
95+
- "Maintains SciML-compatible pattern with `solve(problem, method)` interface"
96+
- "Lens-based approach provides flexible targeting of any parameter in the pricing problem"
97+
- "Multiple Greek calculation methods can be swapped independently"
98+
- "Leverages Julia's AD ecosystem for efficient sensitivity calculation"
99+
- "Solution objects can include error estimates and convergence information"
100+
negative:
101+
- "Requires understanding of lens-based access patterns"
102+
- "Higher-order Greeks (e.g., gamma) require chained operations"
103+
104+
alternatives:
105+
- name: "Standalone Greek functions"
106+
pros: "Simpler API with intuitive function names (delta, vega, etc.)"
107+
cons: "Less flexible, harder to extend to arbitrary parameters"
108+
109+
- name: "Include Greeks in pricing solutions"
110+
pros: "Compute price and Greeks in a single operation"
111+
cons: "Inefficient for cases where only the price is needed"
112+
113+
- name: "Lambda-based parameter selection"
114+
pros: "Could use functions like (inputs -> inputs.spot) instead of lenses"
115+
cons: "Less composable and harder to use with AD frameworks"
116+
117+
examples: |
118+
```julia
119+
# Setup
120+
using Hedgehog
121+
using Accessors # For @optic macro
122+
123+
# Define the option and market data
124+
option = VanillaOption(100.0, Date(2023, 12, 31), European(), Call())
125+
market = BlackScholesInputs(Date(2023, 1, 1), 0.05, 100.0, 0.2)
126+
127+
# Create the pricing problem
128+
problem = PricingProblem(option, market)
129+
130+
# Calculate price
131+
price_solution = solve(problem, BlackScholesAnalytic())
132+
price = price_solution.price
133+
134+
# Calculate delta (first-order sensitivity to spot)
135+
spot_lens = @optic _.market_inputs.spot
136+
delta_problem = GreekProblem(problem, spot_lens)
137+
delta = solve(delta_problem, ForwardAD(), BlackScholesAnalytic()).greek
138+
139+
# Calculate vega (sensitivity to volatility)
140+
vol_lens = @optic _.market_inputs.volatility
141+
vega = solve(GreekProblem(problem, vol_lens), ForwardAD(), BlackScholesAnalytic()).greek
142+
143+
# Calculate gamma (second-order sensitivity to spot)
144+
gamma_problem = GreekProblem(delta_problem, spot_lens) # Note: problem composition
145+
gamma = solve(gamma_problem, ForwardAD(), BlackScholesAnalytic()).greek
146+
```
147+
148+
references:
149+
- adr-004-sciml-integration.yaml

docs/adr/adr-006-calibration.yaml

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
adr_id: 006
2+
title: "Calibration System Design"
3+
status: Accepted
4+
date: 2025-04-21
5+
context: |
6+
Model calibration is a critical component of any pricing library. A properly calibrated model
7+
ensures that theoretical prices match observable market prices. In Hedgehog, we need a
8+
calibration system that:
9+
10+
1. Is consistent with our SciML-based architecture (ADR-004)
11+
2. Supports multiple optimization methods and algorithms
12+
3. Can calibrate to various market observables (option prices, implied volatilities, etc.)
13+
4. Provides clear diagnostics and quality metrics
14+
5. Is extensible to new models and market data types
15+
16+
Different models require different calibration approaches:
17+
- Implied volatility inversion for individual options
18+
- Volatility surfaces: Fitting to a grid of market quotes
19+
- Heston model: Multi-dimensional optimization against a volatility surface
20+
- Term structure models: Bootstrapping or global fitting to yield curve instruments
21+
22+
decision: |
23+
- Define a `CalibrationProblem` type that follows our SciML pattern:
24+
```julia
25+
struct CalibrationProblem{M, D, O, P}
26+
model::M # The model to calibrate
27+
data::D # Market observables
28+
objective::O # Function to measure calibration quality
29+
params::P # Parameters to calibrate with bounds
30+
end
31+
```
32+
33+
- Create a uniform interface using `solve`:
34+
```julia
35+
# Implement a set of calibration methods
36+
abstract type AbstractCalibrationMethod end
37+
38+
# Example calibration methods
39+
struct OptimizationMethod <: AbstractCalibrationMethod
40+
algorithm # The optimization algorithm to use
41+
options::NamedTuple # Configuration options
42+
end
43+
44+
# Solve the calibration problem with a chosen method
45+
calibrated_model = solve(problem, method)
46+
```
47+
48+
- Return a `CalibrationSolution` with results and diagnostics:
49+
```julia
50+
struct CalibrationSolution{M, R}
51+
calibrated_model::M # The calibrated model with optimized parameters
52+
residuals::Vector{R} # Residuals between model and market data
53+
error::R # Error metric (RMSE or other)
54+
metadata::Dict # Additional calibration diagnostics
55+
end
56+
```
57+
58+
- Leverage existing Julia optimization packages:
59+
```julia
60+
# Integration with Optim.jl
61+
function solve(prob::CalibrationProblem, method::OptimizationMethod)
62+
# Convert to optimization problem and solve
63+
end
64+
```
65+
66+
- Use lens-based access (via Accessors.jl) to specify which parameters to calibrate:
67+
```julia
68+
# Specify what params to calibrate with lenses
69+
calibration_params = [
70+
(@optic _.kappa, (0.1, 10.0)), # Parameter name and bounds
71+
(@optic _.theta, (0.01, 0.5)),
72+
(@optic _.sigma, (0.05, 1.0)),
73+
(@optic _.rho, (-0.9, 0.9))
74+
]
75+
76+
problem = CalibrationProblem(
77+
model,
78+
market_quotes,
79+
SquaredError(),
80+
calibration_params
81+
)
82+
```
83+
84+
consequences:
85+
positive:
86+
- "Maintains consistency with SciML design pattern"
87+
- "Provides flexibility to use different optimization algorithms"
88+
- "Enables clean separation between problem definition and solution method"
89+
- "Allows precise control over which parameters get calibrated"
90+
- "Easily extensible to new models and objective functions"
91+
negative:
92+
- "May have performance overhead compared to model-specific calibration routines"
93+
- "Requires understanding of lens-based access for parameter specification"
94+
- "More complex than direct function calls for simple cases"
95+
96+
alternatives:
97+
- name: "Model-specific calibration methods"
98+
pros: "Could be more efficient for specific models with known calibration techniques"
99+
cons: "Less extensible and would lead to code duplication across models"
100+
101+
- name: "Direct exposure of optimization interfaces"
102+
pros: "More flexibility for advanced users familiar with optimization packages"
103+
cons: "Inconsistent API and more complexity for typical use cases"
104+
105+
- name: "Global vs. local parameter specification"
106+
pros: "Could specify all parameters at model level rather than with lenses"
107+
cons: "Less flexibility for calibrating subset of parameters or nested structures"
108+
109+
examples: |
110+
```julia
111+
using Hedgehog
112+
using Accessors
113+
using Dates
114+
using Optim
115+
116+
# Market data: option quotes at different strikes and maturities
117+
strikes = [90.0, 95.0, 100.0, 105.0, 110.0]
118+
maturities = [Date(2023, 1, 1) + Month(i) for i in [1, 2, 3, 6, 12]]
119+
120+
# Create synthetic market data (in real usage, this would be actual market quotes)
121+
market_quotes = [
122+
OptionQuote(strike, maturity, Call(), price=price_value, implied_vol=vol_value)
123+
for strike in strikes, (i, maturity) in enumerate(maturities)
124+
]
125+
126+
# Create initial Heston model with guess parameters
127+
initial_model = HestonModel(
128+
kappa = 1.5, # Mean reversion speed
129+
theta = 0.04, # Long-term variance
130+
sigma = 0.3, # Volatility of volatility
131+
rho = -0.6, # Correlation
132+
v0 = 0.04 # Initial variance
133+
)
134+
135+
# Define which parameters to calibrate and their bounds
136+
calibration_params = [
137+
(@optic _.kappa, (0.1, 10.0)),
138+
(@optic _.theta, (0.01, 0.5)),
139+
(@optic _.sigma, (0.05, 1.0)),
140+
(@optic _.rho, (-0.9, 0.9))
141+
]
142+
143+
# Create the calibration problem
144+
problem = CalibrationProblem(
145+
initial_model,
146+
market_quotes,
147+
SquaredError(), # Objective function
148+
calibration_params
149+
)
150+
151+
# Solve using an optimization algorithm from Optim.jl
152+
solution = solve(problem, OptimizationMethod(
153+
LBFGS(),
154+
(iterations = 1000, g_tol = 1e-6)
155+
))
156+
157+
# Access the calibrated model and diagnostics
158+
calibrated_model = solution.calibrated_model
159+
println("Calibrated Heston parameters:")
160+
println("κ = $(calibrated_model.kappa)")
161+
println("θ = $(calibrated_model.theta)")
162+
println("σ = $(calibrated_model.sigma)")
163+
println("ρ = $(calibrated_model.rho)")
164+
println("Error = $(solution.error)")
165+
```
166+
167+
references:
168+
- adr-004-sciml-integration.yaml
169+
- adr-005-greeks-calculation-design.yaml

docs/adr/index.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ adr_index:
33
- "adr-002-market-inputs.yaml"
44
- "adr-003-pricing-methods.yaml"
55
- "adr-004-sciml-integration.yaml"
6-
- "adr-005-greeks-calculation-design.yaml"
6+
- "adr-005-greeks.yaml"
7+
- "adr-006-calibration.yaml"

0 commit comments

Comments
 (0)