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,436 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Risk-free rate curve representations.
|
|
3
|
+
"""
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import List, Tuple
|
|
7
|
+
import math
|
|
8
|
+
from quantark.util.exceptions import ValidationError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class RateCurve(ABC):
|
|
12
|
+
"""
|
|
13
|
+
Abstract base class for risk-free rate curves.
|
|
14
|
+
|
|
15
|
+
Rate curves provide risk-free interest rates as a function of maturity.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
@abstractmethod
|
|
19
|
+
def get_rate(self, time_to_maturity: float) -> float:
|
|
20
|
+
"""
|
|
21
|
+
Get risk-free rate for given maturity.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
time_to_maturity: Time to maturity in years
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
Annualized risk-free rate (continuously compounded)
|
|
28
|
+
"""
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
@abstractmethod
|
|
32
|
+
def get_discount_factor(self, time_to_maturity: float) -> float:
|
|
33
|
+
"""
|
|
34
|
+
Get discount factor for given maturity.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
time_to_maturity: Time to maturity in years
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Discount factor exp(-r*T)
|
|
41
|
+
"""
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
def get_forward_rate(self, t1: float, t2: float) -> float:
|
|
45
|
+
"""
|
|
46
|
+
Calculate forward rate between two times.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
t1: Start time (years)
|
|
50
|
+
t2: End time (years)
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
Forward rate from t1 to t2
|
|
54
|
+
"""
|
|
55
|
+
if t2 <= t1:
|
|
56
|
+
raise ValidationError(f"t2 ({t2}) must be greater than t1 ({t1})")
|
|
57
|
+
|
|
58
|
+
df1 = self.get_discount_factor(t1)
|
|
59
|
+
df2 = self.get_discount_factor(t2)
|
|
60
|
+
|
|
61
|
+
return -math.log(df2 / df1) / (t2 - t1)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class ParallelShiftRateCurve(RateCurve):
|
|
65
|
+
"""
|
|
66
|
+
Parallel shift wrapper for a base rate curve.
|
|
67
|
+
|
|
68
|
+
Applies a constant additive shift to continuously-compounded rates.
|
|
69
|
+
Equivalent to shifting discount factors by exp(-shift * T):
|
|
70
|
+
DF_shift(T) = DF_base(T) * exp(-shift * T)
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
def __init__(self, base_curve: RateCurve, shift: float):
|
|
74
|
+
if base_curve is None:
|
|
75
|
+
raise ValidationError("base_curve is required")
|
|
76
|
+
self.base_curve = base_curve
|
|
77
|
+
self.shift = shift
|
|
78
|
+
|
|
79
|
+
def get_discount_factor(self, time_to_maturity: float) -> float:
|
|
80
|
+
if time_to_maturity < 0:
|
|
81
|
+
raise ValidationError(
|
|
82
|
+
f"Time to maturity must be non-negative, got {time_to_maturity}"
|
|
83
|
+
)
|
|
84
|
+
df_base = self.base_curve.get_discount_factor(time_to_maturity)
|
|
85
|
+
return df_base * math.exp(-self.shift * time_to_maturity)
|
|
86
|
+
|
|
87
|
+
def get_rate(self, time_to_maturity: float) -> float:
|
|
88
|
+
if time_to_maturity <= 0:
|
|
89
|
+
return self.base_curve.get_rate(time_to_maturity) + self.shift
|
|
90
|
+
df = self.get_discount_factor(time_to_maturity)
|
|
91
|
+
return -math.log(df) / time_to_maturity
|
|
92
|
+
|
|
93
|
+
def __repr__(self):
|
|
94
|
+
return f"ParallelShiftRateCurve(shift={self.shift:.2%}, base={self.base_curve!r})"
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@dataclass
|
|
98
|
+
class FlatRateCurve(RateCurve):
|
|
99
|
+
"""
|
|
100
|
+
Flat (constant) rate curve.
|
|
101
|
+
|
|
102
|
+
Returns the same rate regardless of maturity.
|
|
103
|
+
|
|
104
|
+
Attributes:
|
|
105
|
+
rate: Constant annualized risk-free rate (continuously compounded)
|
|
106
|
+
"""
|
|
107
|
+
rate: float
|
|
108
|
+
|
|
109
|
+
def __post_init__(self):
|
|
110
|
+
"""Validate rate - allow negative rates but warn if extreme."""
|
|
111
|
+
if self.rate < -0.10: # -10% rate - sanity check
|
|
112
|
+
raise ValidationError(f"Rate seems unreasonably low: {self.rate}")
|
|
113
|
+
if self.rate > 0.50: # 50% rate - sanity check
|
|
114
|
+
raise ValidationError(f"Rate seems unreasonably high: {self.rate}")
|
|
115
|
+
|
|
116
|
+
def get_rate(self, time_to_maturity: float) -> float:
|
|
117
|
+
"""
|
|
118
|
+
Return constant rate.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
time_to_maturity: Time to maturity (ignored)
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
Constant risk-free rate
|
|
125
|
+
"""
|
|
126
|
+
return self.rate
|
|
127
|
+
|
|
128
|
+
def get_discount_factor(self, time_to_maturity: float) -> float:
|
|
129
|
+
"""
|
|
130
|
+
Calculate discount factor.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
time_to_maturity: Time to maturity in years
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
Discount factor exp(-r*T)
|
|
137
|
+
"""
|
|
138
|
+
import math
|
|
139
|
+
if time_to_maturity < 0:
|
|
140
|
+
raise ValidationError(f"Time to maturity must be non-negative, got {time_to_maturity}")
|
|
141
|
+
return math.exp(-self.rate * time_to_maturity)
|
|
142
|
+
|
|
143
|
+
def __repr__(self):
|
|
144
|
+
return f"FlatRateCurve(rate={self.rate:.2%})"
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class InterpolatedRateCurve(RateCurve):
|
|
148
|
+
"""
|
|
149
|
+
Base class for interpolated rate curves.
|
|
150
|
+
|
|
151
|
+
Stores rate pillars (time, rate pairs) and interpolates between them.
|
|
152
|
+
|
|
153
|
+
Attributes:
|
|
154
|
+
pillars: List of (time, rate) tuples, sorted by time
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
def __init__(self, pillars: List[Tuple[float, float]]):
|
|
158
|
+
"""
|
|
159
|
+
Initialize interpolated rate curve.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
pillars: List of (time_to_maturity, rate) tuples
|
|
163
|
+
|
|
164
|
+
Raises:
|
|
165
|
+
ValidationError: If pillars are invalid
|
|
166
|
+
"""
|
|
167
|
+
if not pillars:
|
|
168
|
+
raise ValidationError("Must provide at least one pillar")
|
|
169
|
+
|
|
170
|
+
# Sort pillars by time
|
|
171
|
+
self.pillars = sorted(pillars, key=lambda x: x[0])
|
|
172
|
+
|
|
173
|
+
# Validate pillars
|
|
174
|
+
for i, (time, rate) in enumerate(self.pillars):
|
|
175
|
+
if time < 0:
|
|
176
|
+
raise ValidationError(f"Pillar {i}: time must be non-negative, got {time}")
|
|
177
|
+
if i > 0 and time == self.pillars[i-1][0]:
|
|
178
|
+
raise ValidationError(f"Duplicate time pillar at {time}")
|
|
179
|
+
|
|
180
|
+
def _find_bracketing_pillars(self, time: float) -> Tuple[int, int]:
|
|
181
|
+
"""
|
|
182
|
+
Find indices of pillars that bracket the given time.
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
Tuple of (left_index, right_index)
|
|
186
|
+
If time is outside range, returns appropriate boundary indices.
|
|
187
|
+
"""
|
|
188
|
+
if time <= self.pillars[0][0]:
|
|
189
|
+
return (0, 0) # Flat extrapolation on left
|
|
190
|
+
|
|
191
|
+
if time >= self.pillars[-1][0]:
|
|
192
|
+
return (len(self.pillars)-1, len(self.pillars)-1) # Flat extrapolation on right
|
|
193
|
+
|
|
194
|
+
# Binary search for bracketing pillars
|
|
195
|
+
left = 0
|
|
196
|
+
right = len(self.pillars) - 1
|
|
197
|
+
|
|
198
|
+
while right - left > 1:
|
|
199
|
+
mid = (left + right) // 2
|
|
200
|
+
if self.pillars[mid][0] <= time:
|
|
201
|
+
left = mid
|
|
202
|
+
else:
|
|
203
|
+
right = mid
|
|
204
|
+
|
|
205
|
+
return (left, right)
|
|
206
|
+
|
|
207
|
+
def __repr__(self):
|
|
208
|
+
return f"{self.__class__.__name__}(pillars={len(self.pillars)})"
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class LinearRateCurve(InterpolatedRateCurve):
|
|
212
|
+
"""
|
|
213
|
+
Linear interpolated rate curve.
|
|
214
|
+
|
|
215
|
+
Performs linear interpolation on rates between pillars.
|
|
216
|
+
Uses flat extrapolation outside the pillar range.
|
|
217
|
+
"""
|
|
218
|
+
|
|
219
|
+
def get_rate(self, time_to_maturity: float) -> float:
|
|
220
|
+
"""
|
|
221
|
+
Get rate using linear interpolation.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
time_to_maturity: Time to maturity in years
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
Interpolated rate
|
|
228
|
+
"""
|
|
229
|
+
if time_to_maturity < 0:
|
|
230
|
+
raise ValidationError(f"Time to maturity must be non-negative, got {time_to_maturity}")
|
|
231
|
+
|
|
232
|
+
left_idx, right_idx = self._find_bracketing_pillars(time_to_maturity)
|
|
233
|
+
|
|
234
|
+
# Flat extrapolation
|
|
235
|
+
if left_idx == right_idx:
|
|
236
|
+
return self.pillars[left_idx][1]
|
|
237
|
+
|
|
238
|
+
# Linear interpolation
|
|
239
|
+
t1, r1 = self.pillars[left_idx]
|
|
240
|
+
t2, r2 = self.pillars[right_idx]
|
|
241
|
+
|
|
242
|
+
# Linear interpolation formula
|
|
243
|
+
weight = (time_to_maturity - t1) / (t2 - t1)
|
|
244
|
+
rate = r1 + weight * (r2 - r1)
|
|
245
|
+
|
|
246
|
+
return rate
|
|
247
|
+
|
|
248
|
+
def get_discount_factor(self, time_to_maturity: float) -> float:
|
|
249
|
+
"""
|
|
250
|
+
Get discount factor.
|
|
251
|
+
|
|
252
|
+
Args:
|
|
253
|
+
time_to_maturity: Time to maturity in years
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
Discount factor exp(-r*T)
|
|
257
|
+
"""
|
|
258
|
+
rate = self.get_rate(time_to_maturity)
|
|
259
|
+
return math.exp(-rate * time_to_maturity)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
class LogLinearRateCurve(InterpolatedRateCurve):
|
|
263
|
+
"""
|
|
264
|
+
Log-linear interpolated rate curve.
|
|
265
|
+
|
|
266
|
+
Performs linear interpolation on log(discount factors) between pillars.
|
|
267
|
+
This ensures smooth forward rates and is the market standard for
|
|
268
|
+
discount curve interpolation.
|
|
269
|
+
"""
|
|
270
|
+
|
|
271
|
+
def get_discount_factor(self, time_to_maturity: float) -> float:
|
|
272
|
+
"""
|
|
273
|
+
Get discount factor using log-linear interpolation.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
time_to_maturity: Time to maturity in years
|
|
277
|
+
|
|
278
|
+
Returns:
|
|
279
|
+
Interpolated discount factor
|
|
280
|
+
"""
|
|
281
|
+
if time_to_maturity < 0:
|
|
282
|
+
raise ValidationError(f"Time to maturity must be non-negative, got {time_to_maturity}")
|
|
283
|
+
|
|
284
|
+
if time_to_maturity == 0:
|
|
285
|
+
return 1.0
|
|
286
|
+
|
|
287
|
+
left_idx, right_idx = self._find_bracketing_pillars(time_to_maturity)
|
|
288
|
+
|
|
289
|
+
# Calculate discount factors at pillars
|
|
290
|
+
t1, r1 = self.pillars[left_idx]
|
|
291
|
+
df1 = math.exp(-r1 * t1) if t1 > 0 else 1.0
|
|
292
|
+
|
|
293
|
+
# Flat extrapolation
|
|
294
|
+
if left_idx == right_idx:
|
|
295
|
+
if t1 == 0:
|
|
296
|
+
return math.exp(-r1 * time_to_maturity)
|
|
297
|
+
# Flat forward rate extrapolation
|
|
298
|
+
return df1 * math.exp(-r1 * (time_to_maturity - t1))
|
|
299
|
+
|
|
300
|
+
# Log-linear interpolation
|
|
301
|
+
t2, r2 = self.pillars[right_idx]
|
|
302
|
+
df2 = math.exp(-r2 * t2)
|
|
303
|
+
|
|
304
|
+
# Linear interpolation on log(DF)
|
|
305
|
+
log_df1 = math.log(df1)
|
|
306
|
+
log_df2 = math.log(df2)
|
|
307
|
+
|
|
308
|
+
weight = (time_to_maturity - t1) / (t2 - t1)
|
|
309
|
+
log_df = log_df1 + weight * (log_df2 - log_df1)
|
|
310
|
+
|
|
311
|
+
return math.exp(log_df)
|
|
312
|
+
|
|
313
|
+
def get_rate(self, time_to_maturity: float) -> float:
|
|
314
|
+
"""
|
|
315
|
+
Get rate from discount factor.
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
time_to_maturity: Time to maturity in years
|
|
319
|
+
|
|
320
|
+
Returns:
|
|
321
|
+
Zero rate
|
|
322
|
+
"""
|
|
323
|
+
if time_to_maturity <= 0:
|
|
324
|
+
if len(self.pillars) > 0:
|
|
325
|
+
return self.pillars[0][1]
|
|
326
|
+
return 0.0
|
|
327
|
+
|
|
328
|
+
df = self.get_discount_factor(time_to_maturity)
|
|
329
|
+
return -math.log(df) / time_to_maturity
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
class CubicSplineRateCurve(InterpolatedRateCurve):
|
|
333
|
+
"""
|
|
334
|
+
Cubic spline interpolated rate curve.
|
|
335
|
+
|
|
336
|
+
Uses natural cubic spline interpolation on rates.
|
|
337
|
+
Provides smooth first and second derivatives.
|
|
338
|
+
"""
|
|
339
|
+
|
|
340
|
+
def __init__(self, pillars: List[Tuple[float, float]]):
|
|
341
|
+
"""Initialize and compute spline coefficients."""
|
|
342
|
+
super().__init__(pillars)
|
|
343
|
+
self._compute_spline_coefficients()
|
|
344
|
+
|
|
345
|
+
def _compute_spline_coefficients(self):
|
|
346
|
+
"""
|
|
347
|
+
Compute natural cubic spline coefficients.
|
|
348
|
+
|
|
349
|
+
For each interval [x_i, x_{i+1}], the spline is:
|
|
350
|
+
S_i(x) = a_i + b_i*(x-x_i) + c_i*(x-x_i)^2 + d_i*(x-x_i)^3
|
|
351
|
+
"""
|
|
352
|
+
n = len(self.pillars)
|
|
353
|
+
|
|
354
|
+
if n < 2:
|
|
355
|
+
# Not enough points for spline, treat as constant
|
|
356
|
+
self.coefficients = [(self.pillars[0][1], 0, 0, 0)]
|
|
357
|
+
return
|
|
358
|
+
|
|
359
|
+
# Extract times and rates
|
|
360
|
+
times = [p[0] for p in self.pillars]
|
|
361
|
+
rates = [p[1] for p in self.pillars]
|
|
362
|
+
|
|
363
|
+
# Compute intervals
|
|
364
|
+
h = [times[i+1] - times[i] for i in range(n-1)]
|
|
365
|
+
|
|
366
|
+
# Set up tridiagonal system for second derivatives
|
|
367
|
+
# Natural spline: second derivative is 0 at endpoints
|
|
368
|
+
alpha = [0] * n
|
|
369
|
+
for i in range(1, n-1):
|
|
370
|
+
alpha[i] = (3.0/h[i]) * (rates[i+1] - rates[i]) - (3.0/h[i-1]) * (rates[i] - rates[i-1])
|
|
371
|
+
|
|
372
|
+
# Solve tridiagonal system
|
|
373
|
+
l = [1.0] * n
|
|
374
|
+
mu = [0.0] * n
|
|
375
|
+
z = [0.0] * n
|
|
376
|
+
|
|
377
|
+
for i in range(1, n-1):
|
|
378
|
+
l[i] = 2.0 * (times[i+1] - times[i-1]) - h[i-1] * mu[i-1]
|
|
379
|
+
mu[i] = h[i] / l[i]
|
|
380
|
+
z[i] = (alpha[i] - h[i-1] * z[i-1]) / l[i]
|
|
381
|
+
|
|
382
|
+
# Back substitution
|
|
383
|
+
c = [0.0] * n
|
|
384
|
+
b = [0.0] * (n-1)
|
|
385
|
+
d = [0.0] * (n-1)
|
|
386
|
+
|
|
387
|
+
for j in range(n-2, -1, -1):
|
|
388
|
+
c[j] = z[j] - mu[j] * c[j+1]
|
|
389
|
+
b[j] = (rates[j+1] - rates[j]) / h[j] - h[j] * (c[j+1] + 2.0*c[j]) / 3.0
|
|
390
|
+
d[j] = (c[j+1] - c[j]) / (3.0 * h[j])
|
|
391
|
+
|
|
392
|
+
# Store coefficients (a, b, c, d) for each interval
|
|
393
|
+
self.coefficients = []
|
|
394
|
+
for i in range(n-1):
|
|
395
|
+
self.coefficients.append((rates[i], b[i], c[i], d[i]))
|
|
396
|
+
|
|
397
|
+
def get_rate(self, time_to_maturity: float) -> float:
|
|
398
|
+
"""
|
|
399
|
+
Get rate using cubic spline interpolation.
|
|
400
|
+
|
|
401
|
+
Args:
|
|
402
|
+
time_to_maturity: Time to maturity in years
|
|
403
|
+
|
|
404
|
+
Returns:
|
|
405
|
+
Interpolated rate
|
|
406
|
+
"""
|
|
407
|
+
if time_to_maturity < 0:
|
|
408
|
+
raise ValidationError(f"Time to maturity must be non-negative, got {time_to_maturity}")
|
|
409
|
+
|
|
410
|
+
left_idx, right_idx = self._find_bracketing_pillars(time_to_maturity)
|
|
411
|
+
|
|
412
|
+
# Flat extrapolation
|
|
413
|
+
if left_idx == right_idx:
|
|
414
|
+
return self.pillars[left_idx][1]
|
|
415
|
+
|
|
416
|
+
# Cubic spline evaluation
|
|
417
|
+
a, b, c, d = self.coefficients[left_idx]
|
|
418
|
+
t0 = self.pillars[left_idx][0]
|
|
419
|
+
dt = time_to_maturity - t0
|
|
420
|
+
|
|
421
|
+
rate = a + b*dt + c*dt*dt + d*dt*dt*dt
|
|
422
|
+
|
|
423
|
+
return rate
|
|
424
|
+
|
|
425
|
+
def get_discount_factor(self, time_to_maturity: float) -> float:
|
|
426
|
+
"""
|
|
427
|
+
Get discount factor.
|
|
428
|
+
|
|
429
|
+
Args:
|
|
430
|
+
time_to_maturity: Time to maturity in years
|
|
431
|
+
|
|
432
|
+
Returns:
|
|
433
|
+
Discount factor exp(-r*T)
|
|
434
|
+
"""
|
|
435
|
+
rate = self.get_rate(time_to_maturity)
|
|
436
|
+
return math.exp(-rate * time_to_maturity)
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Volatility surface representations.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
|
|
8
|
+
from quantark.util.exceptions import ValidationError
|
|
9
|
+
from quantark.util.numerical import safe_divide, safe_sqrt, validate_positive
|
|
10
|
+
import numpy as np
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class VolatilitySurface(ABC):
|
|
14
|
+
"""
|
|
15
|
+
Abstract base class for volatility surfaces.
|
|
16
|
+
|
|
17
|
+
Volatility surfaces provide implied volatility as a function of
|
|
18
|
+
strike and time to maturity.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
@abstractmethod
|
|
22
|
+
def get_vol(self, strike: float, time_to_maturity: float, spot: float) -> float:
|
|
23
|
+
"""
|
|
24
|
+
Get implied volatility for given strike and maturity.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
strike: Strike price
|
|
28
|
+
time_to_maturity: Time to maturity in years
|
|
29
|
+
spot: Current spot price
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Implied volatility (annualized)
|
|
33
|
+
"""
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass
|
|
38
|
+
class FlatVolSurface(VolatilitySurface):
|
|
39
|
+
"""
|
|
40
|
+
Flat (constant) volatility surface.
|
|
41
|
+
|
|
42
|
+
Returns the same volatility regardless of strike and maturity.
|
|
43
|
+
Suitable for Black-Scholes models with constant volatility.
|
|
44
|
+
|
|
45
|
+
Attributes:
|
|
46
|
+
volatility: Constant annualized volatility
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
volatility: float
|
|
50
|
+
|
|
51
|
+
def __post_init__(self) -> None:
|
|
52
|
+
"""Validate volatility."""
|
|
53
|
+
if not isinstance(self.volatility, (int, float)):
|
|
54
|
+
raise ValidationError(f"Volatility must be numeric, got {self.volatility}")
|
|
55
|
+
validate_positive(float(self.volatility), name="volatility")
|
|
56
|
+
if self.volatility > 5.0: # 500% vol - sanity check
|
|
57
|
+
raise ValidationError(
|
|
58
|
+
f"Volatility seems unreasonably high: {self.volatility}"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
def get_vol(self, strike: float, time_to_maturity: float, spot: float) -> float:
|
|
62
|
+
"""
|
|
63
|
+
Return constant volatility.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
strike: Strike price (ignored)
|
|
67
|
+
time_to_maturity: Time to maturity (ignored)
|
|
68
|
+
spot: Spot price (ignored)
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Constant volatility
|
|
72
|
+
"""
|
|
73
|
+
return self.volatility
|
|
74
|
+
|
|
75
|
+
def __repr__(self):
|
|
76
|
+
return f"FlatVolSurface(vol={self.volatility:.2%})"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@dataclass
|
|
80
|
+
class TermStructureVolSurface(VolatilitySurface):
|
|
81
|
+
"""
|
|
82
|
+
Term-structure volatility surface (ATM by maturity).
|
|
83
|
+
|
|
84
|
+
Provides a time-dependent volatility via total variance interpolation on maturity.
|
|
85
|
+
Strike is ignored (ATM term structure).
|
|
86
|
+
|
|
87
|
+
Attributes:
|
|
88
|
+
times: Increasing maturities (year fractions).
|
|
89
|
+
vols: Implied volatilities for each maturity.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
times: list[float]
|
|
93
|
+
vols: list[float]
|
|
94
|
+
|
|
95
|
+
def __post_init__(self) -> None:
|
|
96
|
+
if len(self.times) != len(self.vols):
|
|
97
|
+
raise ValidationError("times and vols must have the same length.")
|
|
98
|
+
if len(self.times) < 2:
|
|
99
|
+
raise ValidationError("times must have at least 2 points.")
|
|
100
|
+
if any(t <= 0 for t in self.times):
|
|
101
|
+
raise ValidationError("times must be positive.")
|
|
102
|
+
if any(self.times[i] >= self.times[i + 1] for i in range(len(self.times) - 1)):
|
|
103
|
+
raise ValidationError("times must be strictly increasing.")
|
|
104
|
+
for v in self.vols:
|
|
105
|
+
validate_positive(float(v), name="volatility")
|
|
106
|
+
|
|
107
|
+
def get_vol(self, strike: float, time_to_maturity: float, spot: float) -> float:
|
|
108
|
+
t = float(time_to_maturity)
|
|
109
|
+
if t <= self.times[0]:
|
|
110
|
+
return float(self.vols[0])
|
|
111
|
+
if t >= self.times[-1]:
|
|
112
|
+
return float(self.vols[-1])
|
|
113
|
+
total_variances = [float(v) ** 2 * float(tt) for v, tt in zip(self.vols, self.times)]
|
|
114
|
+
interp_total_var = float(np.interp(t, self.times, total_variances))
|
|
115
|
+
return float(safe_sqrt(safe_divide(interp_total_var, t)))
|
|
116
|
+
|
|
117
|
+
def __repr__(self):
|
|
118
|
+
return "TermStructureVolSurface(points=%d)" % len(self.times)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Portfolio management module.
|
|
3
|
+
|
|
4
|
+
This module provides comprehensive portfolio management capabilities for
|
|
5
|
+
tracking positions, calculating valuations, and aggregating risk metrics
|
|
6
|
+
across multiple assets and products.
|
|
7
|
+
|
|
8
|
+
Supports multiple asset classes:
|
|
9
|
+
- Equity: Options, futures, delta-one products
|
|
10
|
+
- Fixed Income: Bonds, bond futures, interest rate derivatives
|
|
11
|
+
|
|
12
|
+
Main components:
|
|
13
|
+
- BasePosition, BasePortfolio: Asset-agnostic protocols
|
|
14
|
+
- EquityPosition, EquityPortfolio: Equity-specific implementations
|
|
15
|
+
- FIPosition, FIPortfolio: Fixed Income-specific implementations
|
|
16
|
+
- PortfolioSnapshot: Point-in-time snapshot of portfolio state
|
|
17
|
+
- PortfolioExporter: Export functionality for parquet and excel formats
|
|
18
|
+
|
|
19
|
+
Backward Compatibility:
|
|
20
|
+
- Position: Alias for EquityPosition
|
|
21
|
+
- Portfolio: Alias for EquityPortfolio
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
# Base protocols
|
|
25
|
+
from .base import BasePosition, BasePortfolio
|
|
26
|
+
|
|
27
|
+
# Equity implementations (with backward-compatible aliases)
|
|
28
|
+
from .equity import (
|
|
29
|
+
EquityPosition,
|
|
30
|
+
EquityPortfolio,
|
|
31
|
+
Position, # Backward compatibility alias
|
|
32
|
+
Portfolio, # Backward compatibility alias
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# Fixed Income implementations
|
|
36
|
+
from .fi import (
|
|
37
|
+
FIPosition,
|
|
38
|
+
FIPortfolio,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# Utilities
|
|
42
|
+
from .portfolio_snapshot import PortfolioSnapshot
|
|
43
|
+
from .portfolio_storage import PortfolioExporter
|
|
44
|
+
|
|
45
|
+
__all__ = [
|
|
46
|
+
# Base protocols
|
|
47
|
+
"BasePosition",
|
|
48
|
+
"BasePortfolio",
|
|
49
|
+
# Equity
|
|
50
|
+
"EquityPosition",
|
|
51
|
+
"EquityPortfolio",
|
|
52
|
+
# Fixed Income
|
|
53
|
+
"FIPosition",
|
|
54
|
+
"FIPortfolio",
|
|
55
|
+
# Backward compatibility aliases
|
|
56
|
+
"Position",
|
|
57
|
+
"Portfolio",
|
|
58
|
+
# Utilities
|
|
59
|
+
"PortfolioSnapshot",
|
|
60
|
+
"PortfolioExporter",
|
|
61
|
+
]
|