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,108 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Black-Scholes-Merton process with continuous dividend yield.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from quantark.util.exceptions import ValidationError
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class BSMProcess:
|
|
11
|
+
"""
|
|
12
|
+
Black-Scholes-Merton stochastic process for equity pricing.
|
|
13
|
+
|
|
14
|
+
The BSM model assumes the underlying asset follows geometric Brownian motion:
|
|
15
|
+
dS/S = (r - q) dt + σ dW
|
|
16
|
+
|
|
17
|
+
where:
|
|
18
|
+
S = spot price
|
|
19
|
+
r = risk-free rate
|
|
20
|
+
q = continuous dividend yield
|
|
21
|
+
σ = volatility
|
|
22
|
+
W = Wiener process
|
|
23
|
+
|
|
24
|
+
Attributes:
|
|
25
|
+
spot: Current spot price
|
|
26
|
+
volatility: Annualized volatility (σ)
|
|
27
|
+
risk_free_rate: Risk-free interest rate (r)
|
|
28
|
+
dividend_yield: Continuous dividend yield (q)
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
spot: float
|
|
32
|
+
volatility: float
|
|
33
|
+
risk_free_rate: float
|
|
34
|
+
dividend_yield: float = 0.0
|
|
35
|
+
|
|
36
|
+
def __post_init__(self):
|
|
37
|
+
"""Validate process parameters."""
|
|
38
|
+
if self.spot <= 0:
|
|
39
|
+
raise ValidationError(f"Spot must be positive, got {self.spot}")
|
|
40
|
+
if self.volatility <= 0:
|
|
41
|
+
raise ValidationError(f"Volatility must be positive, got {self.volatility}")
|
|
42
|
+
if self.volatility > 5.0: # 500% vol - sanity check
|
|
43
|
+
raise ValidationError(
|
|
44
|
+
f"Volatility seems unreasonably high: {self.volatility}"
|
|
45
|
+
)
|
|
46
|
+
if self.dividend_yield < 0:
|
|
47
|
+
raise ValidationError(
|
|
48
|
+
f"Dividend yield must be non-negative, got {self.dividend_yield}"
|
|
49
|
+
)
|
|
50
|
+
# Allow negative rates
|
|
51
|
+
if self.risk_free_rate < -0.10 or self.risk_free_rate > 0.50:
|
|
52
|
+
raise ValidationError(
|
|
53
|
+
f"Risk-free rate outside reasonable bounds: {self.risk_free_rate}"
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def drift(self) -> float:
|
|
58
|
+
"""
|
|
59
|
+
Get the risk-neutral drift rate.
|
|
60
|
+
|
|
61
|
+
Under risk-neutral measure: μ = r - q
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
Drift rate (r - q)
|
|
65
|
+
"""
|
|
66
|
+
return self.risk_free_rate - self.dividend_yield
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def forward_price(self) -> float:
|
|
70
|
+
"""
|
|
71
|
+
Calculate the forward price.
|
|
72
|
+
|
|
73
|
+
F = S * exp((r - q) * T)
|
|
74
|
+
|
|
75
|
+
For T=1 year, returns the 1-year forward.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Forward price (1 year)
|
|
79
|
+
"""
|
|
80
|
+
import math
|
|
81
|
+
|
|
82
|
+
return self.spot * math.exp(self.drift)
|
|
83
|
+
|
|
84
|
+
def get_forward_price(self, time_to_maturity: float) -> float:
|
|
85
|
+
"""
|
|
86
|
+
Calculate forward price for a given maturity.
|
|
87
|
+
|
|
88
|
+
F(T) = S * exp((r - q) * T)
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
time_to_maturity: Time to maturity in years
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Forward price
|
|
95
|
+
"""
|
|
96
|
+
import math
|
|
97
|
+
|
|
98
|
+
if time_to_maturity < 0:
|
|
99
|
+
raise ValidationError(
|
|
100
|
+
f"Time to maturity must be non-negative, got {time_to_maturity}"
|
|
101
|
+
)
|
|
102
|
+
return self.spot * math.exp(self.drift * time_to_maturity)
|
|
103
|
+
|
|
104
|
+
def __repr__(self):
|
|
105
|
+
return (
|
|
106
|
+
f"BSMProcess(S={self.spot:.2f}, σ={self.volatility:.2%}, "
|
|
107
|
+
f"r={self.risk_free_rate:.2%}, q={self.dividend_yield:.2%})"
|
|
108
|
+
)
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Created on Mon Nov 17 2025
|
|
3
|
+
|
|
4
|
+
@author: yaofuxin
|
|
5
|
+
@description: Brownian bridge utilities for constructing Brownian motion
|
|
6
|
+
paths from standard normal draws, plus barrier crossing
|
|
7
|
+
helpers for barrier-style options.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
|
|
16
|
+
from quantark.util.numerical import safe_log
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class BrownianBridge:
|
|
21
|
+
"""
|
|
22
|
+
Brownian bridge constructor for a given time grid.
|
|
23
|
+
|
|
24
|
+
The bridge is defined on times t_1 < ... < t_n with t_0 = 0.0. Given
|
|
25
|
+
independent standard normal samples Z_1, ..., Z_n, the bridge produces
|
|
26
|
+
Brownian motion values W(t_k) with the correct covariance structure but
|
|
27
|
+
in a dimension ordering that is better suited for QMC (low effective
|
|
28
|
+
dimension).
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
times: np.ndarray
|
|
32
|
+
indices: np.ndarray
|
|
33
|
+
left: np.ndarray
|
|
34
|
+
right: np.ndarray
|
|
35
|
+
variances: np.ndarray
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def from_time_grid(cls, times: np.ndarray) -> "BrownianBridge":
|
|
39
|
+
"""
|
|
40
|
+
Precompute the Brownian bridge structure for a strictly increasing
|
|
41
|
+
time grid.
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
----------
|
|
45
|
+
times : np.ndarray
|
|
46
|
+
One-dimensional array of shape (n_steps,) with t_k > 0 and
|
|
47
|
+
strictly increasing.
|
|
48
|
+
"""
|
|
49
|
+
times = np.asarray(times, dtype=float)
|
|
50
|
+
if times.ndim != 1:
|
|
51
|
+
raise ValueError("times must be a one-dimensional array")
|
|
52
|
+
if not np.all(np.isfinite(times)):
|
|
53
|
+
raise ValueError("times must be finite")
|
|
54
|
+
if np.any(times <= 0.0):
|
|
55
|
+
raise ValueError("times must be strictly positive")
|
|
56
|
+
if np.any(np.diff(times) <= 0.0):
|
|
57
|
+
raise ValueError("times must be strictly increasing")
|
|
58
|
+
|
|
59
|
+
n = times.shape[0]
|
|
60
|
+
indices = np.empty(n, dtype=int)
|
|
61
|
+
left = np.full(n, -1, dtype=int)
|
|
62
|
+
right = np.full(n, -1, dtype=int)
|
|
63
|
+
variances = np.zeros(n, dtype=float)
|
|
64
|
+
|
|
65
|
+
# First dimension corresponds to terminal time T
|
|
66
|
+
indices[0] = n - 1
|
|
67
|
+
left[0] = -1
|
|
68
|
+
right[0] = -1
|
|
69
|
+
variances[0] = times[-1]
|
|
70
|
+
|
|
71
|
+
# Recursively fill midpoints
|
|
72
|
+
counter = 1
|
|
73
|
+
|
|
74
|
+
def build(l_idx: int, r_idx: int, cnt: int) -> int:
|
|
75
|
+
# Build bridge between times t_l and t_r, where l_idx/r_idx are
|
|
76
|
+
# indices in [0, n-1], with l_idx == -1 representing time 0.
|
|
77
|
+
if r_idx - l_idx <= 1:
|
|
78
|
+
return cnt
|
|
79
|
+
m_idx = (l_idx + r_idx) // 2
|
|
80
|
+
indices[cnt] = m_idx
|
|
81
|
+
left[cnt] = l_idx
|
|
82
|
+
right[cnt] = r_idx
|
|
83
|
+
|
|
84
|
+
t_l = 0.0 if l_idx == -1 else times[l_idx]
|
|
85
|
+
t_r = times[r_idx]
|
|
86
|
+
t_m = times[m_idx]
|
|
87
|
+
|
|
88
|
+
variance = (t_m - t_l) * (t_r - t_m) / (t_r - t_l)
|
|
89
|
+
variances[cnt] = max(variance, 0.0)
|
|
90
|
+
|
|
91
|
+
cnt = build(l_idx, m_idx, cnt + 1)
|
|
92
|
+
cnt = build(m_idx, r_idx, cnt)
|
|
93
|
+
return cnt
|
|
94
|
+
|
|
95
|
+
counter = build(-1, n - 1, counter)
|
|
96
|
+
if counter != n:
|
|
97
|
+
raise RuntimeError(
|
|
98
|
+
"BrownianBridge construction failed to fill all dimensions"
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
return cls(
|
|
102
|
+
times=times, indices=indices, left=left, right=right, variances=variances
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
def transform(self, z: np.ndarray) -> np.ndarray:
|
|
106
|
+
"""
|
|
107
|
+
Map independent normals z to Brownian increments via Brownian bridge.
|
|
108
|
+
|
|
109
|
+
Parameters
|
|
110
|
+
----------
|
|
111
|
+
z : np.ndarray
|
|
112
|
+
Array of shape (n_paths, n_steps) with independent N(0, 1)
|
|
113
|
+
samples along the second axis.
|
|
114
|
+
|
|
115
|
+
Returns
|
|
116
|
+
-------
|
|
117
|
+
np.ndarray
|
|
118
|
+
Array of Brownian increments dW of shape (n_paths, n_steps),
|
|
119
|
+
ordered chronologically in time.
|
|
120
|
+
"""
|
|
121
|
+
z = np.asarray(z, dtype=float)
|
|
122
|
+
if z.ndim != 2:
|
|
123
|
+
raise ValueError("z must be a 2D array of shape (n_paths, n_steps)")
|
|
124
|
+
|
|
125
|
+
n_paths, n_steps = z.shape
|
|
126
|
+
if n_steps != self.times.shape[0]:
|
|
127
|
+
raise ValueError(
|
|
128
|
+
f"z has {n_steps} time steps but BrownianBridge is configured "
|
|
129
|
+
f"for {self.times.shape[0]} steps."
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
W = np.zeros((n_paths, n_steps), dtype=float)
|
|
133
|
+
|
|
134
|
+
# First dimension: terminal time T
|
|
135
|
+
idx0 = self.indices[0]
|
|
136
|
+
std0 = np.sqrt(self.variances[0])
|
|
137
|
+
W[:, idx0] = std0 * z[:, 0]
|
|
138
|
+
|
|
139
|
+
# Remaining dimensions: midpoints
|
|
140
|
+
for j in range(1, n_steps):
|
|
141
|
+
k = self.indices[j]
|
|
142
|
+
l = self.left[j]
|
|
143
|
+
r = self.right[j]
|
|
144
|
+
|
|
145
|
+
t_l = 0.0 if l == -1 else self.times[l]
|
|
146
|
+
t_r = self.times[r]
|
|
147
|
+
t_m = self.times[k]
|
|
148
|
+
|
|
149
|
+
if l == -1:
|
|
150
|
+
W_l = 0.0
|
|
151
|
+
else:
|
|
152
|
+
W_l = W[:, l]
|
|
153
|
+
W_r = W[:, r]
|
|
154
|
+
|
|
155
|
+
# Conditional mean and variance for the bridge
|
|
156
|
+
denom = t_r - t_l
|
|
157
|
+
if denom <= 0.0:
|
|
158
|
+
raise ValueError("Invalid Brownian bridge interval length.")
|
|
159
|
+
mean = ((t_r - t_m) * W_l + (t_m - t_l) * W_r) / denom
|
|
160
|
+
std = np.sqrt(self.variances[j])
|
|
161
|
+
|
|
162
|
+
W[:, k] = mean + std * z[:, j]
|
|
163
|
+
|
|
164
|
+
# Convert Brownian motion values to increments
|
|
165
|
+
dW = np.empty_like(W)
|
|
166
|
+
dW[:, 0] = W[:, 0]
|
|
167
|
+
if n_steps > 1:
|
|
168
|
+
dW[:, 1:] = W[:, 1:] - W[:, :-1]
|
|
169
|
+
|
|
170
|
+
return dW
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def apply_brownian_bridge(z: np.ndarray, times: np.ndarray) -> np.ndarray:
|
|
174
|
+
"""
|
|
175
|
+
Convenience function: apply Brownian bridge to standard normals.
|
|
176
|
+
|
|
177
|
+
Parameters
|
|
178
|
+
----------
|
|
179
|
+
z : np.ndarray
|
|
180
|
+
Standard normal samples of shape (n_paths, n_steps).
|
|
181
|
+
times : np.ndarray
|
|
182
|
+
Time grid with shape (n_steps,) and strictly increasing values.
|
|
183
|
+
|
|
184
|
+
Returns
|
|
185
|
+
-------
|
|
186
|
+
np.ndarray
|
|
187
|
+
Brownian increments dW with shape (n_paths, n_steps).
|
|
188
|
+
"""
|
|
189
|
+
bridge = BrownianBridge.from_time_grid(times)
|
|
190
|
+
return bridge.transform(z)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def apply_brownian_bridge_multi_asset(z: np.ndarray, times: np.ndarray) -> np.ndarray:
|
|
194
|
+
"""
|
|
195
|
+
Apply Brownian bridge construction to multi-asset standard normals.
|
|
196
|
+
|
|
197
|
+
This function applies the Brownian bridge transformation independently
|
|
198
|
+
to each asset's time series. This is the recommended approach for
|
|
199
|
+
multi-asset QMC simulations as it preserves the low-discrepancy
|
|
200
|
+
properties of the underlying sequence for each asset's marginal
|
|
201
|
+
distribution.
|
|
202
|
+
|
|
203
|
+
Parameters
|
|
204
|
+
----------
|
|
205
|
+
z : np.ndarray
|
|
206
|
+
Standard normal samples of shape (n_assets, n_paths, n_steps).
|
|
207
|
+
Each asset's normals are in z[i, :, :].
|
|
208
|
+
times : np.ndarray
|
|
209
|
+
Time grid with shape (n_steps,) and strictly increasing values.
|
|
210
|
+
The same time grid is used for all assets.
|
|
211
|
+
|
|
212
|
+
Returns
|
|
213
|
+
-------
|
|
214
|
+
np.ndarray
|
|
215
|
+
Brownian increments dW with shape (n_assets, n_paths, n_steps),
|
|
216
|
+
where dW[i, :, :] contains the increments for asset i.
|
|
217
|
+
|
|
218
|
+
Notes
|
|
219
|
+
-----
|
|
220
|
+
The Brownian bridge is applied per-asset because:
|
|
221
|
+
1. It preserves QMC effectiveness for path-dependent payoffs
|
|
222
|
+
2. Correlation between assets is applied separately after building
|
|
223
|
+
the increments, using Cholesky decomposition
|
|
224
|
+
3. Each asset's marginal distribution benefits from the low effective
|
|
225
|
+
dimension property of Brownian bridge construction
|
|
226
|
+
|
|
227
|
+
Example
|
|
228
|
+
-------
|
|
229
|
+
>>> import numpy as np
|
|
230
|
+
>>> from asset.equity.process.qmc_brownian_bridge import (
|
|
231
|
+
... apply_brownian_bridge_multi_asset
|
|
232
|
+
... )
|
|
233
|
+
>>> n_assets, n_paths, n_steps = 3, 1000, 252
|
|
234
|
+
>>> z = np.random.standard_normal((n_assets, n_paths, n_steps))
|
|
235
|
+
>>> times = np.linspace(1/252, 1.0, n_steps)
|
|
236
|
+
>>> dW = apply_brownian_bridge_multi_asset(z, times)
|
|
237
|
+
>>> dW.shape
|
|
238
|
+
(3, 1000, 252)
|
|
239
|
+
"""
|
|
240
|
+
z = np.asarray(z, dtype=float)
|
|
241
|
+
times = np.asarray(times, dtype=float)
|
|
242
|
+
|
|
243
|
+
if z.ndim != 3:
|
|
244
|
+
raise ValueError(
|
|
245
|
+
f"z must be a 3D array of shape (n_assets, n_paths, n_steps), "
|
|
246
|
+
f"got ndim={z.ndim}"
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
n_assets, n_paths, n_steps = z.shape
|
|
250
|
+
|
|
251
|
+
if times.ndim != 1:
|
|
252
|
+
raise ValueError("times must be a 1D array")
|
|
253
|
+
if times.shape[0] != n_steps:
|
|
254
|
+
raise ValueError(
|
|
255
|
+
f"times has {times.shape[0]} elements but z has {n_steps} time steps"
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
# Create bridge once and reuse for all assets
|
|
259
|
+
bridge = BrownianBridge.from_time_grid(times)
|
|
260
|
+
|
|
261
|
+
# Apply bridge to each asset
|
|
262
|
+
dW = np.zeros_like(z)
|
|
263
|
+
for i in range(n_assets):
|
|
264
|
+
dW[i] = bridge.transform(z[i])
|
|
265
|
+
|
|
266
|
+
return dW
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def compute_step_crossing_probabilities(
|
|
270
|
+
paths: np.ndarray,
|
|
271
|
+
barrier_level: float,
|
|
272
|
+
sigma: float,
|
|
273
|
+
times: np.ndarray,
|
|
274
|
+
) -> np.ndarray:
|
|
275
|
+
"""
|
|
276
|
+
Compute step-wise barrier crossing probabilities using a Brownian bridge.
|
|
277
|
+
|
|
278
|
+
For each simulated path and each time interval, it returns the probability
|
|
279
|
+
that the path has crossed the barrier between the endpoints.
|
|
280
|
+
|
|
281
|
+
Parameters
|
|
282
|
+
----------
|
|
283
|
+
paths : np.ndarray
|
|
284
|
+
Simulated spot paths of shape (n_paths, n_steps + 1).
|
|
285
|
+
barrier_level : float
|
|
286
|
+
Barrier level in spot space.
|
|
287
|
+
sigma : float
|
|
288
|
+
Volatility used in the GBM dynamics.
|
|
289
|
+
times : np.ndarray
|
|
290
|
+
Time grid of shape (n_steps,) corresponding to the path intervals
|
|
291
|
+
(excluding t=0).
|
|
292
|
+
|
|
293
|
+
Returns
|
|
294
|
+
-------
|
|
295
|
+
np.ndarray
|
|
296
|
+
Array of shape (n_paths, n_steps) containing the crossing probability
|
|
297
|
+
per step.
|
|
298
|
+
"""
|
|
299
|
+
paths = np.asarray(paths, dtype=float)
|
|
300
|
+
times = np.asarray(times, dtype=float)
|
|
301
|
+
|
|
302
|
+
if paths.ndim != 2:
|
|
303
|
+
raise ValueError("paths must be a 2D array of shape (n_paths, n_steps + 1)")
|
|
304
|
+
if times.ndim != 1:
|
|
305
|
+
raise ValueError("times must be a 1D array of shape (n_steps,)")
|
|
306
|
+
if barrier_level <= 0.0:
|
|
307
|
+
raise ValueError("barrier_level must be positive")
|
|
308
|
+
if sigma <= 0.0:
|
|
309
|
+
raise ValueError("sigma must be positive")
|
|
310
|
+
if paths.shape[1] != times.shape[0] + 1:
|
|
311
|
+
raise ValueError(
|
|
312
|
+
"paths second dimension must be n_steps + 1 where n_steps = len(times)"
|
|
313
|
+
)
|
|
314
|
+
if np.any(np.diff(times) <= 0.0):
|
|
315
|
+
raise ValueError("times must be strictly increasing")
|
|
316
|
+
|
|
317
|
+
n_paths, n_cols = paths.shape
|
|
318
|
+
n_steps = n_cols - 1
|
|
319
|
+
|
|
320
|
+
S0 = paths[:, :-1]
|
|
321
|
+
S1 = paths[:, 1:]
|
|
322
|
+
dt = np.empty(n_steps, dtype=float)
|
|
323
|
+
dt[0] = times[0]
|
|
324
|
+
if n_steps > 1:
|
|
325
|
+
dt[1:] = np.diff(times)
|
|
326
|
+
|
|
327
|
+
# Broadcast dt to match shape of path slices
|
|
328
|
+
dt_matrix = dt.reshape(1, -1)
|
|
329
|
+
h2 = (sigma * sigma) * dt_matrix
|
|
330
|
+
|
|
331
|
+
# Initialize probabilities per step
|
|
332
|
+
prob = np.zeros_like(S0, dtype=float)
|
|
333
|
+
|
|
334
|
+
# Determine where paths are on different sides of the barrier
|
|
335
|
+
crossed_mask = ((S0 < barrier_level) & (S1 > barrier_level)) | (
|
|
336
|
+
(S0 > barrier_level) & (S1 < barrier_level)
|
|
337
|
+
)
|
|
338
|
+
touched_mask = (S0 == barrier_level) | (S1 == barrier_level)
|
|
339
|
+
|
|
340
|
+
# Opposite-side or touching endpoints imply a hit with probability 1
|
|
341
|
+
prob[crossed_mask | touched_mask] = 1.0
|
|
342
|
+
|
|
343
|
+
# Same-side endpoints: Brownian-bridge crossing probability
|
|
344
|
+
same_side = ~(crossed_mask | touched_mask)
|
|
345
|
+
if np.any(same_side):
|
|
346
|
+
log_term = safe_log(S0 / barrier_level) * safe_log(S1 / barrier_level)
|
|
347
|
+
bridge_prob = np.exp(-2.0 * log_term / h2)
|
|
348
|
+
bridge_prob = np.clip(bridge_prob, 0.0, 1.0)
|
|
349
|
+
prob[same_side] = bridge_prob[same_side]
|
|
350
|
+
|
|
351
|
+
return prob
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def compute_barrier_crossing_probabilities(
|
|
355
|
+
paths: np.ndarray,
|
|
356
|
+
barrier_level: float,
|
|
357
|
+
sigma: float,
|
|
358
|
+
times: np.ndarray,
|
|
359
|
+
) -> np.ndarray:
|
|
360
|
+
"""
|
|
361
|
+
Approximate barrier crossing probabilities using a Brownian bridge.
|
|
362
|
+
|
|
363
|
+
This utility is designed for geometric Brownian motion paths and can be
|
|
364
|
+
used by pricing engines for barrier-style options. For each simulated
|
|
365
|
+
path, it returns the probability that the path has crossed the barrier
|
|
366
|
+
between any pair of consecutive observation times.
|
|
367
|
+
|
|
368
|
+
Parameters
|
|
369
|
+
----------
|
|
370
|
+
paths : np.ndarray
|
|
371
|
+
Simulated spot paths of shape (n_paths, n_steps + 1). The second axis
|
|
372
|
+
is assumed to be ordered chronologically.
|
|
373
|
+
barrier_level : float
|
|
374
|
+
Barrier level in spot space.
|
|
375
|
+
sigma : float
|
|
376
|
+
Volatility used in the GBM dynamics.
|
|
377
|
+
times : np.ndarray
|
|
378
|
+
Time grid of shape (n_steps,) corresponding to the path intervals
|
|
379
|
+
(excluding t=0).
|
|
380
|
+
|
|
381
|
+
Returns
|
|
382
|
+
-------
|
|
383
|
+
np.ndarray
|
|
384
|
+
Array of shape (n_paths,) containing the approximate probability that
|
|
385
|
+
each path crosses the barrier at least once.
|
|
386
|
+
"""
|
|
387
|
+
prob = compute_step_crossing_probabilities(paths, barrier_level, sigma, times)
|
|
388
|
+
|
|
389
|
+
# Combine step-wise probabilities into overall crossing probability
|
|
390
|
+
# Assuming conditional independence given endpoints (standard Brownian bridge approximation)
|
|
391
|
+
no_hit_prob = np.prod(1.0 - prob, axis=1)
|
|
392
|
+
return 1.0 - no_hit_prob
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
__all__ = [
|
|
396
|
+
"BrownianBridge",
|
|
397
|
+
"apply_brownian_bridge",
|
|
398
|
+
"apply_brownian_bridge_multi_asset",
|
|
399
|
+
"compute_step_crossing_probabilities",
|
|
400
|
+
"compute_barrier_crossing_probabilities",
|
|
401
|
+
]
|