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,485 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Futures contract implementation with basis handling.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import Optional
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from .base_deltaone_product import BaseDeltaOneProduct
|
|
10
|
+
from quantark.util.enum.deltaone_enums import DeltaOneType
|
|
11
|
+
from quantark.util.exceptions import ValidationError
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class Futures(BaseDeltaOneProduct):
|
|
16
|
+
"""
|
|
17
|
+
Futures contract with basis handling and mark-to-market support.
|
|
18
|
+
|
|
19
|
+
A futures contract is an agreement to buy/sell an asset at a future date
|
|
20
|
+
at a predetermined price. The contract has a multiplier and optional
|
|
21
|
+
observed market price for mark-to-market valuation.
|
|
22
|
+
|
|
23
|
+
Theoretical forward pricing with basis:
|
|
24
|
+
F(t,T) = S(t) * exp((r - q) * (T - t)) + basis(t) * exp(-λ * (T - t))
|
|
25
|
+
|
|
26
|
+
Where:
|
|
27
|
+
- S(t): Current spot price
|
|
28
|
+
- r: Risk-free rate
|
|
29
|
+
- q: Dividend yield
|
|
30
|
+
- basis(t): Current basis (difference from theoretical forward)
|
|
31
|
+
- λ: Basis decay rate (how fast basis converges to 0 at maturity)
|
|
32
|
+
|
|
33
|
+
Attributes:
|
|
34
|
+
underlying: Identifier for the underlying asset
|
|
35
|
+
multiplier: Contract multiplier (e.g., 50 for E-mini S&P)
|
|
36
|
+
maturity: Time to maturity in years (optional if maturity_date provided)
|
|
37
|
+
maturity_date: Date when futures contract expires
|
|
38
|
+
basis: Current basis (futures_price - theoretical_forward)
|
|
39
|
+
basis_decay_rate: Rate at which basis converges to zero (default: 1.0)
|
|
40
|
+
market_price: Optional observed futures price for mark-to-market valuation
|
|
41
|
+
|
|
42
|
+
Example:
|
|
43
|
+
>>> # E-mini S&P 500 futures
|
|
44
|
+
>>> future = Futures(
|
|
45
|
+
... underlying="ES",
|
|
46
|
+
... multiplier=50.0,
|
|
47
|
+
... maturity=0.25, # 3 months
|
|
48
|
+
... basis=2.5,
|
|
49
|
+
... basis_decay_rate=2.0
|
|
50
|
+
... )
|
|
51
|
+
>>>
|
|
52
|
+
>>> # With market price for mark-to-market
|
|
53
|
+
>>> future_mtm = Futures(
|
|
54
|
+
... underlying="ES",
|
|
55
|
+
... multiplier=50.0,
|
|
56
|
+
... maturity=0.25,
|
|
57
|
+
... basis=2.5,
|
|
58
|
+
... market_price=4525.0 # Observed market price
|
|
59
|
+
... )
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
multiplier: float = 1.0
|
|
63
|
+
basis: float = 0.0
|
|
64
|
+
basis_decay_rate: float = 1.0
|
|
65
|
+
market_price: Optional[float] = None
|
|
66
|
+
|
|
67
|
+
def __init__(
|
|
68
|
+
self,
|
|
69
|
+
underlying: str,
|
|
70
|
+
multiplier: float = 1.0,
|
|
71
|
+
maturity: Optional[float] = None,
|
|
72
|
+
maturity_date: Optional[datetime] = None,
|
|
73
|
+
basis: float = 0.0,
|
|
74
|
+
basis_decay_rate: float = 1.0,
|
|
75
|
+
market_price: Optional[float] = None,
|
|
76
|
+
):
|
|
77
|
+
"""
|
|
78
|
+
Initialize futures contract.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
underlying: Identifier for the underlying asset
|
|
82
|
+
multiplier: Contract multiplier (must be positive)
|
|
83
|
+
maturity: Time to maturity in years (optional if maturity_date provided)
|
|
84
|
+
maturity_date: Date when contract expires (optional if maturity provided)
|
|
85
|
+
basis: Current basis (futures_price - theoretical_forward)
|
|
86
|
+
basis_decay_rate: Rate at which basis converges to zero (default: 1.0)
|
|
87
|
+
market_price: Optional observed futures price for mark-to-market
|
|
88
|
+
|
|
89
|
+
Raises:
|
|
90
|
+
ValidationError: If parameters are invalid
|
|
91
|
+
|
|
92
|
+
Note:
|
|
93
|
+
Either maturity OR maturity_date must be provided (not both).
|
|
94
|
+
"""
|
|
95
|
+
# Initialize parent with futures type
|
|
96
|
+
super().__init__(
|
|
97
|
+
underlying=underlying,
|
|
98
|
+
deltaone_type=DeltaOneType.FUTURES,
|
|
99
|
+
maturity=maturity,
|
|
100
|
+
maturity_date=maturity_date,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
self.multiplier = multiplier
|
|
104
|
+
self.basis = basis
|
|
105
|
+
self.basis_decay_rate = basis_decay_rate
|
|
106
|
+
self.market_price = market_price
|
|
107
|
+
|
|
108
|
+
# Validate futures-specific parameters
|
|
109
|
+
self._validate_futures_params()
|
|
110
|
+
|
|
111
|
+
def _validate_futures_params(self) -> None:
|
|
112
|
+
"""
|
|
113
|
+
Validate futures-specific parameters.
|
|
114
|
+
|
|
115
|
+
Raises:
|
|
116
|
+
ValidationError: If parameters are invalid
|
|
117
|
+
"""
|
|
118
|
+
if self.multiplier <= 0:
|
|
119
|
+
raise ValidationError(f"Multiplier must be positive, got {self.multiplier}")
|
|
120
|
+
|
|
121
|
+
# Validate basis-specific parameters
|
|
122
|
+
if not math.isfinite(self.basis):
|
|
123
|
+
raise ValidationError(f"Basis must be finite, got {self.basis}")
|
|
124
|
+
|
|
125
|
+
if self.basis_decay_rate <= 0:
|
|
126
|
+
raise ValidationError(f"Basis decay rate must be positive, got {self.basis_decay_rate}")
|
|
127
|
+
|
|
128
|
+
if self.market_price is not None and self.market_price <= 0:
|
|
129
|
+
raise ValidationError(f"Market price must be positive if provided, got {self.market_price}")
|
|
130
|
+
|
|
131
|
+
# Futures must have a maturity
|
|
132
|
+
if self.maturity is None and self.maturity_date is None:
|
|
133
|
+
raise ValidationError("Futures contract must have a maturity or maturity_date")
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def is_linear(self) -> bool:
|
|
137
|
+
return True
|
|
138
|
+
|
|
139
|
+
def get_forward_price(
|
|
140
|
+
self,
|
|
141
|
+
spot: float,
|
|
142
|
+
rate: float,
|
|
143
|
+
div_yield: float,
|
|
144
|
+
time_to_maturity: float
|
|
145
|
+
) -> float:
|
|
146
|
+
"""
|
|
147
|
+
Calculate the theoretical forward price with basis.
|
|
148
|
+
|
|
149
|
+
Forward price with basis:
|
|
150
|
+
F(t,T) = S(t) * exp((r - q) * (T - t)) + basis(t) * exp(-λ * (T - t))
|
|
151
|
+
|
|
152
|
+
The basis decays exponentially to zero as the contract approaches maturity,
|
|
153
|
+
ensuring convergence to the spot price at expiration.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
spot: Current spot price
|
|
157
|
+
rate: Risk-free rate (continuously compounded)
|
|
158
|
+
div_yield: Dividend yield (continuously compounded)
|
|
159
|
+
time_to_maturity: Time to maturity in years
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
Theoretical forward price with basis
|
|
163
|
+
|
|
164
|
+
Raises:
|
|
165
|
+
ValidationError: If inputs are invalid
|
|
166
|
+
"""
|
|
167
|
+
if spot <= 0:
|
|
168
|
+
raise ValidationError(f"Spot price must be positive, got {spot}")
|
|
169
|
+
if time_to_maturity < 0:
|
|
170
|
+
raise ValidationError(f"Time to maturity must be non-negative, got {time_to_maturity}")
|
|
171
|
+
|
|
172
|
+
# Theoretical forward price (cost-of-carry)
|
|
173
|
+
carry_cost = (rate - div_yield) * time_to_maturity
|
|
174
|
+
theoretical_forward = spot * math.exp(carry_cost)
|
|
175
|
+
|
|
176
|
+
# Add decaying basis
|
|
177
|
+
basis_decay = math.exp(-self.basis_decay_rate * time_to_maturity)
|
|
178
|
+
forward_price = theoretical_forward + self.basis * basis_decay
|
|
179
|
+
|
|
180
|
+
return forward_price
|
|
181
|
+
|
|
182
|
+
def get_theoretical_price(
|
|
183
|
+
self,
|
|
184
|
+
spot: float,
|
|
185
|
+
rate: float,
|
|
186
|
+
div_yield: float,
|
|
187
|
+
time_to_maturity: float
|
|
188
|
+
) -> float:
|
|
189
|
+
"""
|
|
190
|
+
Get theoretical futures contract value (per contract, not notional).
|
|
191
|
+
|
|
192
|
+
For futures contracts, the value is the forward price times multiplier.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
spot: Current spot price
|
|
196
|
+
rate: Risk-free rate
|
|
197
|
+
div_yield: Dividend yield
|
|
198
|
+
time_to_maturity: Time to maturity in years
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
Theoretical contract value
|
|
202
|
+
"""
|
|
203
|
+
forward_price = self.get_forward_price(spot, rate, div_yield, time_to_maturity)
|
|
204
|
+
return forward_price * self.multiplier
|
|
205
|
+
|
|
206
|
+
def get_mark_to_market_price(self) -> Optional[float]:
|
|
207
|
+
"""
|
|
208
|
+
Get mark-to-market price based on observed market price.
|
|
209
|
+
|
|
210
|
+
If market_price is set, returns the contract value based on observed
|
|
211
|
+
market price. Otherwise returns None.
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
Mark-to-market contract value, or None if no market price available
|
|
215
|
+
"""
|
|
216
|
+
if self.market_price is None:
|
|
217
|
+
return None
|
|
218
|
+
return self.market_price * self.multiplier
|
|
219
|
+
|
|
220
|
+
def get_notional_value(self, price: float) -> float:
|
|
221
|
+
"""
|
|
222
|
+
Calculate notional value of the futures contract.
|
|
223
|
+
|
|
224
|
+
Notional value = price * multiplier
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
price: Futures price (per unit)
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
Notional value
|
|
231
|
+
"""
|
|
232
|
+
return price * self.multiplier
|
|
233
|
+
|
|
234
|
+
def update_market_price(self, new_market_price: float) -> None:
|
|
235
|
+
"""
|
|
236
|
+
Update the observed market price for mark-to-market.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
new_market_price: New observed market price
|
|
240
|
+
|
|
241
|
+
Raises:
|
|
242
|
+
ValidationError: If price is invalid
|
|
243
|
+
"""
|
|
244
|
+
if new_market_price <= 0:
|
|
245
|
+
raise ValidationError(f"Market price must be positive, got {new_market_price}")
|
|
246
|
+
self.market_price = new_market_price
|
|
247
|
+
|
|
248
|
+
def get_basis(
|
|
249
|
+
self,
|
|
250
|
+
spot: float,
|
|
251
|
+
rate: float,
|
|
252
|
+
div_yield: float,
|
|
253
|
+
time_to_maturity: float,
|
|
254
|
+
observed_futures_price: Optional[float] = None
|
|
255
|
+
) -> float:
|
|
256
|
+
"""
|
|
257
|
+
Calculate or update the basis.
|
|
258
|
+
|
|
259
|
+
If observed_futures_price is provided, calculates the basis as:
|
|
260
|
+
basis = observed_futures_price - theoretical_forward
|
|
261
|
+
|
|
262
|
+
Otherwise returns the current basis attribute.
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
spot: Current spot price
|
|
266
|
+
rate: Risk-free rate
|
|
267
|
+
div_yield: Dividend yield
|
|
268
|
+
time_to_maturity: Time to maturity in years
|
|
269
|
+
observed_futures_price: Optional observed market price
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
Basis value (in price units, not annualized)
|
|
273
|
+
"""
|
|
274
|
+
if observed_futures_price is not None:
|
|
275
|
+
# Calculate theoretical forward without basis
|
|
276
|
+
carry_cost = (rate - div_yield) * time_to_maturity
|
|
277
|
+
theoretical_forward = spot * math.exp(carry_cost)
|
|
278
|
+
|
|
279
|
+
# Basis is the difference
|
|
280
|
+
return observed_futures_price - theoretical_forward
|
|
281
|
+
else:
|
|
282
|
+
return self.basis
|
|
283
|
+
|
|
284
|
+
def calculate_implied_basis(
|
|
285
|
+
self,
|
|
286
|
+
spot: float,
|
|
287
|
+
rate: float,
|
|
288
|
+
div_yield: float,
|
|
289
|
+
time_to_maturity: float,
|
|
290
|
+
observed_futures_price: float,
|
|
291
|
+
) -> float:
|
|
292
|
+
"""
|
|
293
|
+
Calculate the implied basis from an observed futures price.
|
|
294
|
+
|
|
295
|
+
The basis is defined as the difference between the observed futures
|
|
296
|
+
price and the theoretical forward price (cost-of-carry model):
|
|
297
|
+
|
|
298
|
+
basis = F_observed - S * exp((r - d) * T)
|
|
299
|
+
|
|
300
|
+
Args:
|
|
301
|
+
spot: Current spot price
|
|
302
|
+
rate: Risk-free rate (continuously compounded)
|
|
303
|
+
div_yield: Dividend yield (continuously compounded)
|
|
304
|
+
time_to_maturity: Time to maturity in years
|
|
305
|
+
observed_futures_price: Observed market futures price
|
|
306
|
+
|
|
307
|
+
Returns:
|
|
308
|
+
Implied basis in price units (points)
|
|
309
|
+
|
|
310
|
+
Raises:
|
|
311
|
+
ValidationError: If inputs are invalid
|
|
312
|
+
"""
|
|
313
|
+
if spot <= 0:
|
|
314
|
+
raise ValidationError(f"Spot must be positive, got {spot}")
|
|
315
|
+
if observed_futures_price <= 0:
|
|
316
|
+
raise ValidationError(f"Futures price must be positive, got {observed_futures_price}")
|
|
317
|
+
if time_to_maturity <= 0:
|
|
318
|
+
raise ValidationError(f"Time to maturity must be positive, got {time_to_maturity}")
|
|
319
|
+
|
|
320
|
+
# Theoretical forward from cost-of-carry model
|
|
321
|
+
theoretical_forward = spot * math.exp((rate - div_yield) * time_to_maturity)
|
|
322
|
+
|
|
323
|
+
# Implied basis
|
|
324
|
+
return observed_futures_price - theoretical_forward
|
|
325
|
+
|
|
326
|
+
def calculate_annualized_basis(
|
|
327
|
+
self,
|
|
328
|
+
spot: float,
|
|
329
|
+
rate: float,
|
|
330
|
+
div_yield: float,
|
|
331
|
+
time_to_maturity: float,
|
|
332
|
+
observed_futures_price: Optional[float] = None,
|
|
333
|
+
) -> float:
|
|
334
|
+
"""
|
|
335
|
+
Calculate the annualized basis rate.
|
|
336
|
+
|
|
337
|
+
The annualized basis represents the basis as an annual continuously
|
|
338
|
+
compounded rate. This allows direct comparison with dividend yields
|
|
339
|
+
and interest rates.
|
|
340
|
+
|
|
341
|
+
Derivation:
|
|
342
|
+
F = S * exp((r - d + b_annual) * T)
|
|
343
|
+
|
|
344
|
+
Solving for b_annual:
|
|
345
|
+
b_annual = (1/T) * ln(F/S) - r + d
|
|
346
|
+
|
|
347
|
+
Or equivalently, using the points basis:
|
|
348
|
+
b_annual = (1/T) * ln(1 + basis/S) (for small basis)
|
|
349
|
+
|
|
350
|
+
Args:
|
|
351
|
+
spot: Current spot price
|
|
352
|
+
rate: Risk-free rate (continuously compounded)
|
|
353
|
+
div_yield: Dividend yield (continuously compounded)
|
|
354
|
+
time_to_maturity: Time to maturity in years
|
|
355
|
+
observed_futures_price: Optional observed futures price.
|
|
356
|
+
If not provided, uses self.basis attribute.
|
|
357
|
+
|
|
358
|
+
Returns:
|
|
359
|
+
Annualized basis rate (as a decimal, e.g., 0.02 = 2%)
|
|
360
|
+
|
|
361
|
+
Raises:
|
|
362
|
+
ValidationError: If inputs are invalid or time_to_maturity is zero
|
|
363
|
+
|
|
364
|
+
Examples:
|
|
365
|
+
>>> # ES futures at 4500, spot at 4480, T=0.25 years
|
|
366
|
+
>>> future = Futures(underlying="ES", multiplier=50)
|
|
367
|
+
>>> b_annual = future.calculate_annualized_basis(
|
|
368
|
+
... spot=4480, rate=0.05, div_yield=0.015,
|
|
369
|
+
... time_to_maturity=0.25, observed_futures_price=4500
|
|
370
|
+
... )
|
|
371
|
+
"""
|
|
372
|
+
if spot <= 0:
|
|
373
|
+
raise ValidationError(f"Spot must be positive, got {spot}")
|
|
374
|
+
if time_to_maturity <= 0:
|
|
375
|
+
raise ValidationError(f"Time to maturity must be positive, got {time_to_maturity}")
|
|
376
|
+
|
|
377
|
+
if observed_futures_price is not None:
|
|
378
|
+
if observed_futures_price <= 0:
|
|
379
|
+
raise ValidationError(f"Futures price must be positive, got {observed_futures_price}")
|
|
380
|
+
|
|
381
|
+
# Calculate annualized basis rate directly from futures price
|
|
382
|
+
# F = S * exp((r - d + b) * T)
|
|
383
|
+
# b = (1/T) * ln(F/S) - r + d
|
|
384
|
+
futures_ratio = observed_futures_price / spot
|
|
385
|
+
return (math.log(futures_ratio) / time_to_maturity) - rate + div_yield
|
|
386
|
+
else:
|
|
387
|
+
# Use stored basis attribute
|
|
388
|
+
# For small basis relative to spot: b_annual ≈ basis / (spot * T)
|
|
389
|
+
# More precisely: b_annual = (1/T) * ln(1 + basis/S)
|
|
390
|
+
basis_relative_to_spot = self.basis / spot
|
|
391
|
+
# Ensure we don't take log of negative number (basis can be negative)
|
|
392
|
+
# When basis is small relative to spot, ln(1 + x) ≈ x
|
|
393
|
+
if abs(basis_relative_to_spot) < 0.01:
|
|
394
|
+
# Use approximation for small basis
|
|
395
|
+
return basis_relative_to_spot / time_to_maturity
|
|
396
|
+
else:
|
|
397
|
+
# Use exact formula
|
|
398
|
+
if 1 + basis_relative_to_spot <= 0:
|
|
399
|
+
raise ValidationError(
|
|
400
|
+
f"Basis too negative relative to spot: {basis_relative_to_spot}"
|
|
401
|
+
)
|
|
402
|
+
return math.log(1 + basis_relative_to_spot) / time_to_maturity
|
|
403
|
+
|
|
404
|
+
def calculate_basis_in_bps(
|
|
405
|
+
self,
|
|
406
|
+
spot: float,
|
|
407
|
+
rate: float,
|
|
408
|
+
div_yield: float,
|
|
409
|
+
time_to_maturity: float,
|
|
410
|
+
observed_futures_price: Optional[float] = None,
|
|
411
|
+
) -> float:
|
|
412
|
+
"""
|
|
413
|
+
Calculate the basis in basis points (bps).
|
|
414
|
+
|
|
415
|
+
This is a convenience method that returns the annualized basis
|
|
416
|
+
rate converted to basis points (1 bp = 0.01%).
|
|
417
|
+
|
|
418
|
+
Args:
|
|
419
|
+
spot: Current spot price
|
|
420
|
+
rate: Risk-free rate
|
|
421
|
+
div_yield: Dividend yield
|
|
422
|
+
time_to_maturity: Time to maturity in years
|
|
423
|
+
observed_futures_price: Optional observed futures price
|
|
424
|
+
|
|
425
|
+
Returns:
|
|
426
|
+
Basis in basis points (e.g., 25.0 = 25 bps)
|
|
427
|
+
|
|
428
|
+
Examples:
|
|
429
|
+
>>> bps = future.calculate_basis_in_bps(
|
|
430
|
+
... spot=4480, rate=0.05, div_yield=0.015,
|
|
431
|
+
... time_to_maturity=0.25, observed_futures_price=4500
|
|
432
|
+
... )
|
|
433
|
+
>>> print(f"Basis: {bps:.1f} bps")
|
|
434
|
+
"""
|
|
435
|
+
annualized = self.calculate_annualized_basis(
|
|
436
|
+
spot, rate, div_yield, time_to_maturity, observed_futures_price
|
|
437
|
+
)
|
|
438
|
+
return annualized * 10000 # Convert to bps
|
|
439
|
+
|
|
440
|
+
def get_implied_dividend_from_basis(
|
|
441
|
+
self,
|
|
442
|
+
spot: float,
|
|
443
|
+
rate: float,
|
|
444
|
+
time_to_maturity: float,
|
|
445
|
+
observed_futures_price: float,
|
|
446
|
+
) -> float:
|
|
447
|
+
"""
|
|
448
|
+
Calculate the implied dividend yield from the observed futures price.
|
|
449
|
+
|
|
450
|
+
Using the cost-of-carry model and assuming zero basis:
|
|
451
|
+
F = S * exp((r - d) * T)
|
|
452
|
+
|
|
453
|
+
Solving for d:
|
|
454
|
+
d = r - (1/T) * ln(F/S)
|
|
455
|
+
|
|
456
|
+
Args:
|
|
457
|
+
spot: Current spot price
|
|
458
|
+
rate: Risk-free rate (continuously compounded)
|
|
459
|
+
time_to_maturity: Time to maturity in years
|
|
460
|
+
observed_futures_price: Observed market futures price
|
|
461
|
+
|
|
462
|
+
Returns:
|
|
463
|
+
Implied dividend yield (as a decimal)
|
|
464
|
+
|
|
465
|
+
Raises:
|
|
466
|
+
ValidationError: If inputs are invalid
|
|
467
|
+
"""
|
|
468
|
+
if spot <= 0:
|
|
469
|
+
raise ValidationError(f"Spot must be positive, got {spot}")
|
|
470
|
+
if observed_futures_price <= 0:
|
|
471
|
+
raise ValidationError(f"Futures price must be positive, got {observed_futures_price}")
|
|
472
|
+
if time_to_maturity <= 0:
|
|
473
|
+
raise ValidationError(f"Time to maturity must be positive, got {time_to_maturity}")
|
|
474
|
+
|
|
475
|
+
futures_ratio = observed_futures_price / spot
|
|
476
|
+
return rate - (math.log(futures_ratio) / time_to_maturity)
|
|
477
|
+
|
|
478
|
+
def __repr__(self):
|
|
479
|
+
mtm_str = f", mtm=${self.market_price:.2f}" if self.market_price else ""
|
|
480
|
+
if self.maturity_date:
|
|
481
|
+
return (f"Futures({self.underlying}, mult={self.multiplier:.1f}, "
|
|
482
|
+
f"maturity_date={self.maturity_date.date()}, basis={self.basis:.4f}{mtm_str})")
|
|
483
|
+
else:
|
|
484
|
+
return (f"Futures({self.underlying}, mult={self.multiplier:.1f}, "
|
|
485
|
+
f"T={self.maturity:.4f}, basis={self.basis:.4f}{mtm_str})")
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Spot instrument implementation for stocks, indices, and ETFs.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import Optional
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from .base_deltaone_product import BaseDeltaOneProduct
|
|
10
|
+
from quantark.util.enum.deltaone_enums import DeltaOneType
|
|
11
|
+
from quantark.util.exceptions import ValidationError
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class SpotInstrument(BaseDeltaOneProduct):
|
|
16
|
+
"""
|
|
17
|
+
Spot instrument (Stock, Index, or ETF).
|
|
18
|
+
|
|
19
|
+
Spot instruments are perpetual holdings with no maturity date.
|
|
20
|
+
Their value follows the underlying asset price with delta = 1.
|
|
21
|
+
|
|
22
|
+
Forward pricing follows cost-of-carry model:
|
|
23
|
+
F(t,T) = S(t) * exp((r - q) * (T - t))
|
|
24
|
+
|
|
25
|
+
Attributes:
|
|
26
|
+
underlying: Identifier for the underlying asset (e.g., ticker symbol)
|
|
27
|
+
deltaone_type: Type of spot instrument (STOCK, INDEX, or ETF)
|
|
28
|
+
|
|
29
|
+
Example:
|
|
30
|
+
>>> stock = SpotInstrument(underlying="AAPL", deltaone_type=DeltaOneType.STOCK)
|
|
31
|
+
>>> etf = SpotInstrument(underlying="SPY", deltaone_type=DeltaOneType.ETF)
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
underlying: str,
|
|
37
|
+
deltaone_type: DeltaOneType,
|
|
38
|
+
):
|
|
39
|
+
"""
|
|
40
|
+
Initialize spot instrument.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
underlying: Identifier for the underlying asset
|
|
44
|
+
deltaone_type: Type of instrument (STOCK, INDEX, or ETF)
|
|
45
|
+
|
|
46
|
+
Raises:
|
|
47
|
+
ValidationError: If deltaone_type is FUTURES (use Futures class instead)
|
|
48
|
+
"""
|
|
49
|
+
if deltaone_type == DeltaOneType.FUTURES:
|
|
50
|
+
raise ValidationError(
|
|
51
|
+
"Use Futures class for futures products, not SpotInstrument"
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Spot instruments are perpetual (no maturity)
|
|
55
|
+
super().__init__(
|
|
56
|
+
underlying=underlying,
|
|
57
|
+
deltaone_type=deltaone_type,
|
|
58
|
+
maturity=None,
|
|
59
|
+
maturity_date=None,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def get_forward_price(
|
|
63
|
+
self,
|
|
64
|
+
spot: float,
|
|
65
|
+
rate: float,
|
|
66
|
+
div_yield: float,
|
|
67
|
+
time_to_maturity: float
|
|
68
|
+
) -> float:
|
|
69
|
+
"""
|
|
70
|
+
Calculate the forward price using cost-of-carry model.
|
|
71
|
+
|
|
72
|
+
Forward price: F(t,T) = S(t) * exp((r - q) * (T - t))
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
spot: Current spot price
|
|
76
|
+
rate: Risk-free rate (continuously compounded)
|
|
77
|
+
div_yield: Dividend yield (continuously compounded)
|
|
78
|
+
time_to_maturity: Time to forward date in years
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Forward price
|
|
82
|
+
|
|
83
|
+
Raises:
|
|
84
|
+
ValidationError: If inputs are invalid
|
|
85
|
+
"""
|
|
86
|
+
if spot <= 0:
|
|
87
|
+
raise ValidationError(f"Spot price must be positive, got {spot}")
|
|
88
|
+
if time_to_maturity < 0:
|
|
89
|
+
raise ValidationError(f"Time to maturity must be non-negative, got {time_to_maturity}")
|
|
90
|
+
|
|
91
|
+
# Cost-of-carry forward pricing
|
|
92
|
+
carry_cost = (rate - div_yield) * time_to_maturity
|
|
93
|
+
forward_price = spot * math.exp(carry_cost)
|
|
94
|
+
|
|
95
|
+
return forward_price
|
|
96
|
+
|
|
97
|
+
def get_current_value(self, spot: float) -> float:
|
|
98
|
+
"""
|
|
99
|
+
Get the current value of the spot instrument.
|
|
100
|
+
|
|
101
|
+
For spot instruments, current value equals spot price.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
spot: Current spot price
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
Current value (equals spot)
|
|
108
|
+
"""
|
|
109
|
+
if spot < 0:
|
|
110
|
+
raise ValidationError(f"Spot price must be non-negative, got {spot}")
|
|
111
|
+
return spot
|
|
112
|
+
|
|
113
|
+
@property
|
|
114
|
+
def is_linear(self) -> bool:
|
|
115
|
+
return True
|
|
116
|
+
|
|
117
|
+
def __repr__(self):
|
|
118
|
+
return f"SpotInstrument({self.underlying}, {self.deltaone_type})"
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Equity option products.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .base_equity_option import BaseEquityOption
|
|
6
|
+
from .european_vanilla_option import EuropeanVanillaOption
|
|
7
|
+
from .american_option import AmericanOption
|
|
8
|
+
from .asian_option import AsianOption, AsianObservationRecord
|
|
9
|
+
from .digital_option import CashOrNothingDigitalOption
|
|
10
|
+
from .barrier_option import BarrierOption
|
|
11
|
+
from .double_barrier_option import DoubleBarrierOption
|
|
12
|
+
from .one_touch_option import OneTouchOption
|
|
13
|
+
from .double_one_touch_option import DoubleOneTouchOption
|
|
14
|
+
from .observation_schedule import (
|
|
15
|
+
ObservationRecord,
|
|
16
|
+
ObservationSchedule,
|
|
17
|
+
ResolvedObservationRecord,
|
|
18
|
+
)
|
|
19
|
+
from .snowball_option import SnowballOption
|
|
20
|
+
from .ko_reset_snowball_option import KnockOutResetSnowballOption
|
|
21
|
+
from .snowball_config import BarrierConfig, PayoffConfig, AccrualConfig
|
|
22
|
+
from .snowball_helpers import (
|
|
23
|
+
create_standard_snowball,
|
|
24
|
+
create_stepdown_snowball,
|
|
25
|
+
create_european_ki_snowball,
|
|
26
|
+
create_parachute_snowball,
|
|
27
|
+
create_airbag_snowball,
|
|
28
|
+
create_ko_reset_snowball,
|
|
29
|
+
generate_ko_observation_dates,
|
|
30
|
+
generate_stepdown_barriers,
|
|
31
|
+
)
|
|
32
|
+
from .phoenix_option import PhoenixOption
|
|
33
|
+
from .phoenix_config import CouponBarrierConfig
|
|
34
|
+
from .phoenix_helpers import (
|
|
35
|
+
create_standard_phoenix,
|
|
36
|
+
create_stepdown_phoenix,
|
|
37
|
+
create_reverse_phoenix,
|
|
38
|
+
create_memory_phoenix,
|
|
39
|
+
create_non_memory_phoenix,
|
|
40
|
+
)
|
|
41
|
+
from .range_accrual_option import RangeAccrualOption
|
|
42
|
+
from .range_accrual_config import RangeAccrualConfig, RangeAccrualObservationRecord
|
|
43
|
+
from .range_accrual_helpers import (
|
|
44
|
+
create_standard_range_accrual,
|
|
45
|
+
create_reverse_range_accrual,
|
|
46
|
+
create_stepdown_range_accrual,
|
|
47
|
+
generate_range_observation_records,
|
|
48
|
+
assign_calendar_day_weights,
|
|
49
|
+
)
|
|
50
|
+
from .single_sharkfin_option import SingleSharkfinOption
|
|
51
|
+
from .double_sharkfin_option import DoubleSharkfinOption
|
|
52
|
+
|
|
53
|
+
__all__ = [
|
|
54
|
+
"BaseEquityOption",
|
|
55
|
+
"EuropeanVanillaOption",
|
|
56
|
+
"AmericanOption",
|
|
57
|
+
"AsianOption",
|
|
58
|
+
"AsianObservationRecord",
|
|
59
|
+
"CashOrNothingDigitalOption",
|
|
60
|
+
"BarrierOption",
|
|
61
|
+
"DoubleBarrierOption",
|
|
62
|
+
"OneTouchOption",
|
|
63
|
+
"DoubleOneTouchOption",
|
|
64
|
+
"ObservationRecord",
|
|
65
|
+
"ObservationSchedule",
|
|
66
|
+
"ResolvedObservationRecord",
|
|
67
|
+
"SnowballOption",
|
|
68
|
+
"KnockOutResetSnowballOption",
|
|
69
|
+
"BarrierConfig",
|
|
70
|
+
"PayoffConfig",
|
|
71
|
+
"AccrualConfig",
|
|
72
|
+
# Snowball helpers
|
|
73
|
+
"create_standard_snowball",
|
|
74
|
+
"create_stepdown_snowball",
|
|
75
|
+
"create_european_ki_snowball",
|
|
76
|
+
"create_parachute_snowball",
|
|
77
|
+
"create_airbag_snowball",
|
|
78
|
+
"create_ko_reset_snowball",
|
|
79
|
+
"generate_ko_observation_dates",
|
|
80
|
+
"generate_stepdown_barriers",
|
|
81
|
+
# Phoenix option
|
|
82
|
+
"PhoenixOption",
|
|
83
|
+
"CouponBarrierConfig",
|
|
84
|
+
# Phoenix helpers
|
|
85
|
+
"create_standard_phoenix",
|
|
86
|
+
"create_stepdown_phoenix",
|
|
87
|
+
"create_reverse_phoenix",
|
|
88
|
+
"create_memory_phoenix",
|
|
89
|
+
"create_non_memory_phoenix",
|
|
90
|
+
# Range Accrual option
|
|
91
|
+
"RangeAccrualOption",
|
|
92
|
+
"RangeAccrualConfig",
|
|
93
|
+
"RangeAccrualObservationRecord",
|
|
94
|
+
# Range Accrual helpers
|
|
95
|
+
"create_standard_range_accrual",
|
|
96
|
+
"create_reverse_range_accrual",
|
|
97
|
+
"create_stepdown_range_accrual",
|
|
98
|
+
"generate_range_observation_records",
|
|
99
|
+
"assign_calendar_day_weights",
|
|
100
|
+
# Single Sharkfin option
|
|
101
|
+
"SingleSharkfinOption",
|
|
102
|
+
# Double Sharkfin option
|
|
103
|
+
"DoubleSharkfinOption",
|
|
104
|
+
]
|