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
0 commit comments