Skip to content

Commit

Permalink
Add volatility regularisation for swaption calibration
Browse files Browse the repository at this point in the history
  • Loading branch information
FrameConsult committed Nov 11, 2023
1 parent 11c9780 commit 496165e
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 21 deletions.
48 changes: 39 additions & 9 deletions src/models/rates/SwapRateCalibration.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
swap_rate_volatilities::AbstractMatrix,
yts::YieldTermstructure;
max_iter::Integer = 5,
volatility_regularisation::ModelValue = 0.0,
)
Calibrate a model with flat volatilities and mean reversion
Expand All @@ -23,6 +24,7 @@ function gaussian_hjm_model(
swap_rate_volatilities::AbstractMatrix,
yts::YieldTermstructure;
max_iter::Integer = 5,
volatility_regularisation::ModelValue = 0.0,
)
#
# check inputs first
Expand All @@ -36,6 +38,9 @@ function gaussian_hjm_model(
end
@assert size(swap_rate_volatilities) == (length(option_times), length(swap_maturities))
#
@assert volatility_regularisation 0.0
@assert volatility_regularisation 1.0
#
delta = flat_parameter(swap_maturities)
d = length(swap_maturities)
function model(x::AbstractVector)
Expand All @@ -59,20 +64,30 @@ function gaussian_hjm_model(
function obj_F(x::AbstractVector)
m = model(x)
σ = model_implied_volatilties(yts, m, option_times, swap_maturities, SX)
return σ - swap_rate_volatilities
obj = vec- swap_rate_volatilities)
if volatility_regularisation > 0.0
model_vol = m.sigma_T.sigma_f.values[:,1]
obj_vol = model_vol[begin+1:end] - model_vol[begin:end-1]
obj = vcat(
(1.0 - volatility_regularisation) * obj,
volatility_regularisation * obj_vol,
)
end
return obj
end
#
obj_model(p, x) = vec(obj_F(x))
obj_model(p, x) = obj_F(x)
y0 = obj_F(x0)
res = LsqFit.curve_fit(
obj_model,
zeros(length(swap_rate_volatilities)),
zeros(length(swap_rate_volatilities)),
zeros(length(y0)),
zeros(length(y0)),
x0,
maxIter = max_iter,
autodiff = :forwarddiff)
#
m1 = model(res.param)
fit = obj_F(res.param)
fit = model_implied_volatilties(yts, m1, option_times, swap_maturities, SX) - swap_rate_volatilities
return (model=m1, fit=fit)
end

Expand All @@ -88,6 +103,7 @@ end
swap_rate_volatilities::AbstractMatrix,
yts::YieldTermstructure;
max_iter::Integer = 5,
volatility_regularisation::ModelValue = 0.0,
)
Expand All @@ -106,6 +122,7 @@ function gaussian_hjm_model(
swap_rate_volatilities::AbstractMatrix,
yts::YieldTermstructure;
max_iter::Integer = 5,
volatility_regularisation::ModelValue = 0.0,
)
#
# check inputs first
Expand All @@ -121,6 +138,9 @@ function gaussian_hjm_model(
end
@assert size(swap_rate_volatilities) == (length(option_times), length(swap_maturities))
#
@assert volatility_regularisation 0.0
@assert volatility_regularisation 1.0
#
d = length(delta())
function model(x::AbstractVector, m::GaussianHjmModel, idx::Integer)
@assert length(x) == d
Expand All @@ -145,15 +165,25 @@ function gaussian_hjm_model(
function obj_F(x::AbstractVector, m::GaussianHjmModel, idx::Integer)
m = model(x, m, idx)
σ = model_implied_volatilties(yts, m, option_times[idx:idx], swap_maturities, SX)
return σ - swap_rate_volatilities[idx:idx,:]
obj = vec- swap_rate_volatilities[idx:idx,:])
if volatility_regularisation > 0.0
model_vol = m.sigma_T.sigma_f.values[:,idx]
obj_vol = model_vol[begin+1:end] - model_vol[begin:end-1]
obj = vcat(
(1.0 - volatility_regularisation) * obj,
volatility_regularisation * obj_vol,
)
end
return obj
end
#
for idx in eachindex(option_times)
obj_model(p, x) = vec(obj_F(x, m0, idx))
obj_model(p, x) = obj_F(x, m0, idx)
y0 = obj_F(x0, m0, idx)
res = LsqFit.curve_fit(
obj_model,
zeros(length(swap_maturities)),
zeros(length(swap_maturities)),
zeros(length(y0)),
zeros(length(y0)),
x0,
maxIter = max_iter,
autodiff = :forwarddiff)
Expand Down
78 changes: 68 additions & 10 deletions test/componenttests/calibration/swap_rate_calibration.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,17 @@ using Test
] * 1.0e-4
op_idx = [ 2, 4, 6 ]
sw_idx = [ 2, 3, 4 ]
res = DiffFusion.gaussian_hjm_model("EUR", ch, option_times[op_idx], swap_maturities[sw_idx], vols[op_idx, sw_idx], yts, max_iter = 5)
@test all(res.fit .> -13.5e-4)
res = DiffFusion.gaussian_hjm_model(
"EUR",
ch,
option_times[op_idx],
swap_maturities[sw_idx],
vols[op_idx, sw_idx],
yts,
max_iter = 5,
volatility_regularisation = 0.1,
)
@test all(res.fit .> -13.6e-4)
@test all(res.fit .< 8.2e-4)
println("")
println("Global model calibration results 1:")
Expand All @@ -52,8 +61,16 @@ using Test
] * 1.0e-4
op_idx = [ 2, 4, 6 ]
sw_idx = [ 2, 3, 4 ]
res = DiffFusion.gaussian_hjm_model("EUR", ch, option_times[op_idx], swap_maturities[sw_idx], vols[op_idx, sw_idx], yts, max_iter = 5)
@test all(res.fit .> -18.5e-4)
res = DiffFusion.gaussian_hjm_model(
"EUR",
ch,
option_times[op_idx],
swap_maturities[sw_idx],
vols[op_idx, sw_idx],
yts, max_iter = 5,
volatility_regularisation = 0.3,
)
@test all(res.fit .> -18.9e-4)
@test all(res.fit .< 13.5e-4)
println("")
println("Global model calibration results 2:")
Expand All @@ -73,7 +90,16 @@ using Test
] * 1.0e-4
op_idx = [ 2, 4, 6 ]
sw_idx = [ 2, 3, 4 ]
res = DiffFusion.gaussian_hjm_model("EUR", ch, option_times[op_idx], swap_maturities[sw_idx], vols[op_idx, sw_idx], yts, max_iter = 5)
res = DiffFusion.gaussian_hjm_model(
"EUR",
ch,
option_times[op_idx],
swap_maturities[sw_idx],
vols[op_idx, sw_idx],
yts,
max_iter = 5,
volatility_regularisation = 0.1,
)
@test all(res.fit .> -12.2e-4)
@test all(res.fit .< 12.4e-4)
println("")
Expand Down Expand Up @@ -112,8 +138,19 @@ using Test
] * 1.0e-4
op_idx = [ 1, 2, 4, 6 ]
sw_idx = [ 2, 3, 4 ]
res = DiffFusion.gaussian_hjm_model("EUR", delta, chi, ch, option_times[op_idx], swap_maturities[sw_idx], vols[op_idx, sw_idx], yts, max_iter = 5)
@test all(res.fit .> -6.4e-4)
res = DiffFusion.gaussian_hjm_model(
"EUR",
delta,
chi,
ch,
option_times[op_idx],
swap_maturities[sw_idx],
vols[op_idx, sw_idx],
yts,
max_iter = 5,
volatility_regularisation = 0.1,
)
@test all(res.fit .> -6.8e-4)
@test all(res.fit .< 7.4e-4)
println("")
println("Piece-wise model calibration results 1:")
Expand All @@ -137,8 +174,18 @@ using Test
] * 1.0e-4
op_idx = [ 1, 2, 4, 6 ]
sw_idx = [ 2, 3, 4 ]
res = DiffFusion.gaussian_hjm_model("EUR", delta, chi, ch, option_times[op_idx], swap_maturities[sw_idx], vols[op_idx, sw_idx], yts, max_iter = 5)
@test all(res.fit .> -4.1e-4)
res = DiffFusion.gaussian_hjm_model(
"EUR",
delta,
chi,
ch,
option_times[op_idx],
swap_maturities[sw_idx],
vols[op_idx, sw_idx],
yts, max_iter = 5,
volatility_regularisation = 0.1,
)
@test all(res.fit .> -7.4e-4)
@test all(res.fit .< 6.8e-4)
println("")
println("Piece-wise model calibration results 2:")
Expand All @@ -162,7 +209,18 @@ using Test
] * 1.0e-4
op_idx = [ 1, 2, 4, 6 ]
sw_idx = [ 2, 3, 4 ]
res = DiffFusion.gaussian_hjm_model("EUR", delta, chi, ch, option_times[op_idx], swap_maturities[sw_idx], vols[op_idx, sw_idx], yts, max_iter = 5)
res = DiffFusion.gaussian_hjm_model(
"EUR",
delta,
chi,
ch,
option_times[op_idx],
swap_maturities[sw_idx],
vols[op_idx, sw_idx],
yts,
max_iter = 5,
volatility_regularisation = 0.01,
)
@test all(res.fit .> -0.1e-4)
@test all(res.fit .< 0.1e-4)
println("")
Expand Down
85 changes: 83 additions & 2 deletions test/unittests/models/rates/swap_rate_calibration.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,50 @@ using Test
DiffFusion.set_correlation!(ch, "EUR_f_1", "EUR_f_3", c)
# flat implied vol surface
implied_vols = 0.01 * ones((length(option_times), length(swap_maturities)))
res = DiffFusion.gaussian_hjm_model("EUR", ch, option_times, swap_maturities, implied_vols, yts, max_iter = 5)
res = DiffFusion.gaussian_hjm_model(
"EUR",
ch,
option_times,
swap_maturities,
implied_vols, yts,
max_iter = 5,
)
@test all(res.fit .> -2.1e-4)
@test all(res.fit .< 2.6e-4)
# display(res.model.chi)
# display(res.model.sigma_T.sigma_f)
# display(res.fit * 1e+4)
end

