diff --git a/src/DiffFusion.jl b/src/DiffFusion.jl index f9434371..c68b26e8 100644 --- a/src/DiffFusion.jl +++ b/src/DiffFusion.jl @@ -78,7 +78,10 @@ module Examples using DiffFusion:ModelValue using OrderedCollections using Random + using YAML # + include("examples/Examples.jl") + include("examples/Models.jl") include("examples/Products.jl") end # module diff --git a/src/examples/Examples.jl b/src/examples/Examples.jl new file mode 100644 index 00000000..9d05baff --- /dev/null +++ b/src/examples/Examples.jl @@ -0,0 +1,46 @@ + +"A list of example models and instrument specifications." +examples = [ + "g3_1factor_flat", +] + +""" + load(name::String) + +Return a list of dictionaries representing a DiffFusion example. + +Example details can be modified by changing the dictionary entries. +""" +function load(name::String) + file_name = joinpath(@__DIR__, "yamls", name * ".yaml") + dict_list = YAML.load_file(file_name; dicttype=OrderedDict{String,Any}) + return dict_list +end + + +""" + build(dict_list::Vector{OrderedDict{String, Any}}) + +Return a dictionary of objects and configurations representing a +DiffFusion example. + +The resulting dictionary is supposed to be queried and amended by +methods operating on examples. +""" +function build(dict_list::Vector{OrderedDict{String, Any}}) + example = DiffFusion.deserialise_from_list(dict_list) + return example +end + +""" +Return the first object of a given type from an example dictionary. +""" +function get_object(example::OrderedDict{String,Any}, obj_type) + for value in values(example) + if typeof(value) == obj_type + return value + end + end + error("Example does not contain " * string(obj_type) * ".") +end + diff --git a/src/examples/Models.jl b/src/examples/Models.jl new file mode 100644 index 00000000..932c1a76 --- /dev/null +++ b/src/examples/Models.jl @@ -0,0 +1,126 @@ + +""" + model(example::OrderedDict{String,Any}) + +Return the hybrid model of an example. +""" +model(example::OrderedDict{String,Any}) = get_object(example, DiffFusion.SimpleModel) + +""" + correlation_holder(example::OrderedDict{String,Any}) + +Return the correlation holder of an example. +""" +correlation_holder(example::OrderedDict{String,Any}) = get_object(example, DiffFusion.CorrelationHolder) + +""" + context(example::OrderedDict{String,Any}) + +Return the context of a given example. +""" +context(example::OrderedDict{String,Any}) = get_object(example, DiffFusion.Context) + +""" + term_structures(example::OrderedDict{String,Any}) + +Return a dictionary of term structures for an example. +""" +function term_structures(example::OrderedDict{String,Any}) + ts_dict = Dict{String,DiffFusion.Termstructure}() + for (key, value) in example + if isa(value, DiffFusion.Termstructure) + ts_dict[key] = value + end + end + return ts_dict +end + +""" + simulation!(example::OrderedDict{String,Any}) + +Return a Monte Carlo simulation for a given example. + +If no simulation exists it is created. +""" +function simulation!(example::OrderedDict{String,Any}) + for value in values(example) + if typeof(value) == DiffFusion.Simulation + return value + end + end + @assert haskey(example, "config/simulation") + config = example["config/simulation"] + # + model_ = model(example) + ch_ = correlation_holder(example) + # + @assert haskey(config, "simulation_times") + times = config["simulation_times"] + if isa(times, AbstractDict) + times = Vector(times["start"]:times["step"]:times["stop"]) + end + @assert isa(times, Vector) + # + @assert haskey(config, "n_paths") + n_paths = config["n_paths"] + @assert typeof(n_paths) == Int + # + if haskey(config, "with_progress_bar") + with_progress_bar = config["with_progress_bar"] + else + with_progress_bar = true + end + @assert typeof(with_progress_bar) == Bool + # + if haskey(config, "seed") + brownian_increments(n_states::Int, n_paths::Int, n_times::Int) = + DiffFusion.pseudo_brownian_increments(n_states, n_paths, n_times, config["seed"]) + else + brownian_increments = DiffFusion.sobol_brownian_increments + end + sim = DiffFusion.simple_simulation( + model_, + ch_, + times, + n_paths, + with_progress_bar = with_progress_bar, + brownian_increments = brownian_increments, + ) + example[DiffFusion.alias(model_) * "/simulation"] = sim + return sim +end + +""" + path!(example::OrderedDict{String,Any}) + +Return a Monte Carlo path for a given example. +""" +function path!(example::OrderedDict{String,Any}) + for value in values(example) + if typeof(value) == DiffFusion.Path + return value + end + end + sim = simulation!(example) + ts_dict = term_structures(example) + # + @assert haskey(example, "config/simulation") + config = example["config/simulation"] + # + ctx = context(example) + if haskey(config, "path_interpolation") + path_interpolation = config["path_interpolation"] + @assert typeof(path_interpolation) == Bool + if path_interpolation + path_interpolation = DiffFusion.LinearPathInterpolation + else + path_interpolation = DiffFusion.NoPathInterpolation + end + else + path_interpolation = DiffFusion.NoPathInterpolation + end + # + path_ = DiffFusion.path(sim, ts_dict, ctx, path_interpolation) + example[DiffFusion.alias(sim.model) * "/path"] = path_ + return path_ +end diff --git a/src/examples/Products.jl b/src/examples/Products.jl index d27c42ce..b10e49dc 100644 --- a/src/examples/Products.jl +++ b/src/examples/Products.jl @@ -283,3 +283,35 @@ function display_portfolio(example::OrderedDict{String,Any}) end end +""" + scenarios!(example::OrderedDict{String,Any}) + +Create the exposure scenarios for the portfolio. +""" +function scenarios!(example::OrderedDict{String,Any}) + if haskey(example, "scenarios") + return example["scenarios"] + end + config = example["config/instruments"] + @assert haskey(config, "obs_times") + obs_times = config["obs_times"] + if isa(obs_times, AbstractDict) + obs_times = Vector(obs_times["start"]:obs_times["step"]:obs_times["stop"]) + end + @assert isa(obs_times, Vector) + # + if haskey(config, "with_progress_bar") + with_progress_bar = config["with_progress_bar"] + else + with_progress_bar = true + end + @assert typeof(with_progress_bar) == Bool + # + discount_curve_key = config["discount_curve_key"] + # + legs = vcat(portfolio!(example)...) + path_ = path!(example) + scens = DiffFusion.scenarios(legs, obs_times, path_, discount_curve_key, with_progress_bar=with_progress_bar) + example["scenarios"] = scens + return scens +end diff --git a/src/examples/yamls/g3_1factor_flat.yaml b/src/examples/yamls/g3_1factor_flat.yaml new file mode 100644 index 00000000..3437e48c --- /dev/null +++ b/src/examples/yamls/g3_1factor_flat.yaml @@ -0,0 +1,496 @@ +# Context +- typename: "DiffFusion.Context" + constructor: "Context" + alias: "ct/STD" + numeraire: + typename: "DiffFusion.NumeraireEntry" + constructor: "NumeraireEntry" + context_key: "USD" + model_alias: "md/USD" + termstructure_alias: + : "yc/USD:SOFR" + rates: + USD: + typename: "DiffFusion.RatesEntry" + constructor: "RatesEntry" + context_key: "USD" + model_alias: "md/USD" + termstructure_alias: + : "yc/USD:SOFR" + SOFR : "yc/USD:SOFR" + LIB3M : "yc/USD:LIB3M" + EUR: + typename: "DiffFusion.RatesEntry" + constructor: "RatesEntry" + context_key: "EUR" + model_alias: "md/EUR" + termstructure_alias: + : "yc/EUR:XCCY" + XCCY : "yc/EUR:XCCY" + ESTR : "yc/EUR:ESTR" + EURIBOR6M : "yc/EUR:EURIBOR6M" + GBP: + typename: "DiffFusion.RatesEntry" + constructor: "RatesEntry" + context_key: "GBP" + model_alias: "md/GBP" + termstructure_alias: + : "yc/GBP:XCCY" + XCCY : "yc/GBP:XCCY" + SONIA : "yc/GBP:SONIA" + assets: + EUR-USD: + typename: "DiffFusion.AssetEntry" + constructor: "AssetEntry" + context_key: "EUR-USD" + asset_model_alias: "md/EUR-USD" + domestic_model_alias: "md/USD" + foreign_model_alias: "md/EUR" + asset_spot_alias: "pa/EUR-USD" + domestic_termstructure_alias: + : "yc/USD:SOFR" + foreign_termstructure_alias: + : "yc/EUR:XCCY" + GBP-USD: + typename: "DiffFusion.AssetEntry" + constructor: "AssetEntry" + context_key: "GBP-USD" + asset_model_alias: "md/GBP-USD" + domestic_model_alias: "md/USD" + foreign_model_alias: "md/GBP" + asset_spot_alias: "pa/GBP-USD" + domestic_termstructure_alias: + : "yc/USD:SOFR" + foreign_termstructure_alias: + : "yc/GBP:XCCY" + forward_indices: + EUHICP: + typename: "DiffFusion.ForwardIndexEntry" + constructor: "ForwardIndexEntry" + context_key: "EUHICP" + asset_model_alias: "md/EUHICP" + domestic_model_alias: "md/USD" + foreign_model_alias: "md/EUHICP-RR" + forward_index_alias: "pa/EUHICP" + future_indices: + NIK: + typename: "DiffFusion.FutureIndexEntry" + constructor: "FutureIndexEntry" + context_key: "NIK" + future_model_alias: "md/NIK-FUT" + future_index_alias: "pa/NIK-FUT" + fixings: + USD:SOFR: + typename: "DiffFusion.FixingEntry" + constructor: "FixingEntry" + context_key: "USD:SOFR" + termstructure_alias: "pa/USD:SOFR" + USD:LIB3M: + typename: "DiffFusion.FixingEntry" + constructor: "FixingEntry" + context_key: "USD:LIB3M" + termstructure_alias: "pa/USD:LIB3M" + EUR:ESTR: + typename: "DiffFusion.FixingEntry" + constructor: "FixingEntry" + context_key: "EUR:ESTR" + termstructure_alias: "pa/EUR:ESTR" + EUR:EURIBOR6M: + typename: "DiffFusion.FixingEntry" + constructor: "FixingEntry" + context_key: "EUR:EURIBOR6M" + termstructure_alias: "pa/EUR:EURIBOR6M" + GBP:SONIA: + typename: "DiffFusion.FixingEntry" + constructor: "FixingEntry" + context_key: "GBP:SONIA" + termstructure_alias: "pa/GBP:SONIA" + EUR-USD: + typename: "DiffFusion.FixingEntry" + constructor: "FixingEntry" + context_key: "EUR-USD" + termstructure_alias: "pa/EUR-USD" + GBP-USD: + typename: "DiffFusion.FixingEntry" + constructor: "FixingEntry" + context_key: "GBP-USD" + termstructure_alias: "pa/GBP-USD" +# +# Correlations +- typename: "DiffFusion.CorrelationHolder" + constructor: "CorrelationHolder" + alias: "ch/STD" + correlations: + md/EUR_f_1<>md/USD_f_1: 0.3 + md/EUR-USD_x<>md/USD_f_1: -0.2 + md/EUR-USD_x<>md/EUR_f_1: -0.3 + # + md/GBP_f_1<>md/USD_f_1: 0.3 + md/GBP-USD_x<>md/USD_f_1: -0.2 + md/GBP-USD_x<>md/GBP_f_1: -0.3 + # + md/EUR_f_1<>md/GBP_f_1: 0.3 + sep: "<>" +# Models +- typename: "DiffFusion.LognormalAssetModel" + constructor: "lognormal_asset_model" + alias: "md/EUR-USD" + sigma_x: + typename: "DiffFusion.BackwardFlatVolatility" + constructor: "backward_flat_volatility" + alias: "vol/EUR-USD" + times: + - 0.0 + values: + - 0.15 + correlation_holder: "{ch/STD}" + quanto_model: "nothing" +# +- typename: "DiffFusion.LognormalAssetModel" + constructor: "lognormal_asset_model" + alias: "md/GBP-USD" + sigma_x: + typename: "DiffFusion.BackwardFlatVolatility" + constructor: "backward_flat_volatility" + alias: "vol/GBP-USD" + times: + - 0.0 + values: + - 0.12 + correlation_holder: "{ch/STD}" + quanto_model: "nothing" +# +- typename: "DiffFusion.GaussianHjmModel" + constructor: "gaussian_hjm_model" + alias: "md/USD" + delta: + typename: "DiffFusion.BackwardFlatParameter" + constructor: "flat_parameter" + alias: "" + value: 0.0 + chi: + typename: "DiffFusion.BackwardFlatParameter" + constructor: "flat_parameter" + alias: "" + value: 0.03 + sigma_f: + typename: "DiffFusion.BackwardFlatVolatility" + constructor: "flat_volatility" + alias: "" + value: 0.0075 + correlation_holder: "{ch/STD}" + quanto_model: "nothing" +# +- typename: "DiffFusion.GaussianHjmModel" + constructor: "gaussian_hjm_model" + alias: "md/EUR" + delta: + typename: "DiffFusion.BackwardFlatParameter" + constructor: "flat_parameter" + alias: "" + value: 0.0 + chi: + typename: "DiffFusion.BackwardFlatParameter" + constructor: "flat_parameter" + alias: "" + value: 0.03 + sigma_f: + typename: "DiffFusion.BackwardFlatVolatility" + constructor: "flat_volatility" + alias: "" + value: 0.0065 + correlation_holder: "{ch/STD}" + quanto_model: "{md/EUR-USD}" +# +- typename: "DiffFusion.GaussianHjmModel" + constructor: "gaussian_hjm_model" + alias: "md/GBP" + delta: + typename: "DiffFusion.BackwardFlatParameter" + constructor: "flat_parameter" + alias: "" + value: 0.0 + chi: + typename: "DiffFusion.BackwardFlatParameter" + constructor: "flat_parameter" + alias: "" + value: 0.03 + sigma_f: + typename: "DiffFusion.BackwardFlatVolatility" + constructor: "flat_volatility" + alias: "" + value: 0.0055 + correlation_holder: "{ch/STD}" + quanto_model: "{md/GBP-USD}" +# +- typename: "DiffFusion.SimpleModel" + constructor: "simple_model" + alias: "md/G3" + models: + - "{md/USD}" + - "{md/EUR}" + - "{md/GBP}" + - "{md/EUR-USD}" + - "{md/GBP-USD}" +# Yield curves +- typename: "DiffFusion.FlatForward" + constructor: "FlatForward" + alias: "yc/USD:SOFR" + rate: 0.0358 +# +- typename: "DiffFusion.FlatForward" + constructor: "FlatForward" + alias: "yc/USD:LIB3M" + rate: 0.0374 +# +- typename: "DiffFusion.FlatForward" + constructor: "FlatForward" + alias: "yc/EUR:ESTR" + rate: 0.0297 +# +- typename: "DiffFusion.FlatForward" + constructor: "FlatForward" + alias: "yc/EUR:XCCY" + rate: 0.0293 +# +- typename: "DiffFusion.FlatForward" + constructor: "FlatForward" + alias: "yc/EUR:EURIBOR6M" + rate: 0.0316 +# +- typename: "DiffFusion.FlatForward" + constructor: "FlatForward" + alias: "yc/GBP:SONIA" + rate: 0.0371 +# +- typename: "DiffFusion.FlatForward" + constructor: "FlatForward" + alias: "yc/GBP:XCCY" + rate: 0.0376 +# Fixings +- typename: "DiffFusion.ForwardFlatParameter" + constructor: "forward_flat_parameter" + alias: "pa/USD:SOFR" + times: + - 0.00 + values: + - 0.0455 +# +- typename: "DiffFusion.ForwardFlatParameter" + constructor: "forward_flat_parameter" + alias: "pa/USD:LIB3M" + times: + - 0.00 + values: + - 0.0486 +# +- typename: "DiffFusion.ForwardFlatParameter" + constructor: "forward_flat_parameter" + alias: "pa/EUR:ESTR" + times: + - 0.00 + values: + - 0.0240 +# +- typename: "DiffFusion.ForwardFlatParameter" + constructor: "forward_flat_parameter" + alias: "pa/EUR:EURIBOR6M" + times: + - 0.00 + values: + - 0.0308 +# +- typename: "DiffFusion.ForwardFlatParameter" + constructor: "forward_flat_parameter" + alias: "pa/GBP:SONIA" + times: + - 0.00 + values: + - 0.0308 +# +- typename: "DiffFusion.ForwardFlatParameter" + constructor: "forward_flat_parameter" + alias: "pa/EUR-USD" + times: + - -0.25 + - -0.12 + - 0.00 + values: + - 1.07 + - 1.07 + - 1.07 +# +- typename: "DiffFusion.ForwardFlatParameter" + constructor: "forward_flat_parameter" + alias: "pa/GBP-USD" + times: + - -0.25 + - -0.12 + - 0.00 + values: + - 1.09 + - 1.12 + - 1.20 +# +- typename: "DiffFusion.ForwardFlatParameter" + constructor: "forward_flat_parameter" + alias: "pa/EUHICP" + times: + - 0.00 + values: + - 1.00 +- typename: "DiffFusion.ForwardFlatParameter" + constructor: "forward_flat_parameter" + alias: "pa/NIK-FUT" + times: + - 0.00 + values: + - 23776.50 +# Configs +- alias: "config/simulation" + simulation_times: + start: 0.0 + step: 1.0 + stop: 10.0 + n_paths: 8192 + with_progress_bar: true + seed: 42 + path_interpolation: true +- alias: "config/instruments" + seed: 123456 + obs_times: + start: 0.0 + step: 1.0 + stop: 10.0 + with_progress_bar: true + discount_curve_key: "" + types: + - USD + - EUR + - GBP + - EUR-USD + - GBP-USD + - EUR6M-USD3M + USD: + type: VANILLA + discount_curve_key: USD:SOFR + fx_key: nothing + min_maturity: 1.0 + max_maturity: 10.0 + min_notional: 1.0e+7 + max_notional: 1.0e+8 + fixed_leg: + coupons_per_year: 4 + min_rate: 0.01 + max_rate: 0.04 + float_leg: + coupon_type: COMPOUNDED + coupons_per_year: 4 + forward_curve_key: USD:SOFR + fixing_key: USD:SOFR + EUR: + type: VANILLA + discount_curve_key: EUR:XCCY + fx_key: EUR-USD + min_maturity: 1.0 + max_maturity: 10.0 + min_notional: 1.0e+7 + max_notional: 1.0e+8 + fixed_leg: + coupons_per_year: 1 + min_rate: 0.01 + max_rate: 0.04 + float_leg: + coupon_type: SIMPLE + coupons_per_year: 2 + forward_curve_key: EUR:EURIBOR6M + fixing_key: EUR:EURIBOR6M + GBP: + type: VANILLA + discount_curve_key: GBP:XCCY + fx_key: GBP-USD + min_maturity: 1.0 + max_maturity: 10.0 + min_notional: 1.0e+7 + max_notional: 1.0e+8 + fixed_leg: + coupons_per_year: 4 + min_rate: 0.01 + max_rate: 0.04 + float_leg: + coupon_type: COMPOUNDED + coupons_per_year: 4 + forward_curve_key: GBP:SONIA + fixing_key: GBP:SONIA + EUR-USD: + type: BASIS-MTM + min_maturity: 1.0 + max_maturity: 10.0 + min_notional: 1.0e+7 + max_notional: 1.0e+8 + dom_leg: + coupon_type: COMPOUNDED + coupons_per_year: 4 + forward_curve_key: USD:SOFR + fixing_key: USD:SOFR + # + discount_curve_key: USD:SOFR + fx_key: nothing + for_leg: + coupon_type: COMPOUNDED + coupons_per_year: 4 + forward_curve_key: EUR:ESTR + fixing_key: EUR:ESTR + min_spread: 0.01 + max_spread: 0.03 + # + discount_curve_key: EUR:XCCY + fx_key: EUR-USD + GBP-USD: + type: BASIS-MTM + min_maturity: 1.0 + max_maturity: 10.0 + min_notional: 1.0e+7 + max_notional: 1.0e+8 + dom_leg: + coupon_type: COMPOUNDED + coupons_per_year: 4 + forward_curve_key: USD:SOFR + fixing_key: USD:SOFR + # + discount_curve_key: USD:SOFR + fx_key: nothing + for_leg: + coupon_type: COMPOUNDED + coupons_per_year: 4 + forward_curve_key: GBP:SONIA + fixing_key: GBP:SONIA + min_spread: 0.01 + max_spread: 0.03 + # + discount_curve_key: GBP:XCCY + fx_key: GBP-USD + EUR6M-USD3M: + type: BASIS-MTM + min_maturity: 1.0 + max_maturity: 10.0 + min_notional: 1.0e+7 + max_notional: 1.0e+8 + dom_leg: + coupon_type: SIMPLE + coupons_per_year: 4 + forward_curve_key: USD:LIB3M + fixing_key: USD:LIB3M + # + discount_curve_key: USD:SOFR + fx_key: nothing + for_leg: + coupon_type: SIMPLE + coupons_per_year: 2 + forward_curve_key: EUR:EURIBOR6M + fixing_key: EUR:EURIBOR6M + min_spread: 0.01 + max_spread: 0.03 + # + discount_curve_key: EUR:XCCY + fx_key: EUR-USD diff --git a/test/unittests/examples/examples.jl b/test/unittests/examples/examples.jl index dd6f8b10..8cf026fa 100644 --- a/test/unittests/examples/examples.jl +++ b/test/unittests/examples/examples.jl @@ -4,5 +4,7 @@ using Test @testset "Test Examples." begin include("products.jl") + include("simulations.jl") + include("scenarios.jl") end diff --git a/test/unittests/examples/products.jl b/test/unittests/examples/products.jl index fd9085e2..57273ce7 100644 --- a/test/unittests/examples/products.jl +++ b/test/unittests/examples/products.jl @@ -164,6 +164,28 @@ using YAML fx_key: EUR-USD """ + @testset "Test leg generation" begin + leg = DiffFusion.Examples.fixed_rate_leg( + "", 0.0, 10.0, 1, 0.03, 1.0e+4, "USD" + ) + @test length(leg.cashflows) == 10 + @test isa(leg.cashflows[1], DiffFusion.FixedRateCoupon) + # + leg = DiffFusion.Examples.simple_rate_leg( + "", 0.0, 2.0, 2, "EUR:EURIBOR6M", "EUR:EURIBOR6M", nothing, 1.0e+4, "EUR", "EUR-USD" + ) + @test length(leg.cashflows) == 4 + @test isa(leg.cashflows[1], DiffFusion.SimpleRateCoupon) + # + leg = DiffFusion.Examples.compounded_rate_leg( + "", 0.0, 1.0, 4, "USD:SOFR", "USD:SOFR", nothing, 1.0e+4, "USD" + ) + @test length(leg.cashflows) == 4 + @test isa(leg.cashflows[1], DiffFusion.CompoundedRateCoupon) + #println(leg) + end + + @testset "Test swap generation" begin example = YAML.load(yaml_string, dicttype=OrderedDict{String,Any}) for type_key in example["config/instruments"]["types"] diff --git a/test/unittests/examples/scenarios.jl b/test/unittests/examples/scenarios.jl new file mode 100644 index 00000000..c26cf187 --- /dev/null +++ b/test/unittests/examples/scenarios.jl @@ -0,0 +1,44 @@ + +using DiffFusion +using OrderedCollections +using StatsBase +using Test + +@testset "Test Example scenarios." begin + + @testset "Test scenario generation" begin + serialised_example = DiffFusion.Examples.load(DiffFusion.Examples.examples[1]) + example = DiffFusion.Examples.build(serialised_example) + p = DiffFusion.Examples.portfolio!(example, 5) + example["config/simulation"]["n_paths"] = 2^3 + example["config/simulation"]["with_progress_bar"] = false + example["config/instruments"]["with_progress_bar"] = false + scens = DiffFusion.Examples.scenarios!(example) + @test size(scens.X) == (8,11,10) + #println(size(scens.X)) + if (VERSION ≥ v"1.8") + include("../test_tolerances.jl") + abs_tol = test_tolerances["examples/examples.jl"] + @info "Run regression test with tolerance abs_tol=" * string(abs_tol) * "." + model_prices = mean(scens.X, dims=1)[1,:,:] + model_prices_ref = [ + [-8.800555497384697e6, -6.07808164368673e6, -3.3187282691737576e6, -672029.3457423754, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [ 8.160079321240904e6, 5.7823072140125595e6, 3.08128489092463e6, 594154.193234952, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [ 1.2822341462636108e6, 1.3029336403312571e6, 961005.648992561, 649862.4700369746, 330640.52573131735, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [-4.7792180451030135e6, -4.957854667238729e6, -3.570560697004381e6, -2.2736408185762935e6, -1.0536949659888982e6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [ 2.052058322602283e7, 1.8327767574485175e7, 1.5474993060098104e7, 1.2939567414871339e7, 1.0337122228566911e7, 8.146139880661089e6, 5.786556779180116e6, 3.683177067624899e6, 1.7783174034722417e6, 0.0, 0.0], + [-1.0293449963390617e7, -1.2045759086815968e7, -1.0130447923588615e7, -8.252266093887748e6, -6.365452567594981e6, -4.960721291998119e6, -3.592765178156436e6, -2.3417890170609113e6, -910843.158261814, 0.0, 0.0], + [-3.0867037091375636e6, -3.1021029535281807e6, -2.4262161572243813e6, -1.7936053330619307e6, -1.1811297262802331e6, -579710.019653324, 0.0, 0.0, 0.0, 0.0, 0.0], + [ 1.1099997673603833e7, 1.0682319288259184e7, 8.85133360413585e6, 6.075419450480742e6, 3.719612737319657e6, 1.981583938268915e6, 0.0, 0.0, 0.0, 0.0, 0.0], + [-2.117850952661921e6, -1.6730349282217112e6, -1.1200395685295006e6, -750925.3389081238, -333348.8520029578, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [ 1.5292265651649681e6, 1.1772724443825367e6, 1.0041035713803598e6, 402283.8362401064, 144423.71924248966, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + ] + for k in axes(model_prices, 2) + @test isapprox(model_prices[:,k], model_prices_ref[k], atol=abs_tol) + # println(scens.leg_aliases[k]) + # println(model_prices[:,k]) + end + end + end + +end \ No newline at end of file diff --git a/test/unittests/examples/simulations.jl b/test/unittests/examples/simulations.jl new file mode 100644 index 00000000..6fca9894 --- /dev/null +++ b/test/unittests/examples/simulations.jl @@ -0,0 +1,100 @@ + +using DiffFusion +using OrderedCollections +using Test + + +@testset "Test Example simulations." begin + + # some short-cuts + alias = DiffFusion.alias + at = DiffFusion.at + + + "Run tests for a given example." + function test_example_simulation(ex_name::String) + serialised_example = DiffFusion.Examples.load(ex_name) + example = DiffFusion.Examples.build(serialised_example) + model = DiffFusion.Examples.model(example) + ch = DiffFusion.Examples.correlation_holder(example) + # we do not want outputs here + example["config/simulation"]["with_progress_bar"] = false + sim = DiffFusion.Examples.simulation!(example) + ctx = DiffFusion.Examples.context(example) + ts_dict = DiffFusion.Examples.term_structures(example) + path_ = DiffFusion.Examples.path!(example) + # + @test typeof(serialised_example) == Vector{OrderedDict{String, Any}} + @test typeof(example) == OrderedDict{String, Any} + @test typeof(model) == DiffFusion.SimpleModel + @test typeof(ch) == DiffFusion.CorrelationHolder + @test typeof(sim) == DiffFusion.Simulation + @test sim == example[alias(model) * "/simulation"] + @test typeof(ctx) == DiffFusion.Context + @test typeof(ts_dict) == Dict{String, DiffFusion.Termstructure} + @test typeof(path_) == DiffFusion.Path + @test path_ == example[alias(sim.model) * "/path"] + # + n_paths = example["config/simulation"]["n_paths"] + @test size(at(DiffFusion.Numeraire(1.0, ""), path_)) == (n_paths,) + # + @test size(at(DiffFusion.BankAccount(1.0, "USD"), path_)) == (n_paths,) + @test size(at(DiffFusion.BankAccount(1.0, "USD:SOFR"), path_)) == (n_paths,) + @test size(at(DiffFusion.BankAccount(1.0, "USD:LIB3M"), path_)) == (n_paths,) + # + @test size(at(DiffFusion.BankAccount(1.0, "EUR"), path_)) == (n_paths,) + @test size(at(DiffFusion.BankAccount(1.0, "EUR:XCCY"), path_)) == (n_paths,) + @test size(at(DiffFusion.BankAccount(1.0, "EUR:ESTR"), path_)) == (n_paths,) + # + @test size(at(DiffFusion.BankAccount(1.0, "GBP"), path_)) == (n_paths,) + @test size(at(DiffFusion.BankAccount(1.0, "GBP:XCCY"), path_)) == (n_paths,) + @test size(at(DiffFusion.BankAccount(1.0, "GBP:SONIA"), path_)) == (n_paths,) + # + @test size(at(DiffFusion.ZeroBond(1.0, 2.0, "USD"), path_)) == (n_paths,) + @test size(at(DiffFusion.ZeroBond(1.0, 2.0, "USD:SOFR"), path_)) == (n_paths,) + @test size(at(DiffFusion.ZeroBond(1.0, 2.0, "USD:LIB3M"), path_)) == (n_paths,) + # + @test size(at(DiffFusion.ZeroBond(1.0, 2.0, "EUR"), path_)) == (n_paths,) + @test size(at(DiffFusion.ZeroBond(1.0, 2.0, "EUR:XCCY"), path_)) == (n_paths,) + @test size(at(DiffFusion.ZeroBond(1.0, 2.0, "EUR:ESTR"), path_)) == (n_paths,) + # + @test size(at(DiffFusion.ZeroBond(1.0, 2.0, "GBP"), path_)) == (n_paths,) + @test size(at(DiffFusion.ZeroBond(1.0, 2.0, "GBP:XCCY"), path_)) == (n_paths,) + @test size(at(DiffFusion.ZeroBond(1.0, 2.0, "GBP:SONIA"), path_)) == (n_paths,) + # + @test size(at(DiffFusion.Asset(1.0, "EUR-USD"), path_)) == (n_paths,) + @test size(at(DiffFusion.Asset(1.0, "GBP-USD"), path_)) == (n_paths,) + end + + + @testset "Test empty example edge cases." begin + empty_example = OrderedDict{String, Any}() + @test_throws ErrorException DiffFusion.Examples.model(empty_example) + end + + @testset "Example models." begin + test_example_simulation(DiffFusion.Examples.examples[1]) + end + + + @testset "Missing/default example keys." begin + serialised_example = DiffFusion.Examples.load(DiffFusion.Examples.examples[1]) + # + example = DiffFusion.Examples.build(serialised_example) + example["config/simulation"]["n_paths"] = 2^3 + example["config/simulation"]["with_progress_bar"] = false # we do not want outputs here + delete!(example["config/simulation"],"with_progress_bar") + delete!(example["config/simulation"],"seed") + delete!(example["config/instruments"],"path_interpolation") + path1 = DiffFusion.Examples.path!(example) + path2 = DiffFusion.Examples.path!(example) # use cached path + @test path1 == path2 + # + delete!(example["config/simulation"],"with_progress_bar") + scens1 = DiffFusion.Examples.scenarios!(example) + scens2 = DiffFusion.Examples.scenarios!(example) # use cached scenarios + @test scens1 == scens2 + end + + +end \ No newline at end of file diff --git a/test/unittests/test_tolerances.jl b/test/unittests/test_tolerances.jl index fd648b96..55c97050 100644 --- a/test/unittests/test_tolerances.jl +++ b/test/unittests/test_tolerances.jl @@ -14,5 +14,6 @@ else "simulations/asset_model.jl" => if (VERSION < v"1.7") 0.06 else 0.08 end, "simulations/gaussian_hjm_model.jl" => if (VERSION < v"1.7") 0.05 else 0.05 end, "simulations/simple_models.jl" => if (VERSION < v"1.7") 0.05 else 0.05 end, + "examples/examples.jl" => 1.0e-8, ) end