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,439 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PDE solver for one-touch (and no-touch) options.
|
|
3
|
+
|
|
4
|
+
Implements the finite difference method for digital barrier options
|
|
5
|
+
that pay a fixed rebate on touching (or not touching) a barrier.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Dict, Optional, List, Set
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
from quantark.asset.equity.product.base_equity_product import BaseEquityProduct
|
|
12
|
+
from quantark.asset.equity.product.option.one_touch_option import OneTouchOption
|
|
13
|
+
from quantark.asset.equity.param import PDEParams
|
|
14
|
+
from quantark.priceenv import PricingEnvironment
|
|
15
|
+
from quantark.util.enum import ObservationType, ObservationAggregation, TouchType
|
|
16
|
+
from quantark.util.exceptions import PricingError
|
|
17
|
+
|
|
18
|
+
from .base_pde_solver import BasePDESolver
|
|
19
|
+
from .spatial_grid import SpatialGrid
|
|
20
|
+
from .time_grid import TimeGrid
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class OneTouchPDESolver(BasePDESolver):
|
|
24
|
+
"""
|
|
25
|
+
PDE solver for one-touch and no-touch options.
|
|
26
|
+
|
|
27
|
+
One-touch options pay a fixed rebate if the barrier is touched.
|
|
28
|
+
No-touch options pay a fixed rebate if the barrier is NOT touched.
|
|
29
|
+
|
|
30
|
+
For one-touch (payment at expiry):
|
|
31
|
+
Terminal condition: rebate at barrier side, 0 elsewhere
|
|
32
|
+
Boundary at barrier: rebate (discounted if payment at expiry)
|
|
33
|
+
|
|
34
|
+
For no-touch:
|
|
35
|
+
Terminal condition: rebate inside (away from barrier), 0 at barrier
|
|
36
|
+
Boundary at barrier: 0
|
|
37
|
+
|
|
38
|
+
For one-touch with payment at hit:
|
|
39
|
+
We solve for the present value of the expected rebate,
|
|
40
|
+
with the barrier being an absorbing boundary with value = rebate.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def __init__(self, params: Optional[PDEParams] = None):
|
|
44
|
+
"""
|
|
45
|
+
Initialize one-touch option PDE solver.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
params: PDE engine configuration parameters
|
|
49
|
+
"""
|
|
50
|
+
super().__init__(params)
|
|
51
|
+
self._observation_indices: Set[int] = set()
|
|
52
|
+
self._schedule_records: Dict[int, List] = {}
|
|
53
|
+
self._schedule_aggregation: ObservationAggregation = (
|
|
54
|
+
ObservationAggregation.STOP_FIRST_HIT
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
def price(
|
|
58
|
+
self, product: BaseEquityProduct, pricing_env: PricingEnvironment
|
|
59
|
+
) -> float:
|
|
60
|
+
"""
|
|
61
|
+
Price a one-touch or no-touch option using PDE method.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
product: One-touch option
|
|
65
|
+
pricing_env: Pricing environment
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
Option price
|
|
69
|
+
|
|
70
|
+
Raises:
|
|
71
|
+
PricingError: If product is not a one-touch option
|
|
72
|
+
"""
|
|
73
|
+
if not isinstance(product, OneTouchOption):
|
|
74
|
+
raise PricingError(
|
|
75
|
+
f"OneTouchPDESolver only supports OneTouchOption, "
|
|
76
|
+
f"got {type(product).__name__}"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Check if barrier already hit
|
|
80
|
+
spot = pricing_env.spot
|
|
81
|
+
if product.is_barrier_hit(spot):
|
|
82
|
+
if product.is_one_touch:
|
|
83
|
+
# Already touched, immediate rebate
|
|
84
|
+
return product.rebate
|
|
85
|
+
else:
|
|
86
|
+
# No-touch already failed
|
|
87
|
+
return 0.0
|
|
88
|
+
|
|
89
|
+
return super().price(product, pricing_env)
|
|
90
|
+
|
|
91
|
+
def calculate_greeks(
|
|
92
|
+
self, product: BaseEquityProduct, pricing_env: PricingEnvironment
|
|
93
|
+
) -> Dict[str, float]:
|
|
94
|
+
"""
|
|
95
|
+
Calculate Greeks for a one-touch or no-touch option.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
product: One-touch option
|
|
99
|
+
pricing_env: Pricing environment
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
Dictionary with price, delta, gamma
|
|
103
|
+
|
|
104
|
+
Raises:
|
|
105
|
+
PricingError: If product is not a one-touch option
|
|
106
|
+
"""
|
|
107
|
+
if not isinstance(product, OneTouchOption):
|
|
108
|
+
raise PricingError(
|
|
109
|
+
f"OneTouchPDESolver only supports OneTouchOption, "
|
|
110
|
+
f"got {type(product).__name__}"
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
spot = pricing_env.spot
|
|
114
|
+
tau = product.get_maturity(pricing_env)
|
|
115
|
+
|
|
116
|
+
# Handle expired case
|
|
117
|
+
if tau <= 0:
|
|
118
|
+
return {
|
|
119
|
+
"price": self._calculate_intrinsic(product, spot),
|
|
120
|
+
"delta": self._intrinsic_delta(product, spot),
|
|
121
|
+
"gamma": 0.0,
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
# Check if barrier already hit
|
|
125
|
+
if product.is_barrier_hit(spot):
|
|
126
|
+
if product.is_one_touch:
|
|
127
|
+
# Already touched, fixed rebate (delta=gamma=0)
|
|
128
|
+
return {"price": product.rebate, "delta": 0.0, "gamma": 0.0}
|
|
129
|
+
else:
|
|
130
|
+
# No-touch already failed
|
|
131
|
+
return {"price": 0.0, "delta": 0.0, "gamma": 0.0}
|
|
132
|
+
|
|
133
|
+
return super().calculate_greeks(product, pricing_env)
|
|
134
|
+
|
|
135
|
+
def set_terminal_condition(
|
|
136
|
+
self,
|
|
137
|
+
grid: np.ndarray,
|
|
138
|
+
x_vec: np.ndarray,
|
|
139
|
+
s_vec: np.ndarray,
|
|
140
|
+
product: BaseEquityProduct,
|
|
141
|
+
pricing_env: PricingEnvironment,
|
|
142
|
+
) -> None:
|
|
143
|
+
"""
|
|
144
|
+
Set the terminal condition at maturity.
|
|
145
|
+
|
|
146
|
+
For one-touch (payment at expiry):
|
|
147
|
+
- If barrier not touched by expiry, one-touch pays 0
|
|
148
|
+
- This is handled by solving backwards
|
|
149
|
+
|
|
150
|
+
For no-touch:
|
|
151
|
+
- Pays rebate at expiry if barrier was never touched
|
|
152
|
+
- So terminal is rebate away from barrier, 0 at barrier
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
grid: Solution grid [num_x, num_t]
|
|
156
|
+
x_vec: Log-price grid points
|
|
157
|
+
s_vec: Price grid points
|
|
158
|
+
product: One-touch option
|
|
159
|
+
pricing_env: Pricing environment
|
|
160
|
+
"""
|
|
161
|
+
barrier = product.barrier
|
|
162
|
+
rebate = product.rebate
|
|
163
|
+
|
|
164
|
+
if product.is_one_touch:
|
|
165
|
+
# One-touch: at maturity, if we haven't touched, we get 0
|
|
166
|
+
# But at barrier, we get rebate (this is set in boundary conditions)
|
|
167
|
+
grid[:, -1] = 0.0
|
|
168
|
+
|
|
169
|
+
# Set barrier boundary to rebate value
|
|
170
|
+
if product.is_up_barrier:
|
|
171
|
+
grid[s_vec >= barrier, -1] = rebate
|
|
172
|
+
else:
|
|
173
|
+
grid[s_vec <= barrier, -1] = rebate
|
|
174
|
+
else:
|
|
175
|
+
# No-touch: pays rebate at expiry if never touched
|
|
176
|
+
# Terminal is rebate everywhere except at barrier
|
|
177
|
+
grid[:, -1] = rebate
|
|
178
|
+
|
|
179
|
+
# Zero at barrier (already touched)
|
|
180
|
+
if product.is_up_barrier:
|
|
181
|
+
grid[s_vec >= barrier, -1] = 0.0
|
|
182
|
+
else:
|
|
183
|
+
grid[s_vec <= barrier, -1] = 0.0
|
|
184
|
+
|
|
185
|
+
def set_boundary_conditions(
|
|
186
|
+
self,
|
|
187
|
+
grid: np.ndarray,
|
|
188
|
+
x_vec: np.ndarray,
|
|
189
|
+
s_vec: np.ndarray,
|
|
190
|
+
t_idx: int,
|
|
191
|
+
tau: float,
|
|
192
|
+
product: BaseEquityProduct,
|
|
193
|
+
pricing_env: PricingEnvironment,
|
|
194
|
+
) -> None:
|
|
195
|
+
"""
|
|
196
|
+
Set boundary conditions at spatial edges.
|
|
197
|
+
|
|
198
|
+
For one-touch:
|
|
199
|
+
- Barrier boundary: rebate (discounted if payment at expiry)
|
|
200
|
+
- Far boundary: 0 (never reach barrier from there)
|
|
201
|
+
|
|
202
|
+
For no-touch:
|
|
203
|
+
- Barrier boundary: 0 (touched = failed)
|
|
204
|
+
- Far boundary: discounted rebate
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
grid: Solution grid [num_x, num_t]
|
|
208
|
+
x_vec: Log-price grid points
|
|
209
|
+
s_vec: Price grid points
|
|
210
|
+
t_idx: Current time index
|
|
211
|
+
tau: Time remaining to maturity
|
|
212
|
+
product: One-touch option
|
|
213
|
+
pricing_env: Pricing environment
|
|
214
|
+
"""
|
|
215
|
+
rebate = product.rebate
|
|
216
|
+
r = pricing_env.get_rate(tau) if tau > 0 else 0.0
|
|
217
|
+
|
|
218
|
+
# Discount factor
|
|
219
|
+
df = np.exp(-r * tau) if tau > 0 else 1.0
|
|
220
|
+
|
|
221
|
+
if product.is_one_touch:
|
|
222
|
+
if product.payment_at_hit:
|
|
223
|
+
# Pay immediately on hit - boundary is full rebate
|
|
224
|
+
barrier_value = rebate
|
|
225
|
+
else:
|
|
226
|
+
# Pay at expiry - discount the rebate
|
|
227
|
+
barrier_value = rebate * df
|
|
228
|
+
|
|
229
|
+
far_value = 0.0 # Too far from barrier to ever reach it
|
|
230
|
+
else:
|
|
231
|
+
# No-touch
|
|
232
|
+
barrier_value = 0.0 # Touched = no payout
|
|
233
|
+
far_value = rebate * df # Never touch = get discounted rebate
|
|
234
|
+
|
|
235
|
+
if product.is_up_barrier:
|
|
236
|
+
# Up barrier: upper boundary is barrier
|
|
237
|
+
grid[0, t_idx] = far_value
|
|
238
|
+
grid[-1, t_idx] = barrier_value
|
|
239
|
+
else:
|
|
240
|
+
# Down barrier: lower boundary is barrier
|
|
241
|
+
grid[0, t_idx] = barrier_value
|
|
242
|
+
grid[-1, t_idx] = far_value
|
|
243
|
+
|
|
244
|
+
def _apply_step_modifications(
|
|
245
|
+
self,
|
|
246
|
+
grid: np.ndarray,
|
|
247
|
+
x_vec: np.ndarray,
|
|
248
|
+
s_vec: np.ndarray,
|
|
249
|
+
t_idx: int,
|
|
250
|
+
tau: float,
|
|
251
|
+
product: BaseEquityProduct,
|
|
252
|
+
pricing_env: PricingEnvironment,
|
|
253
|
+
) -> None:
|
|
254
|
+
"""
|
|
255
|
+
Apply barrier checks at each time step.
|
|
256
|
+
|
|
257
|
+
For discrete monitoring, only check at observation times.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
grid: Solution grid
|
|
261
|
+
x_vec: Log-price grid points
|
|
262
|
+
s_vec: Price grid points
|
|
263
|
+
t_idx: Current time index
|
|
264
|
+
tau: Time remaining to maturity
|
|
265
|
+
product: One-touch option
|
|
266
|
+
pricing_env: Pricing environment
|
|
267
|
+
"""
|
|
268
|
+
# For discrete monitoring, only check at observation times
|
|
269
|
+
if product.observation_type == ObservationType.DISCRETE:
|
|
270
|
+
if t_idx not in self._observation_indices:
|
|
271
|
+
return
|
|
272
|
+
|
|
273
|
+
barrier = product.barrier
|
|
274
|
+
rebate = product.rebate
|
|
275
|
+
r = pricing_env.get_rate(tau) if tau > 0 else 0.0
|
|
276
|
+
df = np.exp(-r * tau) if tau > 0 else 1.0
|
|
277
|
+
|
|
278
|
+
schedule_records = self._schedule_records.get(t_idx)
|
|
279
|
+
if schedule_records:
|
|
280
|
+
for rec in schedule_records:
|
|
281
|
+
barrier = rec.barrier if rec.barrier is not None else product.barrier
|
|
282
|
+
payoff = rec.payoff
|
|
283
|
+
if product.is_one_touch:
|
|
284
|
+
barrier_value = payoff if product.payment_at_hit else payoff * df
|
|
285
|
+
else:
|
|
286
|
+
barrier_value = 0.0
|
|
287
|
+
if self._schedule_aggregation == ObservationAggregation.ACCUMULATE:
|
|
288
|
+
if product.is_up_barrier:
|
|
289
|
+
grid[s_vec >= barrier, t_idx] += barrier_value
|
|
290
|
+
else:
|
|
291
|
+
grid[s_vec <= barrier, t_idx] += barrier_value
|
|
292
|
+
else:
|
|
293
|
+
if product.is_up_barrier:
|
|
294
|
+
grid[s_vec >= barrier, t_idx] = barrier_value
|
|
295
|
+
else:
|
|
296
|
+
grid[s_vec <= barrier, t_idx] = barrier_value
|
|
297
|
+
return
|
|
298
|
+
return
|
|
299
|
+
|
|
300
|
+
if product.is_one_touch:
|
|
301
|
+
if product.payment_at_hit:
|
|
302
|
+
barrier_value = rebate
|
|
303
|
+
else:
|
|
304
|
+
barrier_value = rebate * df
|
|
305
|
+
else:
|
|
306
|
+
barrier_value = 0.0
|
|
307
|
+
|
|
308
|
+
# Apply barrier value where barrier is hit
|
|
309
|
+
if product.is_up_barrier:
|
|
310
|
+
grid[s_vec >= barrier, t_idx] = barrier_value
|
|
311
|
+
else:
|
|
312
|
+
grid[s_vec <= barrier, t_idx] = barrier_value
|
|
313
|
+
|
|
314
|
+
def _build_grids(
|
|
315
|
+
self,
|
|
316
|
+
product: BaseEquityProduct,
|
|
317
|
+
pricing_env: PricingEnvironment,
|
|
318
|
+
spot: float,
|
|
319
|
+
sigma: float,
|
|
320
|
+
tau: float,
|
|
321
|
+
r: float,
|
|
322
|
+
q: float,
|
|
323
|
+
):
|
|
324
|
+
"""Build grids for one-touch option."""
|
|
325
|
+
params: PDEParams = self.params
|
|
326
|
+
barrier = product.barrier
|
|
327
|
+
|
|
328
|
+
# Use volatility-based bounds with wider range for barrier options
|
|
329
|
+
# This ensures proper probability capture for touch/no-touch dynamics
|
|
330
|
+
s_min, s_max = SpatialGrid.calculate_auto_bounds(
|
|
331
|
+
spot,
|
|
332
|
+
sigma,
|
|
333
|
+
tau,
|
|
334
|
+
r,
|
|
335
|
+
q,
|
|
336
|
+
barriers=[barrier],
|
|
337
|
+
num_std=5.0, # Wider range for barrier options
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
# Ensure barrier is at the appropriate boundary edge
|
|
341
|
+
if product.is_up_barrier:
|
|
342
|
+
# Up barrier: upper boundary should be at/beyond barrier
|
|
343
|
+
s_max = max(s_max, barrier * 1.01)
|
|
344
|
+
else:
|
|
345
|
+
# Down barrier: lower boundary should be at/beyond barrier
|
|
346
|
+
s_min = min(s_min, barrier * 0.99)
|
|
347
|
+
|
|
348
|
+
# Override with user bounds if specified
|
|
349
|
+
if params.s_min > 0:
|
|
350
|
+
s_min = params.s_min
|
|
351
|
+
if params.s_max > 0:
|
|
352
|
+
s_max = params.s_max
|
|
353
|
+
|
|
354
|
+
# Build spatial grid with concentration at barrier
|
|
355
|
+
critical_points = [barrier]
|
|
356
|
+
|
|
357
|
+
x_vec, s_vec, dx_vec = SpatialGrid.build(
|
|
358
|
+
s_min,
|
|
359
|
+
s_max,
|
|
360
|
+
params.grid_size,
|
|
361
|
+
critical_points=critical_points,
|
|
362
|
+
use_adaptive=params.adaptive_grid,
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
# Get event times
|
|
366
|
+
event_times = self._get_event_times(product, tau)
|
|
367
|
+
|
|
368
|
+
# Build time grid
|
|
369
|
+
t_vec, dt_vec = TimeGrid.build(
|
|
370
|
+
tau,
|
|
371
|
+
params.time_steps,
|
|
372
|
+
method=params.time_grid_type,
|
|
373
|
+
event_times=event_times,
|
|
374
|
+
grade_exponent=params.grade_exponent,
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
# Setup observation indices for discrete monitoring
|
|
378
|
+
self._observation_indices.clear()
|
|
379
|
+
self._schedule_records.clear()
|
|
380
|
+
self._schedule_aggregation = ObservationAggregation.STOP_FIRST_HIT
|
|
381
|
+
schedule = getattr(product, "observation_schedule", None)
|
|
382
|
+
if schedule is not None:
|
|
383
|
+
resolved_records = schedule.resolve(
|
|
384
|
+
pricing_env=pricing_env,
|
|
385
|
+
default_barrier=product.barrier,
|
|
386
|
+
default_payoff=product.rebate,
|
|
387
|
+
require_single=True,
|
|
388
|
+
)
|
|
389
|
+
self._schedule_aggregation = schedule.aggregation_mode
|
|
390
|
+
if self._schedule_aggregation in (
|
|
391
|
+
ObservationAggregation.BEST,
|
|
392
|
+
ObservationAggregation.WORST,
|
|
393
|
+
):
|
|
394
|
+
raise PricingError(
|
|
395
|
+
f"PDE solver does not support aggregation mode {self._schedule_aggregation.value}"
|
|
396
|
+
)
|
|
397
|
+
for rec in resolved_records:
|
|
398
|
+
if 0 < rec.observation_time < tau:
|
|
399
|
+
idx = np.argmin(np.abs(t_vec - rec.observation_time))
|
|
400
|
+
self._observation_indices.add(idx)
|
|
401
|
+
self._schedule_records.setdefault(idx, []).append(rec)
|
|
402
|
+
elif (
|
|
403
|
+
product.observation_type == ObservationType.DISCRETE
|
|
404
|
+
and product.observation_dates is not None
|
|
405
|
+
):
|
|
406
|
+
for obs_time in product.observation_dates:
|
|
407
|
+
if 0 < obs_time < tau:
|
|
408
|
+
idx = np.argmin(np.abs(t_vec - obs_time))
|
|
409
|
+
self._observation_indices.add(idx)
|
|
410
|
+
|
|
411
|
+
return x_vec, s_vec, dx_vec, t_vec, dt_vec
|
|
412
|
+
|
|
413
|
+
def get_critical_points(
|
|
414
|
+
self, product: BaseEquityProduct, pricing_env: PricingEnvironment
|
|
415
|
+
) -> List[float]:
|
|
416
|
+
"""
|
|
417
|
+
Get critical prices for grid concentration.
|
|
418
|
+
|
|
419
|
+
For one-touch, only the barrier is critical.
|
|
420
|
+
|
|
421
|
+
Args:
|
|
422
|
+
product: One-touch option
|
|
423
|
+
pricing_env: Pricing environment
|
|
424
|
+
|
|
425
|
+
Returns:
|
|
426
|
+
List containing the barrier
|
|
427
|
+
"""
|
|
428
|
+
points = [product.barrier]
|
|
429
|
+
schedule = getattr(product, "observation_schedule", None)
|
|
430
|
+
if schedule is not None:
|
|
431
|
+
for rec in schedule.records:
|
|
432
|
+
if rec.barrier is not None:
|
|
433
|
+
points.append(rec.barrier)
|
|
434
|
+
# sort and make unique before return
|
|
435
|
+
points = sorted(set(points))
|
|
436
|
+
return points
|
|
437
|
+
|
|
438
|
+
def __repr__(self):
|
|
439
|
+
return "OneTouchPDESolver()"
|