@testset "Test flat parameter calibration with regularisation." begin
yts = DiffFusion.zero_curve("", [0.0, 10.0], [0.03, 0.03])
option_times = [ 1.0, 2.0, 5.0, 10.0 ]
swap_maturities = [ 2.0, 10.0, 20.0 ]
#
c = 0.80
ch = DiffFusion.correlation_holder("Full")
DiffFusion.set_correlation!(ch, "EUR_f_1", "EUR_f_2", c)
DiffFusion.set_correlation!(ch, "EUR_f_2", "EUR_f_3", c)
DiffFusion.set_correlation!(ch, "EUR_f_1", "EUR_f_3", c)
# flat implied vol surface
implied_vols = 0.01 * ones((length(option_times), length(swap_maturities)))
res = DiffFusion.gaussian_hjm_model(
"EUR",
ch,
option_times,
swap_maturities,
implied_vols,
yts,
max_iter = 5,
volatility_regularisation = 0.5,
)
@test all(res.fit .> -2.1e-4)
@test all(res.fit .< 3.4e-4)
# display(res.model.chi)
# display(res.model.sigma_T.sigma_f)
# display(res.fit * 1e+4)
end

@testset "Test piece-wise flat vol calibration." begin
yts = DiffFusion.zero_curve("", [0.0, 10.0], [0.03, 0.03])
option_times = [ 1.0, 2.0, 5.0, 10.0 ]
Expand All @@ -41,7 +77,16 @@ using Test

