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,283 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Delta-neutral hedging strategy implementation.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Dict, Any, Optional
|
|
6
|
+
from datetime import datetime, timedelta
|
|
7
|
+
from quantark.backtest.strategy.base_strategy import BaseStrategy, AssetClass, HedgingTarget
|
|
8
|
+
from quantark.util.exceptions import ValidationError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DeltaNeutralStrategy(BaseStrategy):
|
|
12
|
+
"""
|
|
13
|
+
Delta-neutral hedging strategy for equity derivatives.
|
|
14
|
+
|
|
15
|
+
This strategy monitors portfolio delta and triggers hedges when:
|
|
16
|
+
1. Delta exceeds a threshold, OR
|
|
17
|
+
2. A rebalance frequency is reached
|
|
18
|
+
|
|
19
|
+
The hedge aims to bring portfolio delta to a target level (typically 0).
|
|
20
|
+
|
|
21
|
+
Attributes:
|
|
22
|
+
delta_threshold: Absolute delta level to trigger hedge (e.g., 100)
|
|
23
|
+
rebalance_frequency: Frequency for periodic rebalancing
|
|
24
|
+
hedge_instrument: Type of hedge ('spot' or 'futures')
|
|
25
|
+
hedge_ratio: Proportion of delta to hedge (0-1, default 1.0)
|
|
26
|
+
target_delta: Target delta after hedging (default 0.0)
|
|
27
|
+
min_time_between_hedges: Minimum time between hedges to avoid over-trading
|
|
28
|
+
|
|
29
|
+
Example:
|
|
30
|
+
>>> strategy = DeltaNeutralStrategy(
|
|
31
|
+
... name="DN_100",
|
|
32
|
+
... delta_threshold=100.0,
|
|
33
|
+
... rebalance_frequency='daily',
|
|
34
|
+
... hedge_instrument='spot'
|
|
35
|
+
... )
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
VALID_FREQUENCIES = ["daily", "hourly", "on_threshold", "continuous"]
|
|
39
|
+
VALID_INSTRUMENTS = ["spot", "futures"]
|
|
40
|
+
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
name: str = "DeltaNeutral",
|
|
44
|
+
delta_threshold: float = 100.0,
|
|
45
|
+
rebalance_frequency: str = "daily",
|
|
46
|
+
hedge_instrument: str = "spot",
|
|
47
|
+
hedge_ratio: float = 1.0,
|
|
48
|
+
target_delta: float = 0.0,
|
|
49
|
+
min_time_between_hedges: Optional[timedelta] = None,
|
|
50
|
+
):
|
|
51
|
+
"""
|
|
52
|
+
Initialize delta-neutral strategy.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
name: Strategy name
|
|
56
|
+
delta_threshold: Absolute delta to trigger hedge
|
|
57
|
+
rebalance_frequency: When to rebalance ('daily', 'hourly', 'on_threshold', 'continuous')
|
|
58
|
+
hedge_instrument: Hedge instrument type ('spot' or 'futures')
|
|
59
|
+
hedge_ratio: Proportion of delta to hedge (0-1)
|
|
60
|
+
target_delta: Target delta after hedging
|
|
61
|
+
min_time_between_hedges: Minimum time between hedges
|
|
62
|
+
|
|
63
|
+
Raises:
|
|
64
|
+
ValidationError: If parameters are invalid
|
|
65
|
+
"""
|
|
66
|
+
super().__init__(
|
|
67
|
+
name=name,
|
|
68
|
+
asset_class=AssetClass.EQUITY,
|
|
69
|
+
hedging_target=HedgingTarget.DELTA,
|
|
70
|
+
hedge_instrument=hedge_instrument,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# Validate parameters
|
|
74
|
+
if delta_threshold < 0:
|
|
75
|
+
raise ValidationError(
|
|
76
|
+
f"Delta threshold must be non-negative, got {delta_threshold}"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
if rebalance_frequency not in self.VALID_FREQUENCIES:
|
|
80
|
+
raise ValidationError(
|
|
81
|
+
f"Invalid rebalance_frequency '{rebalance_frequency}'. "
|
|
82
|
+
f"Must be one of {self.VALID_FREQUENCIES}"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
if hedge_instrument not in self.VALID_INSTRUMENTS:
|
|
86
|
+
raise ValidationError(
|
|
87
|
+
f"Invalid hedge_instrument '{hedge_instrument}'. "
|
|
88
|
+
f"Must be one of {self.VALID_INSTRUMENTS}"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
if not 0 <= hedge_ratio <= 1:
|
|
92
|
+
raise ValidationError(
|
|
93
|
+
f"Hedge ratio must be between 0 and 1, got {hedge_ratio}"
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
self.delta_threshold = delta_threshold
|
|
97
|
+
self.rebalance_frequency = rebalance_frequency
|
|
98
|
+
self.hedge_instrument = hedge_instrument
|
|
99
|
+
self.hedge_ratio = hedge_ratio
|
|
100
|
+
self.target_delta = target_delta
|
|
101
|
+
self.min_time_between_hedges = min_time_between_hedges
|
|
102
|
+
|
|
103
|
+
# Internal state
|
|
104
|
+
self._hedge_count = 0
|
|
105
|
+
self._total_delta_hedged = 0.0
|
|
106
|
+
self._last_rebalance_date: Optional[datetime] = None
|
|
107
|
+
|
|
108
|
+
def should_hedge(
|
|
109
|
+
self,
|
|
110
|
+
current_time: datetime,
|
|
111
|
+
portfolio_greeks: Dict[str, float],
|
|
112
|
+
market_data: Dict[str, float],
|
|
113
|
+
**kwargs,
|
|
114
|
+
) -> bool:
|
|
115
|
+
"""
|
|
116
|
+
Determine if hedging should be performed.
|
|
117
|
+
|
|
118
|
+
Hedging triggers when:
|
|
119
|
+
1. Absolute delta exceeds threshold, AND
|
|
120
|
+
2. Frequency condition is met (if applicable), AND
|
|
121
|
+
3. Minimum time between hedges has elapsed (if set)
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
current_time: Current timestamp
|
|
125
|
+
portfolio_greeks: Portfolio Greeks
|
|
126
|
+
market_data: Market data
|
|
127
|
+
**kwargs: Additional context
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
True if hedging should be executed
|
|
131
|
+
"""
|
|
132
|
+
current_delta = portfolio_greeks.get("delta", 0.0)
|
|
133
|
+
|
|
134
|
+
# Check 1: Does delta exceed threshold?
|
|
135
|
+
delta_exceeds_threshold = abs(current_delta) > self.delta_threshold
|
|
136
|
+
|
|
137
|
+
# Check 2: Has minimum time between hedges elapsed?
|
|
138
|
+
if self.min_time_between_hedges is not None:
|
|
139
|
+
time_since_hedge = self.time_since_last_hedge(current_time)
|
|
140
|
+
if (
|
|
141
|
+
time_since_hedge is not None
|
|
142
|
+
and time_since_hedge < self.min_time_between_hedges
|
|
143
|
+
):
|
|
144
|
+
return False
|
|
145
|
+
|
|
146
|
+
# Check 3: Frequency-based conditions
|
|
147
|
+
if self.rebalance_frequency == "on_threshold":
|
|
148
|
+
# Only hedge when threshold is breached
|
|
149
|
+
return delta_exceeds_threshold
|
|
150
|
+
|
|
151
|
+
elif self.rebalance_frequency == "continuous":
|
|
152
|
+
# Hedge whenever delta exceeds threshold
|
|
153
|
+
return delta_exceeds_threshold
|
|
154
|
+
|
|
155
|
+
elif self.rebalance_frequency == "daily":
|
|
156
|
+
# Hedge once per day if threshold breached
|
|
157
|
+
if not delta_exceeds_threshold:
|
|
158
|
+
return False
|
|
159
|
+
|
|
160
|
+
# Check if we've already hedged today
|
|
161
|
+
if self._last_rebalance_date is not None:
|
|
162
|
+
if current_time.date() == self._last_rebalance_date.date():
|
|
163
|
+
return False
|
|
164
|
+
|
|
165
|
+
return True
|
|
166
|
+
|
|
167
|
+
elif self.rebalance_frequency == "hourly":
|
|
168
|
+
# Hedge once per hour if threshold breached
|
|
169
|
+
if not delta_exceeds_threshold:
|
|
170
|
+
return False
|
|
171
|
+
|
|
172
|
+
# Check if we've hedged this hour
|
|
173
|
+
if self._last_rebalance_date is not None:
|
|
174
|
+
current_hour = current_time.replace(minute=0, second=0, microsecond=0)
|
|
175
|
+
last_hour = self._last_rebalance_date.replace(
|
|
176
|
+
minute=0, second=0, microsecond=0
|
|
177
|
+
)
|
|
178
|
+
if current_hour == last_hour:
|
|
179
|
+
return False
|
|
180
|
+
|
|
181
|
+
return True
|
|
182
|
+
|
|
183
|
+
return False
|
|
184
|
+
|
|
185
|
+
def calculate_hedge_size(
|
|
186
|
+
self,
|
|
187
|
+
current_time: datetime,
|
|
188
|
+
portfolio_greeks: Dict[str, float],
|
|
189
|
+
market_data: Dict[str, float],
|
|
190
|
+
**kwargs,
|
|
191
|
+
) -> float:
|
|
192
|
+
"""
|
|
193
|
+
Calculate hedge size to bring delta to target.
|
|
194
|
+
|
|
195
|
+
Formula:
|
|
196
|
+
hedge_size = -(current_delta - target_delta) * hedge_ratio
|
|
197
|
+
|
|
198
|
+
Positive hedge_size means buy, negative means sell.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
current_time: Current timestamp
|
|
202
|
+
portfolio_greeks: Portfolio Greeks
|
|
203
|
+
market_data: Market data
|
|
204
|
+
**kwargs: Additional context
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
Hedge size (shares/contracts to trade)
|
|
208
|
+
"""
|
|
209
|
+
current_delta = portfolio_greeks.get("delta", 0.0)
|
|
210
|
+
|
|
211
|
+
# Calculate required hedge to reach target delta
|
|
212
|
+
delta_to_hedge = current_delta - self.target_delta
|
|
213
|
+
|
|
214
|
+
# Apply hedge ratio (allows partial hedging)
|
|
215
|
+
hedge_size = -delta_to_hedge * self.hedge_ratio
|
|
216
|
+
|
|
217
|
+
return hedge_size
|
|
218
|
+
|
|
219
|
+
def on_step(
|
|
220
|
+
self,
|
|
221
|
+
current_time: datetime,
|
|
222
|
+
portfolio_greeks: Dict[str, float],
|
|
223
|
+
market_data: Dict[str, float],
|
|
224
|
+
**kwargs,
|
|
225
|
+
):
|
|
226
|
+
"""Update strategy state at each step."""
|
|
227
|
+
# Can be used for custom logic, logging, etc.
|
|
228
|
+
pass
|
|
229
|
+
|
|
230
|
+
def on_hedge_executed(
|
|
231
|
+
self, current_time: datetime, hedge_size: float, hedge_price: float, **kwargs
|
|
232
|
+
):
|
|
233
|
+
"""Update strategy state after hedge execution."""
|
|
234
|
+
super().on_hedge_executed(current_time, hedge_size, hedge_price, **kwargs)
|
|
235
|
+
|
|
236
|
+
self._hedge_count += 1
|
|
237
|
+
self._total_delta_hedged += abs(hedge_size)
|
|
238
|
+
self._last_rebalance_date = current_time
|
|
239
|
+
|
|
240
|
+
def get_parameters(self) -> Dict[str, Any]:
|
|
241
|
+
"""Get strategy parameters."""
|
|
242
|
+
return {
|
|
243
|
+
"name": self.name,
|
|
244
|
+
"delta_threshold": self.delta_threshold,
|
|
245
|
+
"rebalance_frequency": self.rebalance_frequency,
|
|
246
|
+
"hedge_instrument": self.hedge_instrument,
|
|
247
|
+
"hedge_ratio": self.hedge_ratio,
|
|
248
|
+
"target_delta": self.target_delta,
|
|
249
|
+
"min_time_between_hedges": (
|
|
250
|
+
str(self.min_time_between_hedges)
|
|
251
|
+
if self.min_time_between_hedges
|
|
252
|
+
else None
|
|
253
|
+
),
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
def get_statistics(self) -> Dict[str, Any]:
|
|
257
|
+
"""
|
|
258
|
+
Get strategy statistics.
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
Dictionary with hedge statistics
|
|
262
|
+
"""
|
|
263
|
+
return {
|
|
264
|
+
"hedge_count": self._hedge_count,
|
|
265
|
+
"total_delta_hedged": self._total_delta_hedged,
|
|
266
|
+
"last_hedge_time": self._last_hedge_time,
|
|
267
|
+
"last_rebalance_date": self._last_rebalance_date,
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
def reset(self):
|
|
271
|
+
"""Reset strategy state."""
|
|
272
|
+
super().reset()
|
|
273
|
+
self._hedge_count = 0
|
|
274
|
+
self._total_delta_hedged = 0.0
|
|
275
|
+
self._last_rebalance_date = None
|
|
276
|
+
|
|
277
|
+
def __repr__(self) -> str:
|
|
278
|
+
return (
|
|
279
|
+
f"DeltaNeutralStrategy("
|
|
280
|
+
f"threshold={self.delta_threshold}, "
|
|
281
|
+
f"freq={self.rebalance_frequency}, "
|
|
282
|
+
f"instrument={self.hedge_instrument})"
|
|
283
|
+
)
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DV01-neutral hedging strategy for Fixed Income portfolios.
|
|
3
|
+
"""
|
|
4
|
+
from typing import Dict, Any, Optional
|
|
5
|
+
from datetime import datetime, timedelta
|
|
6
|
+
from quantark.backtest.strategy.base_strategy import BaseStrategy, AssetClass, HedgingTarget
|
|
7
|
+
from quantark.util.exceptions import ValidationError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class DV01NeutralStrategy(BaseStrategy):
|
|
11
|
+
"""
|
|
12
|
+
DV01-neutral hedging strategy for Fixed Income portfolios.
|
|
13
|
+
|
|
14
|
+
This strategy monitors portfolio DV01 and triggers hedges when:
|
|
15
|
+
1. DV01 exceeds a threshold, OR
|
|
16
|
+
2. A rebalance frequency is reached
|
|
17
|
+
|
|
18
|
+
The hedge aims to bring portfolio DV01 to a target level (typically 0)
|
|
19
|
+
using bond futures as the hedging instrument.
|
|
20
|
+
|
|
21
|
+
Attributes:
|
|
22
|
+
dv01_threshold: Absolute DV01 level to trigger hedge (e.g., $50,000)
|
|
23
|
+
rebalance_frequency: Frequency for periodic rebalancing
|
|
24
|
+
hedge_instrument: Type of hedge ('bond_futures')
|
|
25
|
+
hedge_ratio: Proportion of DV01 to hedge (0-1, default 1.0)
|
|
26
|
+
target_dv01: Target DV01 after hedging (default 0.0)
|
|
27
|
+
futures_dv01: DV01 per futures contract (for sizing)
|
|
28
|
+
min_time_between_hedges: Minimum time between hedges
|
|
29
|
+
|
|
30
|
+
Example:
|
|
31
|
+
>>> strategy = DV01NeutralStrategy(
|
|
32
|
+
... name="DV01_Neutral",
|
|
33
|
+
... dv01_threshold=50000.0,
|
|
34
|
+
... futures_dv01=1000.0,
|
|
35
|
+
... rebalance_frequency='daily'
|
|
36
|
+
... )
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
VALID_FREQUENCIES = ['daily', 'hourly', 'on_threshold', 'continuous']
|
|
40
|
+
VALID_INSTRUMENTS = ['bond_futures']
|
|
41
|
+
|
|
42
|
+
def __init__(
|
|
43
|
+
self,
|
|
44
|
+
name: str = "DV01Neutral",
|
|
45
|
+
dv01_threshold: float = 50000.0,
|
|
46
|
+
rebalance_frequency: str = 'daily',
|
|
47
|
+
hedge_instrument: str = 'bond_futures',
|
|
48
|
+
hedge_ratio: float = 1.0,
|
|
49
|
+
target_dv01: float = 0.0,
|
|
50
|
+
futures_dv01: float = 1000.0,
|
|
51
|
+
min_time_between_hedges: Optional[timedelta] = None
|
|
52
|
+
):
|
|
53
|
+
"""
|
|
54
|
+
Initialize DV01-neutral strategy.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
name: Strategy name
|
|
58
|
+
dv01_threshold: Absolute DV01 to trigger hedge (in dollars)
|
|
59
|
+
rebalance_frequency: When to rebalance ('daily', 'hourly', 'on_threshold', 'continuous')
|
|
60
|
+
hedge_instrument: Hedge instrument type ('bond_futures')
|
|
61
|
+
hedge_ratio: Proportion of DV01 to hedge (0-1)
|
|
62
|
+
target_dv01: Target DV01 after hedging (default 0)
|
|
63
|
+
futures_dv01: DV01 per futures contract (for hedge sizing)
|
|
64
|
+
min_time_between_hedges: Minimum time between hedges
|
|
65
|
+
|
|
66
|
+
Raises:
|
|
67
|
+
ValidationError: If parameters are invalid
|
|
68
|
+
"""
|
|
69
|
+
super().__init__(
|
|
70
|
+
name=name,
|
|
71
|
+
asset_class=AssetClass.FIXED_INCOME,
|
|
72
|
+
hedging_target=HedgingTarget.DV01,
|
|
73
|
+
hedge_instrument=hedge_instrument
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Validate parameters
|
|
77
|
+
if dv01_threshold < 0:
|
|
78
|
+
raise ValidationError(
|
|
79
|
+
f"DV01 threshold must be non-negative, got {dv01_threshold}"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
if rebalance_frequency not in self.VALID_FREQUENCIES:
|
|
83
|
+
raise ValidationError(
|
|
84
|
+
f"Invalid rebalance_frequency '{rebalance_frequency}'. "
|
|
85
|
+
f"Must be one of {self.VALID_FREQUENCIES}"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
if hedge_instrument not in self.VALID_INSTRUMENTS:
|
|
89
|
+
raise ValidationError(
|
|
90
|
+
f"Invalid hedge_instrument '{hedge_instrument}'. "
|
|
91
|
+
f"Must be one of {self.VALID_INSTRUMENTS}"
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
if not 0 <= hedge_ratio <= 1:
|
|
95
|
+
raise ValidationError(
|
|
96
|
+
f"Hedge ratio must be between 0 and 1, got {hedge_ratio}"
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
if futures_dv01 <= 0:
|
|
100
|
+
raise ValidationError(
|
|
101
|
+
f"Futures DV01 must be positive, got {futures_dv01}"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
self.dv01_threshold = dv01_threshold
|
|
105
|
+
self.rebalance_frequency = rebalance_frequency
|
|
106
|
+
self.hedge_ratio = hedge_ratio
|
|
107
|
+
self.target_dv01 = target_dv01
|
|
108
|
+
self.futures_dv01 = futures_dv01
|
|
109
|
+
self.min_time_between_hedges = min_time_between_hedges
|
|
110
|
+
|
|
111
|
+
# Internal state
|
|
112
|
+
self._hedge_count = 0
|
|
113
|
+
self._total_dv01_hedged = 0.0
|
|
114
|
+
self._last_rebalance_date: Optional[datetime] = None
|
|
115
|
+
|
|
116
|
+
def should_hedge(
|
|
117
|
+
self,
|
|
118
|
+
current_time: datetime,
|
|
119
|
+
portfolio_greeks: Dict[str, float],
|
|
120
|
+
market_data: Dict[str, float],
|
|
121
|
+
**kwargs
|
|
122
|
+
) -> bool:
|
|
123
|
+
"""
|
|
124
|
+
Determine if hedging should be performed.
|
|
125
|
+
|
|
126
|
+
Hedging triggers when:
|
|
127
|
+
1. Absolute DV01 exceeds threshold, AND
|
|
128
|
+
2. Frequency condition is met (if applicable), AND
|
|
129
|
+
3. Minimum time between hedges has elapsed (if set)
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
current_time: Current timestamp
|
|
133
|
+
portfolio_greeks: Portfolio risk measures (should contain 'dv01')
|
|
134
|
+
market_data: Market data
|
|
135
|
+
**kwargs: Additional context
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
True if hedging should be executed
|
|
139
|
+
"""
|
|
140
|
+
current_dv01 = portfolio_greeks.get('dv01', 0.0)
|
|
141
|
+
|
|
142
|
+
# Check 1: Does DV01 exceed threshold?
|
|
143
|
+
dv01_exceeds_threshold = abs(current_dv01) > self.dv01_threshold
|
|
144
|
+
|
|
145
|
+
# Check 2: Has minimum time between hedges elapsed?
|
|
146
|
+
if self.min_time_between_hedges is not None:
|
|
147
|
+
time_since_hedge = self.time_since_last_hedge(current_time)
|
|
148
|
+
if time_since_hedge is not None and time_since_hedge < self.min_time_between_hedges:
|
|
149
|
+
return False
|
|
150
|
+
|
|
151
|
+
# Check 3: Frequency-based conditions
|
|
152
|
+
if self.rebalance_frequency == 'on_threshold':
|
|
153
|
+
return dv01_exceeds_threshold
|
|
154
|
+
|
|
155
|
+
elif self.rebalance_frequency == 'continuous':
|
|
156
|
+
return dv01_exceeds_threshold
|
|
157
|
+
|
|
158
|
+
elif self.rebalance_frequency == 'daily':
|
|
159
|
+
if not dv01_exceeds_threshold:
|
|
160
|
+
return False
|
|
161
|
+
|
|
162
|
+
if self._last_rebalance_date is not None:
|
|
163
|
+
if current_time.date() == self._last_rebalance_date.date():
|
|
164
|
+
return False
|
|
165
|
+
|
|
166
|
+
return True
|
|
167
|
+
|
|
168
|
+
elif self.rebalance_frequency == 'hourly':
|
|
169
|
+
if not dv01_exceeds_threshold:
|
|
170
|
+
return False
|
|
171
|
+
|
|
172
|
+
if self._last_rebalance_date is not None:
|
|
173
|
+
current_hour = current_time.replace(minute=0, second=0, microsecond=0)
|
|
174
|
+
last_hour = self._last_rebalance_date.replace(minute=0, second=0, microsecond=0)
|
|
175
|
+
if current_hour == last_hour:
|
|
176
|
+
return False
|
|
177
|
+
|
|
178
|
+
return True
|
|
179
|
+
|
|
180
|
+
return False
|
|
181
|
+
|
|
182
|
+
def calculate_hedge_size(
|
|
183
|
+
self,
|
|
184
|
+
current_time: datetime,
|
|
185
|
+
portfolio_greeks: Dict[str, float],
|
|
186
|
+
market_data: Dict[str, float],
|
|
187
|
+
**kwargs
|
|
188
|
+
) -> float:
|
|
189
|
+
"""
|
|
190
|
+
Calculate hedge size in number of futures contracts.
|
|
191
|
+
|
|
192
|
+
Formula:
|
|
193
|
+
num_contracts = -(current_dv01 - target_dv01) / futures_dv01 * hedge_ratio
|
|
194
|
+
|
|
195
|
+
Positive = buy futures, Negative = sell futures.
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
current_time: Current timestamp
|
|
199
|
+
portfolio_greeks: Portfolio risk measures
|
|
200
|
+
market_data: Market data
|
|
201
|
+
**kwargs: Additional context
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
Number of futures contracts to trade (can be fractional, round in executor)
|
|
205
|
+
"""
|
|
206
|
+
current_dv01 = portfolio_greeks.get('dv01', 0.0)
|
|
207
|
+
|
|
208
|
+
# Calculate DV01 to hedge
|
|
209
|
+
dv01_to_hedge = current_dv01 - self.target_dv01
|
|
210
|
+
|
|
211
|
+
# Calculate number of contracts (short futures to reduce long DV01)
|
|
212
|
+
num_contracts = -dv01_to_hedge / self.futures_dv01 * self.hedge_ratio
|
|
213
|
+
|
|
214
|
+
return num_contracts
|
|
215
|
+
|
|
216
|
+
def on_step(
|
|
217
|
+
self,
|
|
218
|
+
current_time: datetime,
|
|
219
|
+
portfolio_greeks: Dict[str, float],
|
|
220
|
+
market_data: Dict[str, float],
|
|
221
|
+
**kwargs
|
|
222
|
+
):
|
|
223
|
+
"""Update strategy state at each step."""
|
|
224
|
+
pass
|
|
225
|
+
|
|
226
|
+
def on_hedge_executed(
|
|
227
|
+
self,
|
|
228
|
+
current_time: datetime,
|
|
229
|
+
hedge_size: float,
|
|
230
|
+
hedge_price: float,
|
|
231
|
+
**kwargs
|
|
232
|
+
):
|
|
233
|
+
"""Update strategy state after hedge execution."""
|
|
234
|
+
super().on_hedge_executed(current_time, hedge_size, hedge_price, **kwargs)
|
|
235
|
+
|
|
236
|
+
self._hedge_count += 1
|
|
237
|
+
self._total_dv01_hedged += abs(hedge_size * self.futures_dv01)
|
|
238
|
+
self._last_rebalance_date = current_time
|
|
239
|
+
|
|
240
|
+
def get_parameters(self) -> Dict[str, Any]:
|
|
241
|
+
"""Get strategy parameters."""
|
|
242
|
+
return {
|
|
243
|
+
'name': self.name,
|
|
244
|
+
'asset_class': self.asset_class.value,
|
|
245
|
+
'hedging_target': self.hedging_target.value,
|
|
246
|
+
'dv01_threshold': self.dv01_threshold,
|
|
247
|
+
'rebalance_frequency': self.rebalance_frequency,
|
|
248
|
+
'hedge_instrument': self.hedge_instrument,
|
|
249
|
+
'hedge_ratio': self.hedge_ratio,
|
|
250
|
+
'target_dv01': self.target_dv01,
|
|
251
|
+
'futures_dv01': self.futures_dv01,
|
|
252
|
+
'min_time_between_hedges': str(self.min_time_between_hedges) if self.min_time_between_hedges else None
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
def get_statistics(self) -> Dict[str, Any]:
|
|
256
|
+
"""
|
|
257
|
+
Get strategy statistics.
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
Dictionary with hedge statistics
|
|
261
|
+
"""
|
|
262
|
+
return {
|
|
263
|
+
'hedge_count': self._hedge_count,
|
|
264
|
+
'total_dv01_hedged': self._total_dv01_hedged,
|
|
265
|
+
'last_hedge_time': self._last_hedge_time,
|
|
266
|
+
'last_rebalance_date': self._last_rebalance_date
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
def reset(self):
|
|
270
|
+
"""Reset strategy state."""
|
|
271
|
+
super().reset()
|
|
272
|
+
self._hedge_count = 0
|
|
273
|
+
self._total_dv01_hedged = 0.0
|
|
274
|
+
self._last_rebalance_date = None
|
|
275
|
+
|
|
276
|
+
def __repr__(self) -> str:
|
|
277
|
+
return (
|
|
278
|
+
f"DV01NeutralStrategy("
|
|
279
|
+
f"threshold=${self.dv01_threshold:,.0f}, "
|
|
280
|
+
f"freq={self.rebalance_frequency}, "
|
|
281
|
+
f"futures_dv01=${self.futures_dv01:,.0f})"
|
|
282
|
+
)
|
|
283
|
+
|