quantark 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- quantark/__init__.py +3 -0
- quantark/_compat.py +150 -0
- quantark/asset/__init__.py +8 -0
- quantark/asset/bond/__init__.py +2 -0
- quantark/asset/bond/engine/__init__.py +44 -0
- quantark/asset/bond/engine/analytical/__init__.py +12 -0
- quantark/asset/bond/engine/analytical/black_engine.py +583 -0
- quantark/asset/bond/engine/analytical/bond_forward_engine.py +390 -0
- quantark/asset/bond/engine/analytical/bond_futures_engine.py +569 -0
- quantark/asset/bond/engine/convertible/__init__.py +12 -0
- quantark/asset/bond/engine/convertible/convertible_bond_engine.py +800 -0
- quantark/asset/bond/engine/discount/__init__.py +10 -0
- quantark/asset/bond/engine/discount/bond_discount_engine.py +517 -0
- quantark/asset/bond/engine/discount/frn_engine.py +913 -0
- quantark/asset/bond/engine/pde/__init__.py +14 -0
- quantark/asset/bond/engine/pde/convertible/__init__.py +21 -0
- quantark/asset/bond/engine/pde/convertible/jump_diffusion_engine.py +603 -0
- quantark/asset/bond/engine/pde/convertible/pde_params.py +59 -0
- quantark/asset/bond/engine/pde/convertible/tf_engine.py +546 -0
- quantark/asset/bond/engine/tree/__init__.py +14 -0
- quantark/asset/bond/engine/tree/convertible/__init__.py +21 -0
- quantark/asset/bond/engine/tree/convertible/binomial_engine.py +488 -0
- quantark/asset/bond/engine/tree/convertible/tree_params.py +72 -0
- quantark/asset/bond/engine/tree/convertible/trinomial_engine.py +1341 -0
- quantark/asset/bond/product/__init__.py +37 -0
- quantark/asset/bond/product/base_bond_product.py +114 -0
- quantark/asset/bond/product/convertible/__init__.py +16 -0
- quantark/asset/bond/product/convertible/convertible_bond.py +595 -0
- quantark/asset/bond/product/couponbond/__init__.py +12 -0
- quantark/asset/bond/product/couponbond/fixed_bond.py +285 -0
- quantark/asset/bond/product/couponbond/frn.py +538 -0
- quantark/asset/bond/product/forward/__init__.py +9 -0
- quantark/asset/bond/product/forward/base_bond_forward.py +92 -0
- quantark/asset/bond/product/forward/bond_forward.py +335 -0
- quantark/asset/bond/product/futures/__init__.py +8 -0
- quantark/asset/bond/product/futures/bond_futures.py +532 -0
- quantark/asset/bond/product/option/__init__.py +9 -0
- quantark/asset/bond/product/option/euro_short_term_bond_option.py +231 -0
- quantark/asset/bond/riskmeasures/__init__.py +13 -0
- quantark/asset/bond/riskmeasures/bond_greeks_calculator.py +484 -0
- quantark/asset/bond/schedule/__init__.py +21 -0
- quantark/asset/bond/schedule/cashflow.py +595 -0
- quantark/asset/equity/__init__.py +11 -0
- quantark/asset/equity/analysis/__init__.py +4 -0
- quantark/asset/equity/analysis/autocallable_path_analyzer.py +257 -0
- quantark/asset/equity/engine/__init__.py +84 -0
- quantark/asset/equity/engine/analytical/__init__.py +37 -0
- quantark/asset/equity/engine/analytical/american_option_engine.py +682 -0
- quantark/asset/equity/engine/analytical/asian_option_analytical_engine.py +1102 -0
- quantark/asset/equity/engine/analytical/barrier_analytical_engine.py +455 -0
- quantark/asset/equity/engine/analytical/black_scholes_engine.py +322 -0
- quantark/asset/equity/engine/analytical/deltaone_engine.py +340 -0
- quantark/asset/equity/engine/analytical/digital_option_engine.py +168 -0
- quantark/asset/equity/engine/analytical/double_barrier_option_engine.py +481 -0
- quantark/asset/equity/engine/analytical/double_sharkfin_option_analytical_engine.py +508 -0
- quantark/asset/equity/engine/analytical/one_touch_analytical_engine.py +302 -0
- quantark/asset/equity/engine/analytical/range_accrual_analytical_engine.py +396 -0
- quantark/asset/equity/engine/analytical/single_sharkfin_option_analytical_engine.py +229 -0
- quantark/asset/equity/engine/base_engine.py +137 -0
- quantark/asset/equity/engine/event_stats.py +85 -0
- quantark/asset/equity/engine/mc/__init__.py +31 -0
- quantark/asset/equity/engine/mc/american_option_mc_engine.py +485 -0
- quantark/asset/equity/engine/mc/asian_option_mc_engine.py +678 -0
- quantark/asset/equity/engine/mc/barrier_option_mc_engine.py +726 -0
- quantark/asset/equity/engine/mc/digital_option_mc_engine.py +419 -0
- quantark/asset/equity/engine/mc/double_sharkfin_option_mc_engine.py +676 -0
- quantark/asset/equity/engine/mc/euro_mc_engine.py +423 -0
- quantark/asset/equity/engine/mc/phoenix_mc_engine.py +1206 -0
- quantark/asset/equity/engine/mc/range_accrual_mc_engine.py +738 -0
- quantark/asset/equity/engine/mc/single_sharkfin_option_mc_engine.py +549 -0
- quantark/asset/equity/engine/mc/snowball_mc_engine.py +2250 -0
- quantark/asset/equity/engine/pde/__init__.py +36 -0
- quantark/asset/equity/engine/pde/american_pde_solver.py +211 -0
- quantark/asset/equity/engine/pde/barrier_pde_solver.py +692 -0
- quantark/asset/equity/engine/pde/base_pde_solver.py +994 -0
- quantark/asset/equity/engine/pde/double_barrier_pde_solver.py +510 -0
- quantark/asset/equity/engine/pde/double_one_touch_pde_solver.py +435 -0
- quantark/asset/equity/engine/pde/european_pde_solver.py +170 -0
- quantark/asset/equity/engine/pde/ko_reset_snowball_pde_solver.py +477 -0
- quantark/asset/equity/engine/pde/one_touch_pde_solver.py +439 -0
- quantark/asset/equity/engine/pde/phoenix_pde_solver.py +613 -0
- quantark/asset/equity/engine/pde/snowball_pde_solver.py +1810 -0
- quantark/asset/equity/engine/pde/spatial_grid.py +750 -0
- quantark/asset/equity/engine/pde/time_grid.py +308 -0
- quantark/asset/equity/engine/pde_engine.py +238 -0
- quantark/asset/equity/engine/quad/__init__.py +23 -0
- quantark/asset/equity/engine/quad/discrete_quad_engine.py +106 -0
- quantark/asset/equity/engine/quad/european_quad_engine.py +325 -0
- quantark/asset/equity/engine/quad/ko_reset_snowball_quad_engine.py +362 -0
- quantark/asset/equity/engine/quad/phoenix_quad_engine.py +614 -0
- quantark/asset/equity/engine/quad/quad_adapters.py +1260 -0
- quantark/asset/equity/engine/quad/quad_core.py +513 -0
- quantark/asset/equity/engine/quad/quad_math.py +219 -0
- quantark/asset/equity/engine/quad/snowball_quad_engine.py +1137 -0
- quantark/asset/equity/engine/validation/script/benchmark_check_american_analytical.py +117 -0
- quantark/asset/equity/engine/validation/script/benchmark_check_american_pde.py +114 -0
- quantark/asset/equity/engine/validation/script/benchmark_check_asian_analytical.py +440 -0
- quantark/asset/equity/engine/validation/script/benchmark_check_barrier_analytical.py +269 -0
- quantark/asset/equity/engine/validation/script/benchmark_check_barrier_pde_solver.py +636 -0
- quantark/asset/equity/engine/validation/script/benchmark_check_digital_option.py +256 -0
- quantark/asset/equity/engine/validation/script/benchmark_check_snowball_pde_solver.py +807 -0
- quantark/asset/equity/engine/validation/script/boundary_check_american_analytical.py +290 -0
- quantark/asset/equity/engine/validation/script/boundary_check_american_pde.py +242 -0
- quantark/asset/equity/engine/validation/script/boundary_check_asian_analytical.py +612 -0
- quantark/asset/equity/engine/validation/script/boundary_check_barrier_analytical.py +434 -0
- quantark/asset/equity/engine/validation/script/boundary_check_barrier_pde_solver.py +748 -0
- quantark/asset/equity/engine/validation/script/boundary_check_digital_option.py +575 -0
- quantark/asset/equity/engine/validation/script/boundary_check_snowball_pde_solver.py +1101 -0
- quantark/asset/equity/engine/validation/script/greeks_check_digital_option.py +349 -0
- quantark/asset/equity/engine/validation/script/mc_comparison_barrier_pde.py +270 -0
- quantark/asset/equity/engine/validation/script/quick_mc_compare.py +51 -0
- quantark/asset/equity/engine/validation/script/validation_stepdown_improved.py +97 -0
- quantark/asset/equity/param/__init__.py +24 -0
- quantark/asset/equity/param/engine_param_profiles.py +325 -0
- quantark/asset/equity/param/engine_params.py +728 -0
- quantark/asset/equity/process/__init__.py +7 -0
- quantark/asset/equity/process/bsm/__init__.py +7 -0
- quantark/asset/equity/process/bsm/bsm_process.py +108 -0
- quantark/asset/equity/process/bsm/qmc_brownian_bridge.py +401 -0
- quantark/asset/equity/process/bsm/qmc_path_generator.py +694 -0
- quantark/asset/equity/process/bsm/qmc_rqmc_driver.py +163 -0
- quantark/asset/equity/process/bsm/qmc_sobol.py +195 -0
- quantark/asset/equity/process/bsm/qmc_variance_reduction.py +292 -0
- quantark/asset/equity/product/__init__.py +8 -0
- quantark/asset/equity/product/base_equity_product.py +72 -0
- quantark/asset/equity/product/deltaone/__init__.py +22 -0
- quantark/asset/equity/product/deltaone/base_deltaone_product.py +147 -0
- quantark/asset/equity/product/deltaone/futures.py +485 -0
- quantark/asset/equity/product/deltaone/spot_instrument.py +118 -0
- quantark/asset/equity/product/option/__init__.py +104 -0
- quantark/asset/equity/product/option/american_option.py +114 -0
- quantark/asset/equity/product/option/asian_option.py +531 -0
- quantark/asset/equity/product/option/barrier_option.py +289 -0
- quantark/asset/equity/product/option/base_equity_option.py +659 -0
- quantark/asset/equity/product/option/digital_option.py +102 -0
- quantark/asset/equity/product/option/double_barrier_option.py +286 -0
- quantark/asset/equity/product/option/double_one_touch_option.py +310 -0
- quantark/asset/equity/product/option/double_sharkfin_option.py +466 -0
- quantark/asset/equity/product/option/european_vanilla_option.py +103 -0
- quantark/asset/equity/product/option/ko_reset_snowball_option.py +563 -0
- quantark/asset/equity/product/option/observation_schedule.py +530 -0
- quantark/asset/equity/product/option/one_touch_option.py +287 -0
- quantark/asset/equity/product/option/phoenix_config.py +116 -0
- quantark/asset/equity/product/option/phoenix_helpers.py +576 -0
- quantark/asset/equity/product/option/phoenix_option.py +1167 -0
- quantark/asset/equity/product/option/range_accrual_config.py +288 -0
- quantark/asset/equity/product/option/range_accrual_helpers.py +608 -0
- quantark/asset/equity/product/option/range_accrual_option.py +526 -0
- quantark/asset/equity/product/option/single_sharkfin_option.py +420 -0
- quantark/asset/equity/product/option/snowball_config.py +261 -0
- quantark/asset/equity/product/option/snowball_helpers.py +977 -0
- quantark/asset/equity/product/option/snowball_option.py +1242 -0
- quantark/asset/equity/report/__init__.py +15 -0
- quantark/asset/equity/report/autocallable_risk_report.py +2118 -0
- quantark/asset/equity/report/plotting.py +87 -0
- quantark/asset/equity/report/snowball_risk_comparison_report.py +2230 -0
- quantark/asset/equity/report/surfaces.py +123 -0
- quantark/asset/equity/report/term_structure.py +126 -0
- quantark/asset/equity/riskmeasures/__init__.py +7 -0
- quantark/asset/equity/riskmeasures/greeks_calculator.py +1204 -0
- quantark/asset/rate/__init__.py +58 -0
- quantark/asset/rate/engine/__init__.py +25 -0
- quantark/asset/rate/engine/cap_floor_engine.py +514 -0
- quantark/asset/rate/engine/fra_engine.py +286 -0
- quantark/asset/rate/engine/irs_discount_engine.py +891 -0
- quantark/asset/rate/engine/swaption_engine.py +587 -0
- quantark/asset/rate/product/__init__.py +67 -0
- quantark/asset/rate/product/cap_floor.py +550 -0
- quantark/asset/rate/product/fra.py +219 -0
- quantark/asset/rate/product/irs.py +1223 -0
- quantark/asset/rate/product/swaption.py +372 -0
- quantark/backtest/__init__.py +153 -0
- quantark/backtest/base.py +263 -0
- quantark/backtest/dashboard.py +874 -0
- quantark/backtest/equity/__init__.py +35 -0
- quantark/backtest/equity/config.py +118 -0
- quantark/backtest/equity/engine.py +408 -0
- quantark/backtest/equity/hedge_executor.py +374 -0
- quantark/backtest/equity/metrics.py +396 -0
- quantark/backtest/equity/results.py +232 -0
- quantark/backtest/equity/state.py +252 -0
- quantark/backtest/examples/__init__.py +4 -0
- quantark/backtest/examples/advanced_backtest.py +345 -0
- quantark/backtest/examples/basic_delta_hedge.py +246 -0
- quantark/backtest/examples/fi_dv01_hedge.py +267 -0
- quantark/backtest/fi/__init__.py +30 -0
- quantark/backtest/fi/config.py +114 -0
- quantark/backtest/fi/engine.py +378 -0
- quantark/backtest/fi/hedge_executor.py +254 -0
- quantark/backtest/fi/metrics.py +308 -0
- quantark/backtest/fi/results.py +193 -0
- quantark/backtest/fi/state.py +212 -0
- quantark/backtest/logger.py +393 -0
- quantark/backtest/otc/__init__.py +74 -0
- quantark/backtest/otc/_replay.py +637 -0
- quantark/backtest/otc/book_engine.py +587 -0
- quantark/backtest/otc/config.py +175 -0
- quantark/backtest/otc/dashboard.py +1006 -0
- quantark/backtest/otc/engine.py +420 -0
- quantark/backtest/otc/engine_factory.py +138 -0
- quantark/backtest/otc/market.py +216 -0
- quantark/backtest/otc/results.py +107 -0
- quantark/backtest/otc/state.py +166 -0
- quantark/backtest/report_generator.py +608 -0
- quantark/backtest/strategy/__init__.py +28 -0
- quantark/backtest/strategy/base_strategy.py +235 -0
- quantark/backtest/strategy/convexity_neutral_strategy.py +247 -0
- quantark/backtest/strategy/delta_neutral_strategy.py +283 -0
- quantark/backtest/strategy/dv01_neutral_strategy.py +283 -0
- quantark/backtest/transaction_costs.py +485 -0
- quantark/backtest/visualizer.py +1019 -0
- quantark/cashleg/__init__.py +31 -0
- quantark/cashleg/accrual_leg.py +120 -0
- quantark/cashleg/base.py +48 -0
- quantark/cashleg/base_amount.py +60 -0
- quantark/cashleg/deterministic_leg.py +39 -0
- quantark/cashleg/event_distribution.py +262 -0
- quantark/cashleg/fixed_payoff_leg.py +92 -0
- quantark/cashleg/leg_schedule.py +95 -0
- quantark/cashleg/leg_valuator.py +40 -0
- quantark/dynamicscenario/__init__.py +97 -0
- quantark/dynamicscenario/base.py +297 -0
- quantark/dynamicscenario/config.py +122 -0
- quantark/dynamicscenario/engine.py +703 -0
- quantark/dynamicscenario/equity/__init__.py +14 -0
- quantark/dynamicscenario/fi/__init__.py +24 -0
- quantark/dynamicscenario/fi/config.py +149 -0
- quantark/dynamicscenario/fi/engine.py +500 -0
- quantark/dynamicscenario/fi/results.py +503 -0
- quantark/dynamicscenario/path/__init__.py +17 -0
- quantark/dynamicscenario/path/day_path.py +397 -0
- quantark/dynamicscenario/path/fi_path_library.py +488 -0
- quantark/dynamicscenario/path/path_builder.py +726 -0
- quantark/dynamicscenario/path/path_library.py +620 -0
- quantark/dynamicscenario/report/__init__.py +12 -0
- quantark/dynamicscenario/report/dynamic_report.py +1175 -0
- quantark/dynamicscenario/report/visualizer.py +1586 -0
- quantark/dynamicscenario/results/__init__.py +19 -0
- quantark/dynamicscenario/results/dynamic_results.py +579 -0
- quantark/dynamicscenario/results/result_exporter.py +438 -0
- quantark/param/__init__.py +75 -0
- quantark/param/basis/__init__.py +19 -0
- quantark/param/basis/basis_yield.py +301 -0
- quantark/param/div/__init__.py +16 -0
- quantark/param/div/dividend_yield.py +123 -0
- quantark/param/index/__init__.py +52 -0
- quantark/param/index/rate_index.py +568 -0
- quantark/param/quote/__init__.py +7 -0
- quantark/param/quote/spot_quote.py +35 -0
- quantark/param/rrf/__init__.py +22 -0
- quantark/param/rrf/rate_curve.py +436 -0
- quantark/param/vol/__init__.py +6 -0
- quantark/param/vol/vol_surface.py +118 -0
- quantark/portfolio/__init__.py +61 -0
- quantark/portfolio/base.py +203 -0
- quantark/portfolio/equity/__init__.py +17 -0
- quantark/portfolio/equity/portfolio.py +391 -0
- quantark/portfolio/equity/position.py +368 -0
- quantark/portfolio/fi/__init__.py +14 -0
- quantark/portfolio/fi/portfolio.py +424 -0
- quantark/portfolio/fi/position.py +272 -0
- quantark/portfolio/portfolio_snapshot.py +221 -0
- quantark/portfolio/portfolio_storage.py +414 -0
- quantark/priceenv/__init__.py +7 -0
- quantark/priceenv/pricing_environment.py +196 -0
- quantark/rfq/__init__.py +32 -0
- quantark/rfq/builders.py +102 -0
- quantark/rfq/models.py +214 -0
- quantark/rfq/registry.py +611 -0
- quantark/rfq/service.py +237 -0
- quantark/simm/__init__.py +155 -0
- quantark/simm/calibration/__init__.py +206 -0
- quantark/simm/calibration/accessors.py +439 -0
- quantark/simm/calibration/commodity.py +156 -0
- quantark/simm/calibration/credit_non_qualifying.py +79 -0
- quantark/simm/calibration/credit_qualifying.py +130 -0
- quantark/simm/calibration/cross_risk.py +39 -0
- quantark/simm/calibration/equity.py +125 -0
- quantark/simm/calibration/fx.py +92 -0
- quantark/simm/calibration/ir.py +152 -0
- quantark/simm/calibration/version.py +33 -0
- quantark/simm/config.py +186 -0
- quantark/simm/crif/__init__.py +35 -0
- quantark/simm/crif/models.py +230 -0
- quantark/simm/crif/parser.py +585 -0
- quantark/simm/engines/__init__.py +62 -0
- quantark/simm/engines/aggregation/__init__.py +67 -0
- quantark/simm/engines/aggregation/addon.py +141 -0
- quantark/simm/engines/aggregation/bucket_aggregator.py +298 -0
- quantark/simm/engines/aggregation/concentration.py +349 -0
- quantark/simm/engines/aggregation/product_class_aggregator.py +183 -0
- quantark/simm/engines/aggregation/risk_class_aggregator.py +403 -0
- quantark/simm/engines/aggregation/simm_calculator.py +430 -0
- quantark/simm/engines/aggregation/weighted_sensitivity.py +272 -0
- quantark/simm/engines/base.py +231 -0
- quantark/simm/engines/classification/__init__.py +10 -0
- quantark/simm/engines/classification/bucket_mapper.py +347 -0
- quantark/simm/engines/factory.py +137 -0
- quantark/simm/engines/portfolio_adapter.py +336 -0
- quantark/simm/engines/result.py +176 -0
- quantark/simm/engines/risk_class/__init__.py +18 -0
- quantark/simm/engines/risk_class/equity_engine.py +263 -0
- quantark/simm/engines/risk_class/ir_engine.py +264 -0
- quantark/simm/report/__init__.py +17 -0
- quantark/simm/report/crif_export.py +284 -0
- quantark/simm/report/excel_generator.py +401 -0
- quantark/simm/report/html_generator.py +840 -0
- quantark/simm/results/__init__.py +38 -0
- quantark/simm/results/attribution.py +313 -0
- quantark/simm/results/simm_result.py +339 -0
- quantark/simm/results/whatif.py +268 -0
- quantark/simm/sensitivity.py +533 -0
- quantark/simm/taxonomy.py +416 -0
- quantark/stresstest/__init__.py +67 -0
- quantark/stresstest/base.py +116 -0
- quantark/stresstest/config.py +5 -0
- quantark/stresstest/engine.py +5 -0
- quantark/stresstest/equity/__init__.py +17 -0
- quantark/stresstest/equity/config.py +69 -0
- quantark/stresstest/equity/engine.py +272 -0
- quantark/stresstest/equity/report/__init__.py +7 -0
- quantark/stresstest/equity/report/report_generator.py +423 -0
- quantark/stresstest/equity/report/visualizer.py +328 -0
- quantark/stresstest/equity/results.py +145 -0
- quantark/stresstest/fi/__init__.py +15 -0
- quantark/stresstest/fi/config.py +59 -0
- quantark/stresstest/fi/engine.py +213 -0
- quantark/stresstest/fi/metrics.py +60 -0
- quantark/stresstest/fi/results.py +64 -0
- quantark/stresstest/report/__init__.py +12 -0
- quantark/stresstest/report/report_generator.py +5 -0
- quantark/stresstest/report/visualizer.py +5 -0
- quantark/stresstest/results/__init__.py +16 -0
- quantark/stresstest/results/result_aggregator.py +325 -0
- quantark/stresstest/results/result_exporter.py +286 -0
- quantark/stresstest/results/stress_results.py +5 -0
- quantark/stresstest/scenario/__init__.py +13 -0
- quantark/stresstest/scenario/scenario.py +242 -0
- quantark/stresstest/scenario/scenario_builder.py +376 -0
- quantark/stresstest/scenario/scenario_library.py +435 -0
- quantark/stresstest/scenario/scenario_storage.py +224 -0
- quantark/stresstest/stress/__init__.py +13 -0
- quantark/stresstest/stress/stress_applicator.py +590 -0
- quantark/stresstest/stress/stress_types.py +142 -0
- quantark/util/__init__.py +23 -0
- quantark/util/barrier_shift.py +44 -0
- quantark/util/calendar/__init__.py +27 -0
- quantark/util/calendar/business_calendar.py +584 -0
- quantark/util/calendar/day_counter.py +517 -0
- quantark/util/calendar/holidayfile/china.csv +1920 -0
- quantark/util/calendar/holidayfile/china_sse.csv +1462 -0
- quantark/util/enum/__init__.py +81 -0
- quantark/util/enum/bond_enums.py +112 -0
- quantark/util/enum/deltaone_enums.py +16 -0
- quantark/util/enum/engine_enums.py +137 -0
- quantark/util/enum/greeks_enums.py +29 -0
- quantark/util/enum/option_enums.py +221 -0
- quantark/util/exceptions.py +66 -0
- quantark/util/marketdata/__init__.py +39 -0
- quantark/util/marketdata/adapter/base_adapter.py +203 -0
- quantark/util/marketdata/adapter/mock_adapter.py +265 -0
- quantark/util/marketdata/converter.py +289 -0
- quantark/util/marketdata/example_usage.py +314 -0
- quantark/util/marketdata/generator/__init__.py +7 -0
- quantark/util/marketdata/generator/mock_generator.py +466 -0
- quantark/util/marketdata/models.py +358 -0
- quantark/util/marketdata/storage/__init__.py +7 -0
- quantark/util/marketdata/storage/parquet_storage.py +340 -0
- quantark/util/numerical/__init__.py +98 -0
- quantark/util/numerical/comparison.py +219 -0
- quantark/util/numerical/constants.py +98 -0
- quantark/util/numerical/formatting.py +380 -0
- quantark/util/numerical/pnl.py +17 -0
- quantark/util/numerical/safe_math.py +238 -0
- quantark/util/numerical/validation.py +315 -0
- quantark/var/__init__.py +39 -0
- quantark/var/attribution.py +398 -0
- quantark/var/backtest/__init__.py +7 -0
- quantark/var/backtest/var_backtester.py +309 -0
- quantark/var/base.py +63 -0
- quantark/var/config.py +219 -0
- quantark/var/engines/__init__.py +13 -0
- quantark/var/engines/historical.py +925 -0
- quantark/var/engines/monte_carlo.py +870 -0
- quantark/var/engines/parametric.py +1199 -0
- quantark/var/results/__init__.py +16 -0
- quantark/var/results/incremental_var_result.py +131 -0
- quantark/var/results/var_report.py +346 -0
- quantark/var/results/var_result.py +134 -0
- quantark/var/risk_factors/__init__.py +22 -0
- quantark/var/risk_factors/base.py +41 -0
- quantark/var/risk_factors/equity_factors.py +158 -0
- quantark/var/risk_factors/fi_factors.py +99 -0
- quantark-0.1.0.dist-info/METADATA +351 -0
- quantark-0.1.0.dist-info/RECORD +399 -0
- quantark-0.1.0.dist-info/WHEEL +4 -0
- quantark-0.1.0.dist-info/licenses/LICENSE +202 -0
- quantark-0.1.0.dist-info/licenses/NOTICE +2 -0
- quantark_compat.pth +1 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Interest Rate derivatives module.
|
|
3
|
+
|
|
4
|
+
This module provides:
|
|
5
|
+
- Interest Rate Swap (IRS) products
|
|
6
|
+
- Basis Swaps
|
|
7
|
+
- Forward Rate Agreements (FRA)
|
|
8
|
+
- Interest Rate Caps, Floors, and Collars
|
|
9
|
+
- Swaptions (options on swaps)
|
|
10
|
+
- Pricing engines for rate products
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from .product import (
|
|
14
|
+
InterestRateSwap,
|
|
15
|
+
BasisSwap,
|
|
16
|
+
FixedLeg,
|
|
17
|
+
FloatingLeg,
|
|
18
|
+
ForwardRateAgreement,
|
|
19
|
+
CapFloor,
|
|
20
|
+
CapFloorType,
|
|
21
|
+
Caplet,
|
|
22
|
+
Collar,
|
|
23
|
+
Swaption,
|
|
24
|
+
SwaptionType,
|
|
25
|
+
SwaptionExerciseStyle,
|
|
26
|
+
)
|
|
27
|
+
from .engine import (
|
|
28
|
+
IRSDiscountEngine,
|
|
29
|
+
FRAEngine,
|
|
30
|
+
CapFloorEngine,
|
|
31
|
+
SwaptionEngine,
|
|
32
|
+
SwaptionModelType,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
__all__ = [
|
|
36
|
+
# IRS
|
|
37
|
+
'InterestRateSwap',
|
|
38
|
+
'BasisSwap',
|
|
39
|
+
'FixedLeg',
|
|
40
|
+
'FloatingLeg',
|
|
41
|
+
# FRA
|
|
42
|
+
'ForwardRateAgreement',
|
|
43
|
+
# Cap/Floor
|
|
44
|
+
'CapFloor',
|
|
45
|
+
'CapFloorType',
|
|
46
|
+
'Caplet',
|
|
47
|
+
'Collar',
|
|
48
|
+
# Swaption
|
|
49
|
+
'Swaption',
|
|
50
|
+
'SwaptionType',
|
|
51
|
+
'SwaptionExerciseStyle',
|
|
52
|
+
# Engines
|
|
53
|
+
'IRSDiscountEngine',
|
|
54
|
+
'FRAEngine',
|
|
55
|
+
'CapFloorEngine',
|
|
56
|
+
'SwaptionEngine',
|
|
57
|
+
'SwaptionModelType',
|
|
58
|
+
]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Interest Rate derivative pricing engines.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .irs_discount_engine import IRSDiscountEngine, IRSPricingResults
|
|
6
|
+
from .fra_engine import FRAEngine, FRAPricingResults
|
|
7
|
+
from .cap_floor_engine import CapFloorEngine, CapFloorPricingResults, CapletPricingResult
|
|
8
|
+
from .swaption_engine import SwaptionEngine, SwaptionPricingResults, SwaptionModelType
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
# IRS
|
|
12
|
+
'IRSDiscountEngine',
|
|
13
|
+
'IRSPricingResults',
|
|
14
|
+
# FRA
|
|
15
|
+
'FRAEngine',
|
|
16
|
+
'FRAPricingResults',
|
|
17
|
+
# Cap/Floor
|
|
18
|
+
'CapFloorEngine',
|
|
19
|
+
'CapFloorPricingResults',
|
|
20
|
+
'CapletPricingResult',
|
|
21
|
+
# Swaption
|
|
22
|
+
'SwaptionEngine',
|
|
23
|
+
'SwaptionPricingResults',
|
|
24
|
+
'SwaptionModelType',
|
|
25
|
+
]
|
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Analytical pricing engine for Interest Rate Caps, Floors, and Collars.
|
|
3
|
+
|
|
4
|
+
Uses Black's model (Black-76) to price each caplet/floorlet individually,
|
|
5
|
+
then aggregates to obtain the full cap/floor price.
|
|
6
|
+
|
|
7
|
+
Black's caplet formula:
|
|
8
|
+
Caplet = df * dcf * N * [F * N(d1) - K * N(d2)]
|
|
9
|
+
Floorlet = df * dcf * N * [K * N(-d2) - F * N(-d1)]
|
|
10
|
+
|
|
11
|
+
where:
|
|
12
|
+
d1 = [ln(F/K) + 0.5 * sigma^2 * T_fix] / (sigma * sqrt(T_fix))
|
|
13
|
+
d2 = d1 - sigma * sqrt(T_fix)
|
|
14
|
+
F = forward rate for the caplet period
|
|
15
|
+
K = strike rate
|
|
16
|
+
sigma = implied volatility (flat or from vol surface)
|
|
17
|
+
T_fix = time to fixing date (option expiry for this caplet)
|
|
18
|
+
df = discount factor to payment date
|
|
19
|
+
dcf = day count fraction for the accrual period
|
|
20
|
+
N = notional
|
|
21
|
+
N(.) = standard normal CDF
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from dataclasses import dataclass, field
|
|
25
|
+
from datetime import datetime
|
|
26
|
+
from typing import List, Optional
|
|
27
|
+
|
|
28
|
+
from scipy.stats import norm
|
|
29
|
+
|
|
30
|
+
from quantark.asset.rate.product.cap_floor import CapFloor, CapFloorType, Caplet, Collar
|
|
31
|
+
from quantark.priceenv import PricingEnvironment
|
|
32
|
+
from quantark.param.rrf import RateCurve, FlatRateCurve
|
|
33
|
+
from quantark.util.exceptions import ValidationError, MarketDataError
|
|
34
|
+
from quantark.util.numerical import safe_log, safe_sqrt, safe_divide, is_zero
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass
|
|
38
|
+
class CapletPricingResult:
|
|
39
|
+
"""
|
|
40
|
+
Pricing result for a single caplet/floorlet.
|
|
41
|
+
|
|
42
|
+
Attributes:
|
|
43
|
+
price: Present value of the caplet/floorlet
|
|
44
|
+
forward_rate: Forward rate for this period
|
|
45
|
+
vol: Implied volatility used
|
|
46
|
+
d1: Black's d1
|
|
47
|
+
d2: Black's d2
|
|
48
|
+
intrinsic: Intrinsic value (max(0, F-K)*dcf*N*df for cap)
|
|
49
|
+
time_value: Time value (price - intrinsic)
|
|
50
|
+
delta: Rate delta (sensitivity to forward rate)
|
|
51
|
+
vega: Sensitivity to vol (per 1% vol move)
|
|
52
|
+
accrual_start: Start of accrual period
|
|
53
|
+
accrual_end: End of accrual period
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
price: float
|
|
57
|
+
forward_rate: float
|
|
58
|
+
vol: float
|
|
59
|
+
d1: float
|
|
60
|
+
d2: float
|
|
61
|
+
intrinsic: float
|
|
62
|
+
time_value: float
|
|
63
|
+
delta: float
|
|
64
|
+
vega: float
|
|
65
|
+
accrual_start: datetime
|
|
66
|
+
accrual_end: datetime
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass
|
|
70
|
+
class CapFloorPricingResults:
|
|
71
|
+
"""
|
|
72
|
+
Results container for Cap/Floor pricing.
|
|
73
|
+
|
|
74
|
+
Attributes:
|
|
75
|
+
npv: Total present value of the cap/floor
|
|
76
|
+
caplet_prices: Individual caplet/floorlet prices
|
|
77
|
+
caplet_details: Detailed results per caplet
|
|
78
|
+
par_rate: Flat vol implied par strike (forward swap rate)
|
|
79
|
+
flat_vol: Flat vol that reprices the cap/floor (if provided)
|
|
80
|
+
dv01: Dollar value of 1 basis point
|
|
81
|
+
vega: Total vega (sensitivity to 1% vol shift)
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
npv: float
|
|
85
|
+
caplet_prices: List[float] = field(default_factory=list)
|
|
86
|
+
caplet_details: List[CapletPricingResult] = field(default_factory=list)
|
|
87
|
+
par_rate: Optional[float] = None
|
|
88
|
+
flat_vol: Optional[float] = None
|
|
89
|
+
dv01: Optional[float] = None
|
|
90
|
+
vega: Optional[float] = None
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class CapFloorEngine:
|
|
94
|
+
"""
|
|
95
|
+
Analytical pricing engine for Caps, Floors, and Collars.
|
|
96
|
+
|
|
97
|
+
Uses Black's model (Black-76) to price each caplet/floorlet. Supports:
|
|
98
|
+
- Flat volatility (same vol for all caplets)
|
|
99
|
+
- Per-caplet volatility from vol surface
|
|
100
|
+
- Single-curve or dual-curve pricing
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
def __init__(
|
|
104
|
+
self,
|
|
105
|
+
pricing_env: PricingEnvironment,
|
|
106
|
+
projection_curve: Optional[RateCurve] = None,
|
|
107
|
+
vol: Optional[float] = None,
|
|
108
|
+
):
|
|
109
|
+
"""
|
|
110
|
+
Initialize the Cap/Floor engine.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
pricing_env: Pricing environment with discount curve.
|
|
114
|
+
If vol_surface is set, it provides per-caplet vols.
|
|
115
|
+
projection_curve: Separate curve for forward rate projection.
|
|
116
|
+
If None, uses the discount curve.
|
|
117
|
+
vol: Flat volatility override. If provided, uses this vol for
|
|
118
|
+
all caplets instead of the vol surface.
|
|
119
|
+
"""
|
|
120
|
+
if pricing_env is None:
|
|
121
|
+
raise ValidationError("Pricing environment is required")
|
|
122
|
+
if pricing_env.rate_curve is None:
|
|
123
|
+
raise MarketDataError("Rate curve is required for cap/floor pricing")
|
|
124
|
+
|
|
125
|
+
self.pricing_env = pricing_env
|
|
126
|
+
self.projection_curve = projection_curve or pricing_env.rate_curve
|
|
127
|
+
self.flat_vol = vol
|
|
128
|
+
|
|
129
|
+
def price(
|
|
130
|
+
self,
|
|
131
|
+
product: CapFloor,
|
|
132
|
+
valuation_date: Optional[datetime] = None,
|
|
133
|
+
) -> float:
|
|
134
|
+
"""
|
|
135
|
+
Calculate the NPV of a Cap or Floor.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
product: Cap or Floor to price
|
|
139
|
+
valuation_date: Valuation date (default: pricing env date)
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
Net present value (positive for long position)
|
|
143
|
+
"""
|
|
144
|
+
if valuation_date is None:
|
|
145
|
+
valuation_date = self.pricing_env.valuation_date
|
|
146
|
+
|
|
147
|
+
if product.is_expired(valuation_date):
|
|
148
|
+
return 0.0
|
|
149
|
+
|
|
150
|
+
caplets = product.get_future_caplets(valuation_date)
|
|
151
|
+
total = 0.0
|
|
152
|
+
|
|
153
|
+
for caplet in caplets:
|
|
154
|
+
total += self._price_caplet(caplet, product.cap_floor_type, valuation_date)
|
|
155
|
+
|
|
156
|
+
return total
|
|
157
|
+
|
|
158
|
+
def price_collar(
|
|
159
|
+
self,
|
|
160
|
+
collar: Collar,
|
|
161
|
+
valuation_date: Optional[datetime] = None,
|
|
162
|
+
) -> float:
|
|
163
|
+
"""
|
|
164
|
+
Calculate the NPV of a Collar (long cap + short floor).
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
collar: Collar to price
|
|
168
|
+
valuation_date: Valuation date
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
NPV = cap_price - floor_price (borrower's hedge)
|
|
172
|
+
"""
|
|
173
|
+
cap_pv = self.price(collar.cap, valuation_date)
|
|
174
|
+
floor_pv = self.price(collar.floor, valuation_date)
|
|
175
|
+
return cap_pv - floor_pv
|
|
176
|
+
|
|
177
|
+
def dv01(
|
|
178
|
+
self,
|
|
179
|
+
product: CapFloor,
|
|
180
|
+
valuation_date: Optional[datetime] = None,
|
|
181
|
+
bump_size: float = 0.0001,
|
|
182
|
+
) -> float:
|
|
183
|
+
"""
|
|
184
|
+
Calculate DV01 via central difference.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
product: Cap or Floor
|
|
188
|
+
valuation_date: Valuation date
|
|
189
|
+
bump_size: Rate bump (default: 1bp)
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
DV01 value
|
|
193
|
+
"""
|
|
194
|
+
if valuation_date is None:
|
|
195
|
+
valuation_date = self.pricing_env.valuation_date
|
|
196
|
+
|
|
197
|
+
original_curve = self.pricing_env.rate_curve
|
|
198
|
+
original_projection = self.projection_curve
|
|
199
|
+
base_rate = original_curve.get_rate(1.0)
|
|
200
|
+
|
|
201
|
+
try:
|
|
202
|
+
up_curve = FlatRateCurve(rate=base_rate + bump_size)
|
|
203
|
+
self.pricing_env.rate_curve = up_curve
|
|
204
|
+
self.projection_curve = up_curve
|
|
205
|
+
npv_up = self.price(product, valuation_date)
|
|
206
|
+
|
|
207
|
+
down_curve = FlatRateCurve(rate=base_rate - bump_size)
|
|
208
|
+
self.pricing_env.rate_curve = down_curve
|
|
209
|
+
self.projection_curve = down_curve
|
|
210
|
+
npv_down = self.price(product, valuation_date)
|
|
211
|
+
finally:
|
|
212
|
+
self.pricing_env.rate_curve = original_curve
|
|
213
|
+
self.projection_curve = original_projection
|
|
214
|
+
|
|
215
|
+
return (npv_down - npv_up) / (2 * bump_size)
|
|
216
|
+
|
|
217
|
+
def vega(
|
|
218
|
+
self,
|
|
219
|
+
product: CapFloor,
|
|
220
|
+
valuation_date: Optional[datetime] = None,
|
|
221
|
+
vol_bump: float = 0.01,
|
|
222
|
+
) -> float:
|
|
223
|
+
"""
|
|
224
|
+
Calculate total vega (sensitivity to flat vol shift of 1%).
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
product: Cap or Floor
|
|
228
|
+
valuation_date: Valuation date
|
|
229
|
+
vol_bump: Vol bump size (default: 1% = 0.01)
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
Vega (dollar change per 1% vol increase)
|
|
233
|
+
"""
|
|
234
|
+
if valuation_date is None:
|
|
235
|
+
valuation_date = self.pricing_env.valuation_date
|
|
236
|
+
|
|
237
|
+
if self.flat_vol is None:
|
|
238
|
+
return 0.0
|
|
239
|
+
|
|
240
|
+
original_vol = self.flat_vol
|
|
241
|
+
|
|
242
|
+
try:
|
|
243
|
+
self.flat_vol = original_vol + vol_bump
|
|
244
|
+
npv_up = self.price(product, valuation_date)
|
|
245
|
+
|
|
246
|
+
self.flat_vol = original_vol - vol_bump
|
|
247
|
+
npv_down = self.price(product, valuation_date)
|
|
248
|
+
finally:
|
|
249
|
+
self.flat_vol = original_vol
|
|
250
|
+
|
|
251
|
+
return (npv_up - npv_down) / (2 * vol_bump) * 0.01
|
|
252
|
+
|
|
253
|
+
def full_analysis(
|
|
254
|
+
self,
|
|
255
|
+
product: CapFloor,
|
|
256
|
+
valuation_date: Optional[datetime] = None,
|
|
257
|
+
) -> CapFloorPricingResults:
|
|
258
|
+
"""
|
|
259
|
+
Perform full analysis of a Cap or Floor.
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
product: Cap or Floor
|
|
263
|
+
valuation_date: Valuation date
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
CapFloorPricingResults with all metrics
|
|
267
|
+
"""
|
|
268
|
+
if valuation_date is None:
|
|
269
|
+
valuation_date = self.pricing_env.valuation_date
|
|
270
|
+
|
|
271
|
+
caplets = product.get_future_caplets(valuation_date)
|
|
272
|
+
caplet_prices = []
|
|
273
|
+
caplet_details = []
|
|
274
|
+
total_npv = 0.0
|
|
275
|
+
|
|
276
|
+
for caplet in caplets:
|
|
277
|
+
detail = self._analyze_caplet(
|
|
278
|
+
caplet, product.cap_floor_type, valuation_date
|
|
279
|
+
)
|
|
280
|
+
caplet_details.append(detail)
|
|
281
|
+
caplet_prices.append(detail.price)
|
|
282
|
+
total_npv += detail.price
|
|
283
|
+
|
|
284
|
+
total_vega = sum(d.vega for d in caplet_details)
|
|
285
|
+
dv01_val = self.dv01(product, valuation_date)
|
|
286
|
+
|
|
287
|
+
return CapFloorPricingResults(
|
|
288
|
+
npv=total_npv,
|
|
289
|
+
caplet_prices=caplet_prices,
|
|
290
|
+
caplet_details=caplet_details,
|
|
291
|
+
flat_vol=self.flat_vol,
|
|
292
|
+
dv01=dv01_val,
|
|
293
|
+
vega=total_vega,
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
# =========================================================================
|
|
297
|
+
# Internal Pricing Methods
|
|
298
|
+
# =========================================================================
|
|
299
|
+
|
|
300
|
+
def _price_caplet(
|
|
301
|
+
self,
|
|
302
|
+
caplet: Caplet,
|
|
303
|
+
cap_floor_type: CapFloorType,
|
|
304
|
+
valuation_date: datetime,
|
|
305
|
+
) -> float:
|
|
306
|
+
"""
|
|
307
|
+
Price a single caplet/floorlet using Black's formula.
|
|
308
|
+
|
|
309
|
+
Args:
|
|
310
|
+
caplet: Caplet to price
|
|
311
|
+
cap_floor_type: CAP or FLOOR
|
|
312
|
+
valuation_date: Valuation date
|
|
313
|
+
|
|
314
|
+
Returns:
|
|
315
|
+
Present value of the caplet/floorlet
|
|
316
|
+
"""
|
|
317
|
+
# If caplet has already fixed, compute intrinsic value
|
|
318
|
+
if not caplet.is_projected and caplet.index_fixing is not None:
|
|
319
|
+
return self._price_fixed_caplet(caplet, cap_floor_type, valuation_date)
|
|
320
|
+
|
|
321
|
+
# Forward rate
|
|
322
|
+
fwd = self._get_forward_rate(caplet, valuation_date)
|
|
323
|
+
K = caplet.strike
|
|
324
|
+
dcf = caplet.day_count_fraction
|
|
325
|
+
N = caplet.notional
|
|
326
|
+
|
|
327
|
+
# Time to fixing (option expiry for this caplet)
|
|
328
|
+
t_fix = (caplet.fixing_date - valuation_date).days / 365.0
|
|
329
|
+
if t_fix <= 0:
|
|
330
|
+
t_fix = 0.0
|
|
331
|
+
|
|
332
|
+
# Volatility
|
|
333
|
+
sigma = self._get_vol(caplet, fwd, t_fix)
|
|
334
|
+
|
|
335
|
+
# Discount factor to payment date
|
|
336
|
+
t_pay = (caplet.payment_date - valuation_date).days / 365.0
|
|
337
|
+
df = self.pricing_env.get_discount_factor(t_pay)
|
|
338
|
+
|
|
339
|
+
# Near-expiry or zero vol: use intrinsic
|
|
340
|
+
if is_zero(t_fix) or is_zero(sigma):
|
|
341
|
+
if cap_floor_type == CapFloorType.CAP:
|
|
342
|
+
payoff = max(0.0, fwd - K)
|
|
343
|
+
else:
|
|
344
|
+
payoff = max(0.0, K - fwd)
|
|
345
|
+
return df * dcf * N * payoff
|
|
346
|
+
|
|
347
|
+
# Black's formula
|
|
348
|
+
sqrt_t = safe_sqrt(t_fix)
|
|
349
|
+
sigma_sqrt_t = sigma * sqrt_t
|
|
350
|
+
|
|
351
|
+
if is_zero(sigma_sqrt_t):
|
|
352
|
+
if cap_floor_type == CapFloorType.CAP:
|
|
353
|
+
payoff = max(0.0, fwd - K)
|
|
354
|
+
else:
|
|
355
|
+
payoff = max(0.0, K - fwd)
|
|
356
|
+
return df * dcf * N * payoff
|
|
357
|
+
|
|
358
|
+
d1 = safe_divide(
|
|
359
|
+
safe_log(fwd / K) + 0.5 * sigma * sigma * t_fix,
|
|
360
|
+
sigma_sqrt_t,
|
|
361
|
+
fallback=0.0,
|
|
362
|
+
)
|
|
363
|
+
d2 = d1 - sigma_sqrt_t
|
|
364
|
+
|
|
365
|
+
if cap_floor_type == CapFloorType.CAP:
|
|
366
|
+
price = df * dcf * N * (fwd * norm.cdf(d1) - K * norm.cdf(d2))
|
|
367
|
+
else:
|
|
368
|
+
price = df * dcf * N * (K * norm.cdf(-d2) - fwd * norm.cdf(-d1))
|
|
369
|
+
|
|
370
|
+
return max(0.0, price)
|
|
371
|
+
|
|
372
|
+
def _price_fixed_caplet(
|
|
373
|
+
self,
|
|
374
|
+
caplet: Caplet,
|
|
375
|
+
cap_floor_type: CapFloorType,
|
|
376
|
+
valuation_date: datetime,
|
|
377
|
+
) -> float:
|
|
378
|
+
"""Price a caplet that has already fixed (intrinsic only)."""
|
|
379
|
+
L = caplet.index_fixing
|
|
380
|
+
K = caplet.strike
|
|
381
|
+
|
|
382
|
+
if cap_floor_type == CapFloorType.CAP:
|
|
383
|
+
payoff = max(0.0, L - K)
|
|
384
|
+
else:
|
|
385
|
+
payoff = max(0.0, K - L)
|
|
386
|
+
|
|
387
|
+
t_pay = (caplet.payment_date - valuation_date).days / 365.0
|
|
388
|
+
if t_pay <= 0:
|
|
389
|
+
return 0.0
|
|
390
|
+
|
|
391
|
+
df = self.pricing_env.get_discount_factor(t_pay)
|
|
392
|
+
return df * caplet.day_count_fraction * caplet.notional * payoff
|
|
393
|
+
|
|
394
|
+
def _analyze_caplet(
|
|
395
|
+
self,
|
|
396
|
+
caplet: Caplet,
|
|
397
|
+
cap_floor_type: CapFloorType,
|
|
398
|
+
valuation_date: datetime,
|
|
399
|
+
) -> CapletPricingResult:
|
|
400
|
+
"""Full analysis of a single caplet including Greeks."""
|
|
401
|
+
fwd = self._get_forward_rate(caplet, valuation_date)
|
|
402
|
+
K = caplet.strike
|
|
403
|
+
dcf = caplet.day_count_fraction
|
|
404
|
+
N = caplet.notional
|
|
405
|
+
|
|
406
|
+
t_fix = max(0.0, (caplet.fixing_date - valuation_date).days / 365.0)
|
|
407
|
+
sigma = self._get_vol(caplet, fwd, t_fix)
|
|
408
|
+
|
|
409
|
+
t_pay = (caplet.payment_date - valuation_date).days / 365.0
|
|
410
|
+
df = self.pricing_env.get_discount_factor(t_pay)
|
|
411
|
+
|
|
412
|
+
# Calculate d1, d2
|
|
413
|
+
sqrt_t = safe_sqrt(t_fix)
|
|
414
|
+
sigma_sqrt_t = sigma * sqrt_t if not is_zero(t_fix) else 0.0
|
|
415
|
+
|
|
416
|
+
if is_zero(sigma_sqrt_t):
|
|
417
|
+
d1 = 0.0
|
|
418
|
+
d2 = 0.0
|
|
419
|
+
else:
|
|
420
|
+
d1 = safe_divide(
|
|
421
|
+
safe_log(fwd / K) + 0.5 * sigma * sigma * t_fix,
|
|
422
|
+
sigma_sqrt_t,
|
|
423
|
+
fallback=0.0,
|
|
424
|
+
)
|
|
425
|
+
d2 = d1 - sigma_sqrt_t
|
|
426
|
+
|
|
427
|
+
# Price
|
|
428
|
+
price = self._price_caplet(caplet, cap_floor_type, valuation_date)
|
|
429
|
+
|
|
430
|
+
# Intrinsic
|
|
431
|
+
if cap_floor_type == CapFloorType.CAP:
|
|
432
|
+
intrinsic = df * dcf * N * max(0.0, fwd - K)
|
|
433
|
+
else:
|
|
434
|
+
intrinsic = df * dcf * N * max(0.0, K - fwd)
|
|
435
|
+
|
|
436
|
+
# Delta: dPrice/dF
|
|
437
|
+
if cap_floor_type == CapFloorType.CAP:
|
|
438
|
+
delta = df * dcf * N * norm.cdf(d1)
|
|
439
|
+
else:
|
|
440
|
+
delta = -df * dcf * N * norm.cdf(-d1)
|
|
441
|
+
|
|
442
|
+
# Vega: dPrice/dSigma (per 1% move)
|
|
443
|
+
vega_val = df * dcf * N * fwd * norm.pdf(d1) * sqrt_t * 0.01
|
|
444
|
+
|
|
445
|
+
return CapletPricingResult(
|
|
446
|
+
price=price,
|
|
447
|
+
forward_rate=fwd,
|
|
448
|
+
vol=sigma,
|
|
449
|
+
d1=d1,
|
|
450
|
+
d2=d2,
|
|
451
|
+
intrinsic=intrinsic,
|
|
452
|
+
time_value=price - intrinsic,
|
|
453
|
+
delta=delta,
|
|
454
|
+
vega=vega_val,
|
|
455
|
+
accrual_start=caplet.accrual_start,
|
|
456
|
+
accrual_end=caplet.accrual_end,
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
def _get_forward_rate(
|
|
460
|
+
self, caplet: Caplet, valuation_date: datetime
|
|
461
|
+
) -> float:
|
|
462
|
+
"""Get forward rate for a caplet's accrual period."""
|
|
463
|
+
# If already fixed, use the fixing
|
|
464
|
+
if not caplet.is_projected and caplet.index_fixing is not None:
|
|
465
|
+
return caplet.index_fixing
|
|
466
|
+
|
|
467
|
+
t1 = (caplet.accrual_start - valuation_date).days / 365.0
|
|
468
|
+
t2 = (caplet.accrual_end - valuation_date).days / 365.0
|
|
469
|
+
|
|
470
|
+
if t1 < 0:
|
|
471
|
+
t1 = 0.0
|
|
472
|
+
|
|
473
|
+
return self.projection_curve.get_forward_rate(t1, t2)
|
|
474
|
+
|
|
475
|
+
def _get_vol(
|
|
476
|
+
self, caplet: Caplet, forward_rate: float, time_to_fixing: float
|
|
477
|
+
) -> float:
|
|
478
|
+
"""
|
|
479
|
+
Get implied volatility for a caplet.
|
|
480
|
+
|
|
481
|
+
Priority:
|
|
482
|
+
1. Flat vol override (self.flat_vol)
|
|
483
|
+
2. Vol surface from pricing env (if available)
|
|
484
|
+
3. Raise error if neither available
|
|
485
|
+
|
|
486
|
+
Args:
|
|
487
|
+
caplet: Caplet
|
|
488
|
+
forward_rate: Forward rate (used as strike proxy for vol surface)
|
|
489
|
+
time_to_fixing: Time to caplet expiry
|
|
490
|
+
|
|
491
|
+
Returns:
|
|
492
|
+
Implied volatility
|
|
493
|
+
"""
|
|
494
|
+
if self.flat_vol is not None:
|
|
495
|
+
return self.flat_vol
|
|
496
|
+
|
|
497
|
+
if self.pricing_env.vol_surface is not None:
|
|
498
|
+
# Use caplet strike as lookup key
|
|
499
|
+
# For rate vol surfaces, strike = rate level
|
|
500
|
+
return self.pricing_env.vol_surface.get_vol(
|
|
501
|
+
caplet.strike, time_to_fixing, forward_rate
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
raise MarketDataError(
|
|
505
|
+
"No volatility provided: set flat_vol or provide vol_surface "
|
|
506
|
+
"in PricingEnvironment"
|
|
507
|
+
)
|
|
508
|
+
|
|
509
|
+
def __repr__(self):
|
|
510
|
+
vol_str = f", flat_vol={self.flat_vol:.2%}" if self.flat_vol else ""
|
|
511
|
+
return (
|
|
512
|
+
f"CapFloorEngine(valuation_date="
|
|
513
|
+
f"{self.pricing_env.valuation_date.date()}{vol_str})"
|
|
514
|
+
)
|