# flat implied vol surface
implied_vols = 0.01 * ones((length(option_times), length(swap_maturities)))
res = DiffFusion.gaussian_hjm_model("EUR", delta, chi, ch, option_times, swap_maturities, implied_vols, yts, max_iter = 5)
res = DiffFusion.gaussian_hjm_model(
"EUR",
delta,
chi,
ch,
option_times,
swap_maturities,
implied_vols,
yts, max_iter = 5,
)
# display(res)
@test all(res.fit .> -0.1e-4)
@test all(res.fit .< 0.1e-4)
Expand All @@ -51,4 +96,40 @@ using Test
# display(res.fit * 1e+4)
end

@testset "Test piece-wise flat vol calibration with regularisation." begin
yts = DiffFusion.zero_curve("", [0.0, 10.0], [0.03, 0.03])
option_times = [ 1.0, 2.0, 5.0, 10.0 ]
swap_maturities = [ 2.0, 10.0, 20.0 ]
#
c = 0.80
ch = DiffFusion.correlation_holder("Full")
DiffFusion.set_correlation!(ch, "EUR_f_1", "EUR_f_2", c)
DiffFusion.set_correlation!(ch, "EUR_f_2", "EUR_f_3", c)
DiffFusion.set_correlation!(ch, "EUR_f_1", "EUR_f_3", c)
#
delta = DiffFusion.flat_parameter([ 1., 7., 15. ])
chi = DiffFusion.flat_parameter([ 0.01, 0.10, 0.30 ])

# flat implied vol surface
implied_vols = 0.01 * ones((length(option_times), length(swap_maturities)))
res = DiffFusion.gaussian_hjm_model(
"EUR",
delta,
chi,
ch,
option_times,
swap_maturities,
implied_vols,
yts, max_iter = 5,
volatility_regularisation = 0.5,
)
# display(res)
@test all(res.fit .> -2.9e-4)
@test all(res.fit .< 3.0e-4)
display(res.model.chi)
# display(res.model.sigma_T.sigma_f.times)
# display(res.model.sigma_T.sigma_f.values)
# display(res.fit * 1e+4)
end

end

0 comments on commit 496165e

Please sign in to comment.