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,569 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pricing engine for bond futures contracts with CTD analysis.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from typing import Dict, List, Optional, Tuple
|
|
9
|
+
|
|
10
|
+
from quantark.asset.bond.product.futures.bond_futures import BondFutures, DeliverableBond
|
|
11
|
+
from quantark.asset.bond.engine.discount.bond_discount_engine import BondDiscountEngine
|
|
12
|
+
from quantark.priceenv import PricingEnvironment
|
|
13
|
+
from quantark.util.exceptions import ValidationError, PricingError
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class BondAnalysis:
|
|
18
|
+
"""
|
|
19
|
+
Analysis results for a single deliverable bond.
|
|
20
|
+
|
|
21
|
+
Attributes:
|
|
22
|
+
bond_index: Index in the deliverable basket
|
|
23
|
+
dirty_price: Current dirty price
|
|
24
|
+
clean_price: Current clean price
|
|
25
|
+
accrued_interest: Current accrued interest
|
|
26
|
+
conversion_factor: Exchange conversion factor
|
|
27
|
+
gross_basis: Bond Price - Futures × CF
|
|
28
|
+
net_basis: Gross Basis - Carry
|
|
29
|
+
implied_repo_rate: Implied financing rate
|
|
30
|
+
invoice_price: Invoice at delivery
|
|
31
|
+
is_ctd: Whether this is the CTD bond
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
bond_index: int
|
|
35
|
+
dirty_price: float
|
|
36
|
+
clean_price: float
|
|
37
|
+
accrued_interest: float
|
|
38
|
+
conversion_factor: float
|
|
39
|
+
gross_basis: float
|
|
40
|
+
net_basis: float
|
|
41
|
+
implied_repo_rate: float
|
|
42
|
+
invoice_price: float
|
|
43
|
+
is_ctd: bool = False
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass
|
|
47
|
+
class BondFuturesResults:
|
|
48
|
+
"""
|
|
49
|
+
Results from bond futures pricing.
|
|
50
|
+
|
|
51
|
+
Attributes:
|
|
52
|
+
theoretical_futures_price: Theoretical price from CTD bond
|
|
53
|
+
ctd_bond_index: Index of CTD bond in basket
|
|
54
|
+
ctd_implied_repo: Implied repo rate of CTD
|
|
55
|
+
bond_analyses: Analysis for each deliverable bond
|
|
56
|
+
dv01: Dollar value of 1bp rate change
|
|
57
|
+
modified_duration: CTD-adjusted duration
|
|
58
|
+
convexity: CTD-adjusted convexity
|
|
59
|
+
time_to_delivery: Time to delivery in years
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
theoretical_futures_price: float
|
|
63
|
+
ctd_bond_index: int
|
|
64
|
+
ctd_implied_repo: float
|
|
65
|
+
bond_analyses: List[BondAnalysis]
|
|
66
|
+
dv01: float
|
|
67
|
+
modified_duration: float
|
|
68
|
+
convexity: float
|
|
69
|
+
time_to_delivery: float
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class BondFuturesEngine:
|
|
73
|
+
"""
|
|
74
|
+
Pricing engine for bond futures with CTD (Cheapest to Deliver) logic.
|
|
75
|
+
|
|
76
|
+
This engine:
|
|
77
|
+
1. Prices each bond in the deliverable basket
|
|
78
|
+
2. Calculates conversion factors and basis for each bond
|
|
79
|
+
3. Identifies the CTD bond (highest implied repo rate)
|
|
80
|
+
4. Prices the futures from the CTD bond
|
|
81
|
+
5. Calculates CTD-adjusted risk measures
|
|
82
|
+
|
|
83
|
+
The CTD bond is the one that a rational deliverer would choose to
|
|
84
|
+
deliver, as it minimizes their cost (maximizes profit) at delivery.
|
|
85
|
+
|
|
86
|
+
Attributes:
|
|
87
|
+
pricing_env: Pricing environment with market data
|
|
88
|
+
bond_engine: Underlying bond pricing engine
|
|
89
|
+
bump_size: Basis point bump for Greeks (default: 1bp)
|
|
90
|
+
repo_rate: Financing rate for carry calculations
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
def __init__(
|
|
94
|
+
self,
|
|
95
|
+
pricing_env: PricingEnvironment,
|
|
96
|
+
repo_rate: float = 0.05,
|
|
97
|
+
bump_size: float = 0.0001,
|
|
98
|
+
):
|
|
99
|
+
"""
|
|
100
|
+
Initialize bond futures engine.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
pricing_env: Pricing environment with rate curve
|
|
104
|
+
repo_rate: Financing rate for carry calculations (default: 5%)
|
|
105
|
+
bump_size: Bump size for finite difference Greeks (default: 1bp)
|
|
106
|
+
"""
|
|
107
|
+
if pricing_env is None:
|
|
108
|
+
raise ValidationError("Pricing environment is required")
|
|
109
|
+
|
|
110
|
+
self.pricing_env = pricing_env
|
|
111
|
+
self.bond_engine = BondDiscountEngine(pricing_env)
|
|
112
|
+
self.repo_rate = repo_rate
|
|
113
|
+
self.bump_size = bump_size
|
|
114
|
+
|
|
115
|
+
def price(
|
|
116
|
+
self,
|
|
117
|
+
futures: BondFutures,
|
|
118
|
+
valuation_date: Optional[datetime] = None,
|
|
119
|
+
calculate_greeks: bool = True,
|
|
120
|
+
) -> BondFuturesResults:
|
|
121
|
+
"""
|
|
122
|
+
Price a bond futures contract and identify CTD.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
futures: Bond futures contract to price
|
|
126
|
+
valuation_date: Valuation date (default: pricing env date)
|
|
127
|
+
calculate_greeks: Whether to calculate Greeks (default: True)
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
BondFuturesResults with pricing and CTD analysis
|
|
131
|
+
|
|
132
|
+
Raises:
|
|
133
|
+
PricingError: If pricing fails
|
|
134
|
+
"""
|
|
135
|
+
if valuation_date is None:
|
|
136
|
+
valuation_date = self.pricing_env.valuation_date
|
|
137
|
+
|
|
138
|
+
# Check if expired
|
|
139
|
+
if futures.is_expired(valuation_date):
|
|
140
|
+
raise PricingError("Futures contract has expired")
|
|
141
|
+
|
|
142
|
+
time_to_delivery = futures.get_time_to_delivery(valuation_date)
|
|
143
|
+
|
|
144
|
+
# Price each deliverable bond
|
|
145
|
+
bond_prices = []
|
|
146
|
+
bond_analyses = []
|
|
147
|
+
|
|
148
|
+
for i, db in enumerate(futures.deliverable_basket):
|
|
149
|
+
bond = db.bond
|
|
150
|
+
|
|
151
|
+
# Price the bond
|
|
152
|
+
dirty_price = self.bond_engine.dirty_price(
|
|
153
|
+
bond, valuation_date, valuation_date
|
|
154
|
+
)
|
|
155
|
+
clean_price = self.bond_engine.clean_price(
|
|
156
|
+
bond, valuation_date, valuation_date
|
|
157
|
+
)
|
|
158
|
+
accrued = bond.calculate_accrued_interest(valuation_date)
|
|
159
|
+
|
|
160
|
+
bond_prices.append(dirty_price)
|
|
161
|
+
|
|
162
|
+
cf = futures.get_conversion_factor(i)
|
|
163
|
+
|
|
164
|
+
# Calculate basis and implied repo (if futures price set)
|
|
165
|
+
if futures.futures_price is not None:
|
|
166
|
+
gross_basis = futures.calculate_gross_basis(i, dirty_price)
|
|
167
|
+
implied_repo = futures.calculate_implied_repo_rate(
|
|
168
|
+
i, dirty_price, valuation_date
|
|
169
|
+
)
|
|
170
|
+
invoice = futures.calculate_invoice_price(i)
|
|
171
|
+
|
|
172
|
+
# Calculate carry for net basis
|
|
173
|
+
carry = self._calculate_carry(
|
|
174
|
+
bond, dirty_price, valuation_date, futures.delivery_date
|
|
175
|
+
)
|
|
176
|
+
net_basis = gross_basis - carry
|
|
177
|
+
else:
|
|
178
|
+
gross_basis = 0.0
|
|
179
|
+
net_basis = 0.0
|
|
180
|
+
implied_repo = 0.0
|
|
181
|
+
invoice = 0.0
|
|
182
|
+
|
|
183
|
+
analysis = BondAnalysis(
|
|
184
|
+
bond_index=i,
|
|
185
|
+
dirty_price=dirty_price,
|
|
186
|
+
clean_price=clean_price,
|
|
187
|
+
accrued_interest=accrued,
|
|
188
|
+
conversion_factor=cf,
|
|
189
|
+
gross_basis=gross_basis,
|
|
190
|
+
net_basis=net_basis,
|
|
191
|
+
implied_repo_rate=implied_repo,
|
|
192
|
+
invoice_price=invoice,
|
|
193
|
+
is_ctd=False,
|
|
194
|
+
)
|
|
195
|
+
bond_analyses.append(analysis)
|
|
196
|
+
|
|
197
|
+
# Find CTD bond
|
|
198
|
+
if futures.futures_price is not None:
|
|
199
|
+
ctd_index, ctd_repo = futures.find_ctd_bond(bond_prices, valuation_date)
|
|
200
|
+
else:
|
|
201
|
+
# If no futures price, use first bond as pseudo-CTD
|
|
202
|
+
ctd_index = 0
|
|
203
|
+
ctd_repo = 0.0
|
|
204
|
+
|
|
205
|
+
# Mark CTD
|
|
206
|
+
bond_analyses[ctd_index].is_ctd = True
|
|
207
|
+
|
|
208
|
+
# Calculate theoretical futures price from CTD
|
|
209
|
+
theoretical_price = futures.calculate_theoretical_futures_price(
|
|
210
|
+
ctd_index, bond_prices[ctd_index], valuation_date, self.repo_rate
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
# Calculate Greeks (avoid recursion by skipping in bumped engines)
|
|
214
|
+
if calculate_greeks:
|
|
215
|
+
dv01 = self._calculate_futures_dv01(
|
|
216
|
+
futures, bond_prices, ctd_index, valuation_date, theoretical_price
|
|
217
|
+
)
|
|
218
|
+
mod_dur = self._calculate_futures_duration(theoretical_price, dv01)
|
|
219
|
+
convexity = self._calculate_futures_convexity(
|
|
220
|
+
futures, bond_prices, ctd_index, valuation_date, theoretical_price
|
|
221
|
+
)
|
|
222
|
+
else:
|
|
223
|
+
dv01 = 0.0
|
|
224
|
+
mod_dur = 0.0
|
|
225
|
+
convexity = 0.0
|
|
226
|
+
|
|
227
|
+
return BondFuturesResults(
|
|
228
|
+
theoretical_futures_price=theoretical_price,
|
|
229
|
+
ctd_bond_index=ctd_index,
|
|
230
|
+
ctd_implied_repo=ctd_repo,
|
|
231
|
+
bond_analyses=bond_analyses,
|
|
232
|
+
dv01=dv01,
|
|
233
|
+
modified_duration=mod_dur,
|
|
234
|
+
convexity=convexity,
|
|
235
|
+
time_to_delivery=time_to_delivery,
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
def _calculate_carry(
|
|
239
|
+
self,
|
|
240
|
+
bond,
|
|
241
|
+
dirty_price: float,
|
|
242
|
+
valuation_date: datetime,
|
|
243
|
+
delivery_date: datetime,
|
|
244
|
+
) -> float:
|
|
245
|
+
"""
|
|
246
|
+
Calculate carry cost between valuation and delivery.
|
|
247
|
+
|
|
248
|
+
Carry = Coupon Income - Financing Cost
|
|
249
|
+
"""
|
|
250
|
+
time_to_delivery = (delivery_date - valuation_date).days / 365.0
|
|
251
|
+
|
|
252
|
+
if time_to_delivery <= 0:
|
|
253
|
+
return 0.0
|
|
254
|
+
|
|
255
|
+
# Financing cost
|
|
256
|
+
financing_cost = dirty_price * (math.exp(self.repo_rate * time_to_delivery) - 1)
|
|
257
|
+
|
|
258
|
+
# Coupon income
|
|
259
|
+
coupon_income = 0.0
|
|
260
|
+
all_cashflows = bond.get_all_cashflows()
|
|
261
|
+
|
|
262
|
+
for cf in all_cashflows:
|
|
263
|
+
if valuation_date < cf.payment_date <= delivery_date:
|
|
264
|
+
# Exclude principal
|
|
265
|
+
amount = cf.amount
|
|
266
|
+
if cf.payment_date == bond.maturity_date:
|
|
267
|
+
amount = cf.amount - bond.get_denominator()
|
|
268
|
+
if amount > 0:
|
|
269
|
+
coupon_income += amount
|
|
270
|
+
|
|
271
|
+
carry = coupon_income - financing_cost
|
|
272
|
+
return carry
|
|
273
|
+
|
|
274
|
+
def _calculate_futures_dv01(
|
|
275
|
+
self,
|
|
276
|
+
futures: BondFutures,
|
|
277
|
+
bond_prices: List[float],
|
|
278
|
+
ctd_index: int,
|
|
279
|
+
valuation_date: datetime,
|
|
280
|
+
base_price: float,
|
|
281
|
+
) -> float:
|
|
282
|
+
"""Calculate DV01 of futures (CTD-adjusted)."""
|
|
283
|
+
from quantark.param.rrf.rate_curve import FlatRateCurve
|
|
284
|
+
|
|
285
|
+
# Get base rate
|
|
286
|
+
base_rate = self.pricing_env.rate_curve.get_rate(1.0)
|
|
287
|
+
|
|
288
|
+
# Create bumped environment with a new flat curve
|
|
289
|
+
bumped_rate = base_rate + self.bump_size
|
|
290
|
+
bumped_curve = FlatRateCurve(rate=bumped_rate)
|
|
291
|
+
|
|
292
|
+
env_up = PricingEnvironment(
|
|
293
|
+
rate_curve=bumped_curve,
|
|
294
|
+
valuation_date=self.pricing_env.valuation_date,
|
|
295
|
+
spot_quote=self.pricing_env.spot_quote,
|
|
296
|
+
vol_surface=self.pricing_env.vol_surface,
|
|
297
|
+
div_yield=self.pricing_env.div_yield,
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
# Create new engine and price without Greeks to avoid recursion
|
|
301
|
+
engine_up = BondFuturesEngine(env_up, self.repo_rate, self.bump_size)
|
|
302
|
+
results_up = engine_up.price(futures, valuation_date, calculate_greeks=False)
|
|
303
|
+
|
|
304
|
+
# DV01 = change in futures price for 1bp increase
|
|
305
|
+
dv01 = base_price - results_up.theoretical_futures_price
|
|
306
|
+
|
|
307
|
+
# Adjust by conversion factor of CTD
|
|
308
|
+
cf = futures.get_conversion_factor(ctd_index)
|
|
309
|
+
dv01_adjusted = dv01 / cf
|
|
310
|
+
|
|
311
|
+
return dv01_adjusted
|
|
312
|
+
|
|
313
|
+
def _calculate_futures_duration(self, futures_price: float, dv01: float) -> float:
|
|
314
|
+
"""Calculate modified duration of futures (CTD-adjusted)."""
|
|
315
|
+
if futures_price == 0:
|
|
316
|
+
return 0.0
|
|
317
|
+
|
|
318
|
+
# Duration = DV01 / (Price * bump_size)
|
|
319
|
+
mod_dur = dv01 / (futures_price * self.bump_size)
|
|
320
|
+
|
|
321
|
+
return mod_dur
|
|
322
|
+
|
|
323
|
+
def _calculate_futures_convexity(
|
|
324
|
+
self,
|
|
325
|
+
futures: BondFutures,
|
|
326
|
+
bond_prices: List[float],
|
|
327
|
+
ctd_index: int,
|
|
328
|
+
valuation_date: datetime,
|
|
329
|
+
futures_price: float,
|
|
330
|
+
) -> float:
|
|
331
|
+
"""Calculate convexity of futures (CTD-adjusted)."""
|
|
332
|
+
from quantark.param.rrf.rate_curve import FlatRateCurve
|
|
333
|
+
|
|
334
|
+
if futures_price == 0:
|
|
335
|
+
return 0.0
|
|
336
|
+
|
|
337
|
+
# Get base rate
|
|
338
|
+
base_rate = self.pricing_env.rate_curve.get_rate(1.0)
|
|
339
|
+
|
|
340
|
+
# Bump up
|
|
341
|
+
bumped_rate_up = base_rate + self.bump_size
|
|
342
|
+
curve_up = FlatRateCurve(rate=bumped_rate_up)
|
|
343
|
+
env_up = PricingEnvironment(
|
|
344
|
+
rate_curve=curve_up,
|
|
345
|
+
valuation_date=self.pricing_env.valuation_date,
|
|
346
|
+
spot_quote=self.pricing_env.spot_quote,
|
|
347
|
+
vol_surface=self.pricing_env.vol_surface,
|
|
348
|
+
div_yield=self.pricing_env.div_yield,
|
|
349
|
+
)
|
|
350
|
+
engine_up = BondFuturesEngine(env_up, self.repo_rate, self.bump_size)
|
|
351
|
+
results_up = engine_up.price(futures, valuation_date, calculate_greeks=False)
|
|
352
|
+
|
|
353
|
+
# Bump down
|
|
354
|
+
bumped_rate_down = base_rate - self.bump_size
|
|
355
|
+
curve_down = FlatRateCurve(rate=bumped_rate_down)
|
|
356
|
+
env_down = PricingEnvironment(
|
|
357
|
+
rate_curve=curve_down,
|
|
358
|
+
valuation_date=self.pricing_env.valuation_date,
|
|
359
|
+
spot_quote=self.pricing_env.spot_quote,
|
|
360
|
+
vol_surface=self.pricing_env.vol_surface,
|
|
361
|
+
div_yield=self.pricing_env.div_yield,
|
|
362
|
+
)
|
|
363
|
+
engine_down = BondFuturesEngine(env_down, self.repo_rate, self.bump_size)
|
|
364
|
+
results_down = engine_down.price(
|
|
365
|
+
futures, valuation_date, calculate_greeks=False
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
price_up = results_up.theoretical_futures_price
|
|
369
|
+
price_down = results_down.theoretical_futures_price
|
|
370
|
+
|
|
371
|
+
# Convexity = (P_up - 2*P_base + P_down) / (P_base * bump^2)
|
|
372
|
+
convexity = (price_up - 2 * futures_price + price_down) / (
|
|
373
|
+
futures_price * self.bump_size * self.bump_size
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
return convexity
|
|
377
|
+
|
|
378
|
+
def calculate_greeks(
|
|
379
|
+
self, futures: BondFutures, valuation_date: Optional[datetime] = None
|
|
380
|
+
) -> Dict[str, float]:
|
|
381
|
+
"""
|
|
382
|
+
Calculate Greeks for bond futures.
|
|
383
|
+
|
|
384
|
+
Greeks calculated:
|
|
385
|
+
- theoretical_price: Theoretical futures price from CTD
|
|
386
|
+
- dv01: Dollar value of 1bp rate change
|
|
387
|
+
- modified_duration: CTD-adjusted duration
|
|
388
|
+
- convexity: CTD-adjusted convexity
|
|
389
|
+
- ctd_bond_index: Index of CTD bond
|
|
390
|
+
- ctd_implied_repo: Implied repo rate of CTD
|
|
391
|
+
- basis_point_value: BPV per contract
|
|
392
|
+
|
|
393
|
+
Args:
|
|
394
|
+
futures: Bond futures contract
|
|
395
|
+
valuation_date: Valuation date
|
|
396
|
+
|
|
397
|
+
Returns:
|
|
398
|
+
Dictionary of Greeks
|
|
399
|
+
"""
|
|
400
|
+
if valuation_date is None:
|
|
401
|
+
valuation_date = self.pricing_env.valuation_date
|
|
402
|
+
|
|
403
|
+
results = self.price(futures, valuation_date)
|
|
404
|
+
|
|
405
|
+
greeks = {
|
|
406
|
+
"theoretical_price": results.theoretical_futures_price,
|
|
407
|
+
"dv01": results.dv01,
|
|
408
|
+
"modified_duration": results.modified_duration,
|
|
409
|
+
"convexity": results.convexity,
|
|
410
|
+
"ctd_bond_index": results.ctd_bond_index,
|
|
411
|
+
"ctd_implied_repo": results.ctd_implied_repo,
|
|
412
|
+
"time_to_delivery": results.time_to_delivery,
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
# Basis point value per contract
|
|
416
|
+
greeks["basis_point_value"] = results.dv01 * futures.contract_size / 100.0
|
|
417
|
+
|
|
418
|
+
return greeks
|
|
419
|
+
|
|
420
|
+
def analyze_basis(
|
|
421
|
+
self, futures: BondFutures, valuation_date: Optional[datetime] = None
|
|
422
|
+
) -> List[Dict]:
|
|
423
|
+
"""
|
|
424
|
+
Analyze basis for all deliverable bonds.
|
|
425
|
+
|
|
426
|
+
Returns detailed basis analysis including:
|
|
427
|
+
- Gross basis
|
|
428
|
+
- Net basis
|
|
429
|
+
- Implied repo rate
|
|
430
|
+
- Delivery option value
|
|
431
|
+
|
|
432
|
+
Args:
|
|
433
|
+
futures: Bond futures contract
|
|
434
|
+
valuation_date: Valuation date
|
|
435
|
+
|
|
436
|
+
Returns:
|
|
437
|
+
List of dictionaries with basis analysis per bond
|
|
438
|
+
"""
|
|
439
|
+
if valuation_date is None:
|
|
440
|
+
valuation_date = self.pricing_env.valuation_date
|
|
441
|
+
|
|
442
|
+
if futures.futures_price is None:
|
|
443
|
+
raise PricingError("Futures price must be set for basis analysis")
|
|
444
|
+
|
|
445
|
+
results = self.price(futures, valuation_date)
|
|
446
|
+
|
|
447
|
+
analyses = []
|
|
448
|
+
for ba in results.bond_analyses:
|
|
449
|
+
bond = futures.deliverable_basket[ba.bond_index].bond
|
|
450
|
+
|
|
451
|
+
analysis = {
|
|
452
|
+
"bond_index": ba.bond_index,
|
|
453
|
+
"bond_description": f"{bond.coupon_rate:.2%} {bond.maturity_date.date()}",
|
|
454
|
+
"dirty_price": ba.dirty_price,
|
|
455
|
+
"clean_price": ba.clean_price,
|
|
456
|
+
"conversion_factor": ba.conversion_factor,
|
|
457
|
+
"gross_basis": ba.gross_basis,
|
|
458
|
+
"net_basis": ba.net_basis,
|
|
459
|
+
"implied_repo_rate": ba.implied_repo_rate,
|
|
460
|
+
"invoice_price": ba.invoice_price,
|
|
461
|
+
"is_ctd": ba.is_ctd,
|
|
462
|
+
"delivery_value": (
|
|
463
|
+
ba.invoice_price - ba.dirty_price if ba.is_ctd else None
|
|
464
|
+
),
|
|
465
|
+
}
|
|
466
|
+
analyses.append(analysis)
|
|
467
|
+
|
|
468
|
+
# Sort by implied repo (highest first = CTD)
|
|
469
|
+
analyses.sort(key=lambda x: x["implied_repo_rate"], reverse=True)
|
|
470
|
+
|
|
471
|
+
return analyses
|
|
472
|
+
|
|
473
|
+
def calculate_hedge_ratio(
|
|
474
|
+
self,
|
|
475
|
+
futures: BondFutures,
|
|
476
|
+
target_bond_dv01: float,
|
|
477
|
+
valuation_date: Optional[datetime] = None,
|
|
478
|
+
) -> float:
|
|
479
|
+
"""
|
|
480
|
+
Calculate hedge ratio for hedging a bond position with futures.
|
|
481
|
+
|
|
482
|
+
Hedge Ratio = Target Bond DV01 / Futures DV01
|
|
483
|
+
|
|
484
|
+
Args:
|
|
485
|
+
futures: Bond futures contract
|
|
486
|
+
target_bond_dv01: DV01 of the bond position to hedge
|
|
487
|
+
valuation_date: Valuation date
|
|
488
|
+
|
|
489
|
+
Returns:
|
|
490
|
+
Number of futures contracts needed (may be fractional)
|
|
491
|
+
"""
|
|
492
|
+
if valuation_date is None:
|
|
493
|
+
valuation_date = self.pricing_env.valuation_date
|
|
494
|
+
|
|
495
|
+
results = self.price(futures, valuation_date)
|
|
496
|
+
|
|
497
|
+
if results.dv01 == 0:
|
|
498
|
+
raise PricingError("Futures DV01 is zero, cannot calculate hedge ratio")
|
|
499
|
+
|
|
500
|
+
# Futures DV01 per contract
|
|
501
|
+
futures_dv01_per_contract = results.dv01 * futures.contract_size / 100.0
|
|
502
|
+
|
|
503
|
+
# Hedge ratio
|
|
504
|
+
hedge_ratio = target_bond_dv01 / futures_dv01_per_contract
|
|
505
|
+
|
|
506
|
+
return hedge_ratio
|
|
507
|
+
|
|
508
|
+
def find_delivery_option_value(
|
|
509
|
+
self, futures: BondFutures, valuation_date: Optional[datetime] = None
|
|
510
|
+
) -> Dict[str, float]:
|
|
511
|
+
"""
|
|
512
|
+
Estimate the value of the delivery option.
|
|
513
|
+
|
|
514
|
+
The delivery option value is approximately the difference between:
|
|
515
|
+
- The theoretical futures price (from CTD)
|
|
516
|
+
- The average price across all deliverables
|
|
517
|
+
|
|
518
|
+
A positive value indicates the delivery option is valuable.
|
|
519
|
+
|
|
520
|
+
Args:
|
|
521
|
+
futures: Bond futures contract
|
|
522
|
+
valuation_date: Valuation date
|
|
523
|
+
|
|
524
|
+
Returns:
|
|
525
|
+
Dictionary with delivery option analysis
|
|
526
|
+
"""
|
|
527
|
+
if valuation_date is None:
|
|
528
|
+
valuation_date = self.pricing_env.valuation_date
|
|
529
|
+
|
|
530
|
+
results = self.price(futures, valuation_date)
|
|
531
|
+
|
|
532
|
+
# Calculate theoretical price for each bond
|
|
533
|
+
theoretical_prices = []
|
|
534
|
+
for i, ba in enumerate(results.bond_analyses):
|
|
535
|
+
theo_price = futures.calculate_theoretical_futures_price(
|
|
536
|
+
i, ba.dirty_price, valuation_date, self.repo_rate
|
|
537
|
+
)
|
|
538
|
+
theoretical_prices.append(theo_price)
|
|
539
|
+
|
|
540
|
+
# CTD price
|
|
541
|
+
ctd_price = results.theoretical_futures_price
|
|
542
|
+
|
|
543
|
+
# Average price
|
|
544
|
+
avg_price = (
|
|
545
|
+
sum(theoretical_prices) / len(theoretical_prices)
|
|
546
|
+
if theoretical_prices
|
|
547
|
+
else 0
|
|
548
|
+
)
|
|
549
|
+
|
|
550
|
+
# Max price (most expensive to deliver)
|
|
551
|
+
max_price = max(theoretical_prices) if theoretical_prices else 0
|
|
552
|
+
|
|
553
|
+
# Delivery option value approximation
|
|
554
|
+
# = what the futures would be worth if we couldn't choose CTD
|
|
555
|
+
option_value = avg_price - ctd_price
|
|
556
|
+
|
|
557
|
+
return {
|
|
558
|
+
"ctd_theoretical_price": ctd_price,
|
|
559
|
+
"average_theoretical_price": avg_price,
|
|
560
|
+
"max_theoretical_price": max_price,
|
|
561
|
+
"delivery_option_value": option_value,
|
|
562
|
+
"option_value_pct": option_value / ctd_price * 100 if ctd_price > 0 else 0,
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
def __repr__(self):
|
|
566
|
+
return (
|
|
567
|
+
f"BondFuturesEngine(valuation_date={self.pricing_env.valuation_date.date()}, "
|
|
568
|
+
f"repo={self.repo_rate:.2%})"
|
|
569
|
+
)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Facade engines for convertible bond pricing.
|
|
3
|
+
"""
|
|
4
|
+
from quantark.asset.bond.engine.convertible.convertible_bond_engine import (
|
|
5
|
+
ConvertibleBondEngine,
|
|
6
|
+
ConvertibleBondResult,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"ConvertibleBondEngine",
|
|
11
|
+
"ConvertibleBondResult",
|
|
12
|
+
]
|