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,287 @@
|
|
|
1
|
+
"""
|
|
2
|
+
One-touch option implementation.
|
|
3
|
+
|
|
4
|
+
One-touch options are binary/digital options that pay a fixed rebate
|
|
5
|
+
if the underlying price touches a barrier at any point before expiry.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from typing import Optional, List
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from ..base_equity_product import BaseEquityProduct
|
|
12
|
+
from .observation_schedule import ObservationRecord, ObservationSchedule
|
|
13
|
+
from quantark.util.enum import (
|
|
14
|
+
BarrierDirection,
|
|
15
|
+
ObservationType,
|
|
16
|
+
ObservationAggregation,
|
|
17
|
+
TouchType,
|
|
18
|
+
)
|
|
19
|
+
from quantark.util.exceptions import ValidationError
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class OneTouchOption(BaseEquityProduct):
|
|
24
|
+
"""
|
|
25
|
+
One-touch (or no-touch) digital option.
|
|
26
|
+
|
|
27
|
+
One-touch options pay a fixed rebate if the underlying price touches
|
|
28
|
+
the barrier at any point during the option's life.
|
|
29
|
+
|
|
30
|
+
No-touch options pay a fixed rebate if the barrier is NOT touched.
|
|
31
|
+
|
|
32
|
+
Attributes:
|
|
33
|
+
barrier: Barrier price level
|
|
34
|
+
barrier_direction: UP (barrier above spot) or DOWN (barrier below spot)
|
|
35
|
+
rebate: Amount paid when condition is satisfied (default: 1.0)
|
|
36
|
+
maturity: Time to maturity in years
|
|
37
|
+
payment_at_hit: If True, rebate paid immediately on touch;
|
|
38
|
+
If False, rebate paid at expiry
|
|
39
|
+
touch_type: ONE_TOUCH or NO_TOUCH
|
|
40
|
+
observation_type: CONTINUOUS or DISCRETE monitoring
|
|
41
|
+
observation_dates: For discrete, list of observation times
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
barrier: float = 0.0
|
|
45
|
+
barrier_direction: BarrierDirection = BarrierDirection.UP
|
|
46
|
+
rebate: float = 1.0
|
|
47
|
+
maturity: float = 0.0
|
|
48
|
+
payment_at_hit: bool = True
|
|
49
|
+
touch_type: TouchType = TouchType.ONE_TOUCH
|
|
50
|
+
observation_type: ObservationType = ObservationType.CONTINUOUS
|
|
51
|
+
observation_dates: Optional[List[float]] = None
|
|
52
|
+
observation_schedule: Optional[ObservationSchedule] = None
|
|
53
|
+
exercise_date: Optional[datetime] = None
|
|
54
|
+
settlement_date: Optional[datetime] = None
|
|
55
|
+
|
|
56
|
+
def __init__(
|
|
57
|
+
self,
|
|
58
|
+
barrier: float,
|
|
59
|
+
barrier_direction: BarrierDirection,
|
|
60
|
+
maturity: Optional[float] = None,
|
|
61
|
+
exercise_date: Optional[datetime] = None,
|
|
62
|
+
settlement_date: Optional[datetime] = None,
|
|
63
|
+
rebate: float = 1.0,
|
|
64
|
+
payment_at_hit: bool = True,
|
|
65
|
+
touch_type: TouchType = TouchType.ONE_TOUCH,
|
|
66
|
+
observation_type: ObservationType = ObservationType.CONTINUOUS,
|
|
67
|
+
observation_dates: Optional[List[float]] = None,
|
|
68
|
+
observation_schedule: Optional[ObservationSchedule] = None,
|
|
69
|
+
):
|
|
70
|
+
"""
|
|
71
|
+
Initialize one-touch option.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
barrier: Barrier price level
|
|
75
|
+
barrier_direction: UP or DOWN
|
|
76
|
+
maturity: Time to maturity in years (optional if exercise_date provided)
|
|
77
|
+
exercise_date: Expiration date (optional if maturity provided)
|
|
78
|
+
settlement_date: Settlement date (optional)
|
|
79
|
+
rebate: Payment amount on touch (default: 1.0)
|
|
80
|
+
payment_at_hit: If True, pay on touch; if False, pay at expiry
|
|
81
|
+
touch_type: ONE_TOUCH (pay if touched) or NO_TOUCH (pay if not touched)
|
|
82
|
+
observation_type: CONTINUOUS or DISCRETE
|
|
83
|
+
observation_dates: For discrete, list of observation times
|
|
84
|
+
|
|
85
|
+
Raises:
|
|
86
|
+
ValidationError: If parameters are invalid
|
|
87
|
+
"""
|
|
88
|
+
# Handle maturity vs exercise_date
|
|
89
|
+
if maturity is None and exercise_date is None:
|
|
90
|
+
maturity = 0.0
|
|
91
|
+
elif maturity is None:
|
|
92
|
+
maturity = 0.0
|
|
93
|
+
|
|
94
|
+
self.barrier = barrier
|
|
95
|
+
self.barrier_direction = barrier_direction
|
|
96
|
+
self.rebate = rebate
|
|
97
|
+
self.maturity = maturity
|
|
98
|
+
self.payment_at_hit = payment_at_hit
|
|
99
|
+
self.touch_type = touch_type
|
|
100
|
+
self.observation_type = observation_type
|
|
101
|
+
self.observation_dates = observation_dates
|
|
102
|
+
self.observation_schedule = observation_schedule
|
|
103
|
+
self.exercise_date = exercise_date
|
|
104
|
+
self.settlement_date = settlement_date
|
|
105
|
+
|
|
106
|
+
self.validate()
|
|
107
|
+
|
|
108
|
+
def validate(self) -> None:
|
|
109
|
+
"""
|
|
110
|
+
Validate one-touch option parameters.
|
|
111
|
+
|
|
112
|
+
Raises:
|
|
113
|
+
ValidationError: If parameters are invalid
|
|
114
|
+
"""
|
|
115
|
+
if self.barrier <= 0:
|
|
116
|
+
raise ValidationError(f"Barrier must be positive, got {self.barrier}")
|
|
117
|
+
|
|
118
|
+
if self.rebate < 0:
|
|
119
|
+
raise ValidationError(f"Rebate must be non-negative, got {self.rebate}")
|
|
120
|
+
|
|
121
|
+
if not isinstance(self.barrier_direction, BarrierDirection):
|
|
122
|
+
raise ValidationError(
|
|
123
|
+
f"Invalid barrier direction: {self.barrier_direction}"
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
if not isinstance(self.touch_type, TouchType):
|
|
127
|
+
raise ValidationError(f"Invalid touch type: {self.touch_type}")
|
|
128
|
+
|
|
129
|
+
# Validate maturity
|
|
130
|
+
has_dates = self.exercise_date is not None
|
|
131
|
+
has_maturity = self.maturity is not None and self.maturity > 0
|
|
132
|
+
|
|
133
|
+
if not has_dates and not has_maturity:
|
|
134
|
+
raise ValidationError("Either maturity or exercise_date must be provided")
|
|
135
|
+
|
|
136
|
+
# For discrete observation, must have observation dates
|
|
137
|
+
if (
|
|
138
|
+
self.observation_type == ObservationType.DISCRETE
|
|
139
|
+
and self.observation_schedule is None
|
|
140
|
+
and (self.observation_dates is None or len(self.observation_dates) == 0)
|
|
141
|
+
):
|
|
142
|
+
raise ValidationError(
|
|
143
|
+
"Observation dates required for discrete barrier monitoring"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
# Normalize observation schedule (preferred) or legacy dates for discrete monitoring
|
|
147
|
+
if self.observation_schedule is not None:
|
|
148
|
+
if self.observation_type == ObservationType.CONTINUOUS:
|
|
149
|
+
raise ValidationError("ObservationSchedule requires DISCRETE observation_type.")
|
|
150
|
+
normalized_schedule = ObservationSchedule(
|
|
151
|
+
records=[
|
|
152
|
+
ObservationRecord(
|
|
153
|
+
observation_time=rec.observation_time,
|
|
154
|
+
observation_date=rec.observation_date,
|
|
155
|
+
barrier=rec.barrier if rec.barrier is not None else self.barrier,
|
|
156
|
+
payoff=rec.payoff if rec.payoff is not None else self.rebate,
|
|
157
|
+
return_rate=rec.return_rate,
|
|
158
|
+
)
|
|
159
|
+
for rec in self.observation_schedule.records
|
|
160
|
+
],
|
|
161
|
+
aggregation_mode=self.observation_schedule.aggregation_mode,
|
|
162
|
+
frequency=self.observation_schedule.frequency,
|
|
163
|
+
)
|
|
164
|
+
normalized_schedule.validate(require_single=True)
|
|
165
|
+
self.observation_schedule = normalized_schedule
|
|
166
|
+
if self.observation_schedule.times:
|
|
167
|
+
self.observation_dates = self.observation_schedule.times
|
|
168
|
+
self.observation_type = ObservationType.DISCRETE
|
|
169
|
+
elif self.observation_type == ObservationType.DISCRETE:
|
|
170
|
+
self.observation_schedule = ObservationSchedule.from_legacy(
|
|
171
|
+
observation_dates=self.observation_dates or [],
|
|
172
|
+
default_barrier=self.barrier,
|
|
173
|
+
default_payoff=self.rebate,
|
|
174
|
+
aggregation_mode=ObservationAggregation.STOP_FIRST_HIT,
|
|
175
|
+
)
|
|
176
|
+
self.observation_dates = self.observation_schedule.times
|
|
177
|
+
|
|
178
|
+
def get_maturity(self, pricing_env=None) -> float:
|
|
179
|
+
"""
|
|
180
|
+
Get time to maturity in years.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
pricing_env: Pricing environment (required if using dates)
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
Time to maturity in years
|
|
187
|
+
"""
|
|
188
|
+
if self.exercise_date is not None:
|
|
189
|
+
if pricing_env is None:
|
|
190
|
+
raise ValidationError(
|
|
191
|
+
"PricingEnvironment required for date-based maturity calculation"
|
|
192
|
+
)
|
|
193
|
+
from quantark.util.calendar import calculate_year_fraction
|
|
194
|
+
|
|
195
|
+
return calculate_year_fraction(
|
|
196
|
+
pricing_env.valuation_date,
|
|
197
|
+
self.exercise_date,
|
|
198
|
+
pricing_env.day_count_convention,
|
|
199
|
+
pricing_env.bus_days_in_year,
|
|
200
|
+
calendar=getattr(pricing_env, "calendar", None),
|
|
201
|
+
)
|
|
202
|
+
else:
|
|
203
|
+
return self.maturity
|
|
204
|
+
|
|
205
|
+
def time_shift(self, time_bump: float, bumped_date: datetime, pricing_env) -> bool:
|
|
206
|
+
"""Shift observation schedule and maturity for theta bumping."""
|
|
207
|
+
schedule = getattr(self, "observation_schedule", None)
|
|
208
|
+
if schedule is not None:
|
|
209
|
+
if schedule.uses_dates():
|
|
210
|
+
pricing_env.valuation_date = bumped_date
|
|
211
|
+
bumped_schedule = schedule.time_shift(time_bump, bumped_date)
|
|
212
|
+
if bumped_schedule is None:
|
|
213
|
+
return True
|
|
214
|
+
self.observation_schedule = bumped_schedule
|
|
215
|
+
if hasattr(self, "observation_dates") and bumped_schedule.uses_times():
|
|
216
|
+
self.observation_dates = bumped_schedule.times
|
|
217
|
+
|
|
218
|
+
if getattr(self, "exercise_date", None) is None:
|
|
219
|
+
if getattr(self, "maturity", None) is not None:
|
|
220
|
+
self.maturity -= time_bump
|
|
221
|
+
else:
|
|
222
|
+
pricing_env.valuation_date = bumped_date
|
|
223
|
+
|
|
224
|
+
return False
|
|
225
|
+
|
|
226
|
+
def get_payoff(self, spot: float, touched: bool = False) -> float:
|
|
227
|
+
"""
|
|
228
|
+
Calculate the option payoff.
|
|
229
|
+
|
|
230
|
+
For one-touch: pays rebate if touched
|
|
231
|
+
For no-touch: pays rebate if NOT touched
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
spot: Spot price (not used for one-touch, kept for interface)
|
|
235
|
+
touched: Whether the barrier was touched
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
Option payoff
|
|
239
|
+
"""
|
|
240
|
+
if self.touch_type == TouchType.ONE_TOUCH:
|
|
241
|
+
return self.rebate if touched else 0.0
|
|
242
|
+
else: # NO_TOUCH
|
|
243
|
+
return self.rebate if not touched else 0.0
|
|
244
|
+
|
|
245
|
+
@property
|
|
246
|
+
def is_one_touch(self) -> bool:
|
|
247
|
+
"""Check if this is a one-touch option."""
|
|
248
|
+
return self.touch_type == TouchType.ONE_TOUCH
|
|
249
|
+
|
|
250
|
+
@property
|
|
251
|
+
def is_no_touch(self) -> bool:
|
|
252
|
+
"""Check if this is a no-touch option."""
|
|
253
|
+
return self.touch_type == TouchType.NO_TOUCH
|
|
254
|
+
|
|
255
|
+
@property
|
|
256
|
+
def is_up_barrier(self) -> bool:
|
|
257
|
+
"""Check if barrier is above spot."""
|
|
258
|
+
return self.barrier_direction == BarrierDirection.UP
|
|
259
|
+
|
|
260
|
+
@property
|
|
261
|
+
def is_down_barrier(self) -> bool:
|
|
262
|
+
"""Check if barrier is below spot."""
|
|
263
|
+
return self.barrier_direction == BarrierDirection.DOWN
|
|
264
|
+
|
|
265
|
+
def is_barrier_hit(self, spot: float) -> bool:
|
|
266
|
+
"""
|
|
267
|
+
Check if the barrier would be hit at a given spot price.
|
|
268
|
+
|
|
269
|
+
Args:
|
|
270
|
+
spot: Spot price to check
|
|
271
|
+
|
|
272
|
+
Returns:
|
|
273
|
+
True if barrier is hit
|
|
274
|
+
"""
|
|
275
|
+
if self.is_up_barrier:
|
|
276
|
+
return spot >= self.barrier
|
|
277
|
+
else:
|
|
278
|
+
return spot <= self.barrier
|
|
279
|
+
|
|
280
|
+
def __repr__(self):
|
|
281
|
+
touch_str = "OneTouch" if self.is_one_touch else "NoTouch"
|
|
282
|
+
dir_str = "Up" if self.is_up_barrier else "Down"
|
|
283
|
+
return (
|
|
284
|
+
f"{touch_str}Option("
|
|
285
|
+
f"B={self.barrier:.2f}, {dir_str}, "
|
|
286
|
+
f"rebate={self.rebate:.2f}, T={self.maturity:.4f})"
|
|
287
|
+
)
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration classes for Phoenix options.
|
|
3
|
+
|
|
4
|
+
Phoenix options are autocallable structured products with periodic coupon payments
|
|
5
|
+
when spot exceeds a coupon barrier at observation dates.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from typing import List, Optional, Union
|
|
10
|
+
|
|
11
|
+
from quantark.util.enum import CouponPayType
|
|
12
|
+
from quantark.util.calendar.day_counter import DayCountConvention
|
|
13
|
+
from quantark.util.exceptions import ValidationError
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class CouponBarrierConfig:
|
|
18
|
+
"""
|
|
19
|
+
Configuration for coupon barrier settings (Phoenix-specific).
|
|
20
|
+
|
|
21
|
+
The coupon barrier determines when periodic coupons are paid. At each observation
|
|
22
|
+
date, if the spot price is above (or below for reverse) the coupon barrier, a
|
|
23
|
+
coupon is paid based on the coupon_rate and day count convention.
|
|
24
|
+
|
|
25
|
+
Attributes:
|
|
26
|
+
coupon_barrier: Coupon barrier level(s). Can be scalar or time-varying (list).
|
|
27
|
+
For standard Phoenix: pays coupon when spot >= coupon_barrier.
|
|
28
|
+
For reverse Phoenix: pays coupon when spot <= coupon_barrier.
|
|
29
|
+
coupon_rate: Per-period coupon rate (e.g., 0.01 for 1% per period).
|
|
30
|
+
coupon_pay_type: INSTANT (pay at observation) or EXPIRY (accumulate to maturity).
|
|
31
|
+
day_count_convention: Day count convention for year fraction calculation.
|
|
32
|
+
Supported: ACT_365, THIRTY_360_US, THIRTY_360_EUROPEAN, etc.
|
|
33
|
+
memory_coupon: If True, missed coupons accumulate and are paid when barrier
|
|
34
|
+
is hit later. If False, only current period coupon is paid.
|
|
35
|
+
fixed_coupon_year_fraction: Optional fixed year fraction used for each coupon
|
|
36
|
+
period (e.g., 1/12 for equal monthly coupons). If None, engines
|
|
37
|
+
use observation-time differences.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
# Coupon barrier level(s)
|
|
41
|
+
coupon_barrier: Union[float, List[float]]
|
|
42
|
+
|
|
43
|
+
# Coupon rate per period
|
|
44
|
+
coupon_rate: float = 0.01
|
|
45
|
+
|
|
46
|
+
# Payment timing
|
|
47
|
+
coupon_pay_type: CouponPayType = CouponPayType.INSTANT
|
|
48
|
+
|
|
49
|
+
# Day count convention for year fraction calculation
|
|
50
|
+
day_count_convention: DayCountConvention = DayCountConvention.ACT_365
|
|
51
|
+
|
|
52
|
+
# Memory coupon feature
|
|
53
|
+
memory_coupon: bool = True
|
|
54
|
+
|
|
55
|
+
# Optional fixed accrual per coupon period (e.g. 1/12)
|
|
56
|
+
fixed_coupon_year_fraction: Optional[float] = None
|
|
57
|
+
|
|
58
|
+
def __post_init__(self):
|
|
59
|
+
"""Validate configuration after initialization."""
|
|
60
|
+
# Validate coupon_barrier is positive
|
|
61
|
+
self._validate_barrier_positive(self.coupon_barrier, "coupon_barrier")
|
|
62
|
+
|
|
63
|
+
# Validate coupon_rate is non-negative
|
|
64
|
+
if not isinstance(self.coupon_rate, (int, float)):
|
|
65
|
+
raise ValidationError(
|
|
66
|
+
f"coupon_rate must be a number, got {type(self.coupon_rate)}"
|
|
67
|
+
)
|
|
68
|
+
if self.coupon_rate < 0:
|
|
69
|
+
raise ValidationError(f"coupon_rate must be non-negative, got {self.coupon_rate}")
|
|
70
|
+
|
|
71
|
+
# Validate coupon_pay_type is enum
|
|
72
|
+
if not isinstance(self.coupon_pay_type, CouponPayType):
|
|
73
|
+
raise ValidationError(
|
|
74
|
+
f"coupon_pay_type must be CouponPayType, got {type(self.coupon_pay_type)}"
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Validate day_count_convention is enum
|
|
78
|
+
if not isinstance(self.day_count_convention, DayCountConvention):
|
|
79
|
+
raise ValidationError(
|
|
80
|
+
f"day_count_convention must be DayCountConvention, "
|
|
81
|
+
f"got {type(self.day_count_convention)}"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# Validate memory_coupon is boolean
|
|
85
|
+
if not isinstance(self.memory_coupon, bool):
|
|
86
|
+
raise ValidationError(
|
|
87
|
+
f"memory_coupon must be boolean, got {type(self.memory_coupon)}"
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# Validate fixed coupon accrual if provided
|
|
91
|
+
if self.fixed_coupon_year_fraction is not None:
|
|
92
|
+
if not isinstance(self.fixed_coupon_year_fraction, (int, float)):
|
|
93
|
+
raise ValidationError(
|
|
94
|
+
"fixed_coupon_year_fraction must be numeric when provided, "
|
|
95
|
+
f"got {type(self.fixed_coupon_year_fraction)}"
|
|
96
|
+
)
|
|
97
|
+
if self.fixed_coupon_year_fraction <= 0:
|
|
98
|
+
raise ValidationError(
|
|
99
|
+
"fixed_coupon_year_fraction must be positive, "
|
|
100
|
+
f"got {self.fixed_coupon_year_fraction}"
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
@staticmethod
|
|
104
|
+
def _validate_barrier_positive(
|
|
105
|
+
barrier: Union[float, List[float]], name: str
|
|
106
|
+
) -> None:
|
|
107
|
+
"""Validate that barrier level(s) are positive."""
|
|
108
|
+
if isinstance(barrier, list):
|
|
109
|
+
if not barrier:
|
|
110
|
+
raise ValidationError(f"{name} list cannot be empty")
|
|
111
|
+
for i, b in enumerate(barrier):
|
|
112
|
+
if not isinstance(b, (int, float)) or b <= 0:
|
|
113
|
+
raise ValidationError(f"{name}[{i}] must be positive number, got {b}")
|
|
114
|
+
else:
|
|
115
|
+
if not isinstance(barrier, (int, float)) or barrier <= 0:
|
|
116
|
+
raise ValidationError(f"{name} must be positive number, got {barrier}")
|