Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add bermudan regression based on exercise trigger #60

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 71 additions & 16 deletions src/products/BermudanSwaptionLeg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ and `make_regression_variables` are passed on to `BermudanSwaptionLeg`.
`path` and `make_regression` are used to create an `AmcPayoffRegression`
object for `AmcPayoff`s. This data is supposed to be updated subsequent
to leg cretion.

`regression_on_exercise_trigger = true` specifies AMC regression strategy.
If `regression_on_exercise_trigger = then` then regression on regression
is used. `regression_on_exercise_trigger = true` is recommended for
accurate sensitivity calculation.
"""
function bermudan_swaption_leg(
alias::String,
Expand All @@ -133,6 +138,7 @@ function bermudan_swaption_leg(
make_regression_variables::Function,
path::Union{AbstractPath, Nothing},
make_regression::Union{Function, Nothing},
regression_on_exercise_trigger = true,
)
#
@assert length(bermudan_exercises) > 0
Expand All @@ -157,22 +163,52 @@ function bermudan_swaption_leg(
exercise_triggers = [ Cache(Hk > Uk), ]
# backward sweep
for ex in reverse(bermudan_exercises[begin:end-1])
Hk = AmcSum(
ex.exercise_time,
[hold_values[end],],
ex.make_regression_variables(ex.exercise_time),
path,
make_regression,
numeraire_curve_key,
)
Hk = Cache(Hk)
Uk = vcat([
discounted_cashflows(leg, ex.exercise_time)
for leg in ex.cashflow_legs
]...)
Uk = Cache(sum(Uk))
hold_values = vcat(hold_values, Cache(Max(Hk, Uk)))
exercise_triggers = vcat(exercise_triggers, Cache(Hk > Uk))
if regression_on_exercise_trigger
Uk = vcat([
discounted_cashflows(leg, ex.exercise_time)
for leg in ex.cashflow_legs
]...)
Uk = Cache(sum(Uk))
Hk = AmcMax(
ex.exercise_time,
[ hold_values[end], ],
[ Uk, ],
ex.make_regression_variables(ex.exercise_time),
path,
make_regression,
numeraire_curve_key,
)
Hk = Cache(Hk)
Ik = AmcOne(
ex.exercise_time,
[ hold_values[end], ],
[ Uk, ],
ex.make_regression_variables(ex.exercise_time),
path,
make_regression,
numeraire_curve_key,
)
Ik = Cache(Ik)
hold_values = vcat(hold_values, Hk)
exercise_triggers = vcat(exercise_triggers, Ik)
else
Hk = AmcSum(
ex.exercise_time,
[hold_values[end],],
ex.make_regression_variables(ex.exercise_time),
path,
make_regression,
numeraire_curve_key,
)
Hk = Cache(Hk)
Uk = vcat([
discounted_cashflows(leg, ex.exercise_time)
for leg in ex.cashflow_legs
]...)
Uk = Cache(sum(Uk))
hold_values = vcat(hold_values, Cache(Max(Hk, Uk)))
exercise_triggers = vcat(exercise_triggers, Cache(Hk > Uk))
end
end
#
return BermudanSwaptionLeg(
Expand Down Expand Up @@ -383,12 +419,30 @@ function make_bermudan_exercises(
end


"""
bermudan_swaption_leg(
alias::String,
fixed_leg::DeterministicCashFlowLeg,
float_leg::DeterministicCashFlowLeg,
exercise_times::AbstractVector,
option_long_short::ModelValue,
regression_on_exercise_trigger = false,
)

Create a `BermudanSwaptionLeg` using simplified interface.

`regression_on_exercise_trigger = true` specifies AMC regression strategy.
If `regression_on_exercise_trigger = then` then regression on regression
is used. `regression_on_exercise_trigger = true` is recommended for
accurate sensitivity calculation.
"""
function bermudan_swaption_leg(
alias::String,
fixed_leg::DeterministicCashFlowLeg,
float_leg::DeterministicCashFlowLeg,
exercise_times::AbstractVector,
option_long_short::ModelValue,
regression_on_exercise_trigger = true,
)
#
bermudan_exercises = make_bermudan_exercises(fixed_leg, float_leg, exercise_times)
Expand All @@ -409,5 +463,6 @@ function bermudan_swaption_leg(
make_regression_variables_,
path_,
make_regression_,
regression_on_exercise_trigger,
)
end
26 changes: 25 additions & 1 deletion test/componenttests/scenarios/swaptions_expected_exposure.jl
Original file line number Diff line number Diff line change
Expand Up @@ -191,15 +191,28 @@ using UnicodePlots
make_regression_variables,
nothing, # path
nothing, # make_regression
true, # regression_on_exercise_trigger
)


berm_2 = DiffFusion.bermudan_swaption_leg(
"berm/10-nc-2 (regr_on_regr)",
[ exercise_2y, exercise_4y, exercise_6y, exercise_8y, ],
1.0, # long option
"", # default discounting (curve key)
make_regression_variables,
nothing, # path
nothing, # make_regression
false, # regression_on_exercise_trigger
)


# AMC Regression

#make_regression = (C, O) -> DiffFusion.polynomial_regression(C, O, 2)
make_regression = (C, O) -> DiffFusion.piecewise_regression(C, O, 2, [3])

DiffFusion.reset_regression!(berm, path, make_regression)
DiffFusion.reset_regression!(berm_2, path, make_regression)


# Scenario Calculation
Expand All @@ -213,13 +226,15 @@ using UnicodePlots
swaption_8y_scens = DiffFusion.scenarios([swaption_8y], times, path, "", with_progress_bar=false)

berm_scens = DiffFusion.scenarios([berm], times, path, "", with_progress_bar=false)
berm_2_scens = DiffFusion.scenarios([berm_2], times, path, "", with_progress_bar=false)

vanilla_swap_ee = DiffFusion.expected_exposure(vanilla_swap_scens)
swaption_2y_ee = DiffFusion.expected_exposure(swaption_2y_scens)
swaption_4y_ee = DiffFusion.expected_exposure(swaption_4y_scens)
swaption_6y_ee = DiffFusion.expected_exposure(swaption_6y_scens)
swaption_8y_ee = DiffFusion.expected_exposure(swaption_8y_scens)
berm_ee = DiffFusion.expected_exposure(berm_scens)
berm_2_ee = DiffFusion.expected_exposure(berm_2_scens)

portfolo = DiffFusion.join_scenarios([
vanilla_swap_ee,
Expand All @@ -230,6 +245,11 @@ using UnicodePlots
berm_ee,
])

portfolo_berms = DiffFusion.join_scenarios([
berm_ee,
berm_2_ee,
])

function plot_scens(scens, title)
plt = lineplot(scens.times, scens.X[1,:,:],
title = title,
Expand All @@ -245,8 +265,12 @@ using UnicodePlots
end

plot_scens(portfolo, "Rates Derivatives")
plot_scens(portfolo_berms, "Bermudans")

max_european_ee = maximum(portfolo.X[1,:,2:5], dims=2)[:,1]
berm_ee = portfolo.X[1,:,6]
@test berm_ee ≥ max_european_ee

rel_error_berms = (portfolo_berms.X[1,:,1] .+ 1.0e-8) ./ (portfolo_berms.X[1,:,2] .+ 1.0e-8) .- 1.0
@test maximum(abs.(rel_error_berms)) < 0.007
end
2 changes: 1 addition & 1 deletion test/componenttests/sensitivities/swaptions_delta_vega.jl
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ using UnicodePlots
@test abs(sum(g2[5:8])/sum(g1[5:8]) - 1.0) < 7.5e-2 # berm_4y ESTR
#
@test abs(sum(g3[1:4])/sum(g1[1:4]) - 1.0) < 0.27 # berm_10nc2 EURIBOR6M Delta
@test abs(sum(g3[5:8])/sum(g1[5:8]) - 1.0) < 0.17 # berm_10nc2 ESTR
@test abs(sum(g3[5:8])/sum(g1[5:8]) - 1.0) < 0.28 # berm_10nc2 ESTR
end


Expand Down
39 changes: 39 additions & 0 deletions test/unittests/products/bermudan_swaption_leg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,30 @@ berm_35 = "((1.0000 - {({"*option_10*"} > {"*underl_10*"})} * {({"*option_20*"}
berm_40 = "((1.0000 - {({"*option_10*"} > {"*underl_10*"})} * {({"*option_20*"} > {"*underl_20*"})} * {(0.0000 > {"*underl_30*"})}) * "*underl_40*" @ 4.00)"
berm_45 = "((1.0000 - {({"*option_10*"} > {"*underl_10*"})} * {({"*option_20*"} > {"*underl_20*"})} * {(0.0000 > {"*underl_30*"})}) * "*underl_45*" @ 4.50)"

berm_2_00 =
"AmcSum(0.00, [{AmcMax(1.00, [{AmcMax(2.00, [{Max(0.0000, {(((" *
"(P(EUR:OIS, 3.00, 4.00) * 1.0000 * 0.0300 * 1.0000 @ 3.00) + " *
"(P(EUR:OIS, 3.00, 5.00) * 1.0000 * 0.0300 * 1.0000 @ 3.00)) + " *
"(P(EUR:OIS, 3.00, 4.00) * -1.0000 * L(EURIBOR12M, 3.00; 3.00, 4.00) * 1.0000 @ 3.00)) + " *
"(P(EUR:OIS, 3.00, 5.00) * -1.0000 * L(EURIBOR12M, 3.00; 4.00, 5.00) * 1.0000 @ 3.00))})}], [{(((((" *
"(P(EUR:OIS, 2.00, 3.00) * 1.0000 * 0.0300 * 1.0000 @ 2.00) + " *
"(P(EUR:OIS, 2.00, 4.00) * 1.0000 * 0.0300 * 1.0000 @ 2.00)) + " *
"(P(EUR:OIS, 2.00, 5.00) * 1.0000 * 0.0300 * 1.0000 @ 2.00)) + " *
"(P(EUR:OIS, 2.00, 3.00) * -1.0000 * L(EURIBOR12M, 2.00; 2.00, 3.00) * 1.0000 @ 2.00)) + " *
"(P(EUR:OIS, 2.00, 4.00) * -1.0000 * L(EURIBOR12M, 2.00; 3.00, 4.00) * 1.0000 @ 2.00)) + " *
"(P(EUR:OIS, 2.00, 5.00) * -1.0000 * L(EURIBOR12M, 2.00; 4.00, 5.00) * 1.0000 @ 2.00))}], " *
"[L(EURIBOR12M, 2.00; 2.00, 5.00)])}], [{(((((((" *
"(P(EUR:OIS, 1.00, 2.00) * 1.0000 * 0.0300 * 1.0000 @ 1.00) + " *
"(P(EUR:OIS, 1.00, 3.00) * 1.0000 * 0.0300 * 1.0000 @ 1.00)) + " *
"(P(EUR:OIS, 1.00, 4.00) * 1.0000 * 0.0300 * 1.0000 @ 1.00)) + " *
"(P(EUR:OIS, 1.00, 5.00) * 1.0000 * 0.0300 * 1.0000 @ 1.00)) + " *
"(P(EUR:OIS, 1.00, 2.00) * -1.0000 * L(EURIBOR12M, 1.00; 1.00, 2.00) * 1.0000 @ 1.00)) + " *
"(P(EUR:OIS, 1.00, 3.00) * -1.0000 * L(EURIBOR12M, 1.00; 2.00, 3.00) * 1.0000 @ 1.00)) + " *
"(P(EUR:OIS, 1.00, 4.00) * -1.0000 * L(EURIBOR12M, 1.00; 3.00, 4.00) * 1.0000 @ 1.00)) + " *
"(P(EUR:OIS, 1.00, 5.00) * -1.0000 * L(EURIBOR12M, 1.00; 4.00, 5.00) * 1.0000 @ 1.00))}], " *
"[L(EURIBOR12M, 1.00; 1.00, 5.00)])}], [], [L(EURIBOR12M, 0.00; 0.00, 5.00)])"


@testset "Test discounted_cashflows" begin
berm = DiffFusion.bermudan_swaption_leg(
"berm",
Expand All @@ -270,6 +294,7 @@ berm_45 = "((1.0000 - {({"*option_10*"} > {"*underl_10*"})} * {({"*option_20*"}
make_regression_variables,
nothing, # path
nothing, # make_regression
false, # regression_on_exercise_trigger
)
@test isa(berm, DiffFusion.BermudanSwaptionLeg)
cfs = DiffFusion.discounted_cashflows(berm, 0.0)
Expand All @@ -287,6 +312,18 @@ berm_45 = "((1.0000 - {({"*option_10*"} > {"*underl_10*"})} * {({"*option_20*"}

@test length(DiffFusion.discounted_cashflows(berm, 5.0)) == 0
@test length(DiffFusion.discounted_cashflows(berm, 5.5)) == 0

berm_2 = DiffFusion.bermudan_swaption_leg(
"berm",
[ exercise_1, exercise_2, exercise_3,],
1.0, # long option
"OIS", # default discounting (curve key)
make_regression_variables,
nothing, # path
nothing, # make_regression
true, # regression_on_exercise_trigger
)
@test string(DiffFusion.discounted_cashflows(berm_2, 0.0)[1]) == berm_2_00
end

@testset "Test regression details setup" begin
Expand All @@ -302,6 +339,7 @@ berm_45 = "((1.0000 - {({"*option_10*"} > {"*underl_10*"})} * {({"*option_20*"}
make_regression_variables,
NoPath(), # path
no_make_regression, # make_regression
false, # regression_on_exercise_trigger
)
@test isa(berm, DiffFusion.BermudanSwaptionLeg)
@test berm.regression_data.path == NoPath()
Expand All @@ -319,6 +357,7 @@ berm_45 = "((1.0000 - {({"*option_10*"} > {"*underl_10*"})} * {({"*option_20*"}
make_regression_variables,
nothing, # path
nothing, # make_regression
false, # regression_on_exercise_trigger
)
@test isa(berm, DiffFusion.BermudanSwaptionLeg)
@test isnothing(berm.regression_data.path)
Expand Down