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.
Files changed (399) hide show
  1. quantark/__init__.py +3 -0
  2. quantark/_compat.py +150 -0
  3. quantark/asset/__init__.py +8 -0
  4. quantark/asset/bond/__init__.py +2 -0
  5. quantark/asset/bond/engine/__init__.py +44 -0
  6. quantark/asset/bond/engine/analytical/__init__.py +12 -0
  7. quantark/asset/bond/engine/analytical/black_engine.py +583 -0
  8. quantark/asset/bond/engine/analytical/bond_forward_engine.py +390 -0
  9. quantark/asset/bond/engine/analytical/bond_futures_engine.py +569 -0
  10. quantark/asset/bond/engine/convertible/__init__.py +12 -0
  11. quantark/asset/bond/engine/convertible/convertible_bond_engine.py +800 -0
  12. quantark/asset/bond/engine/discount/__init__.py +10 -0
  13. quantark/asset/bond/engine/discount/bond_discount_engine.py +517 -0
  14. quantark/asset/bond/engine/discount/frn_engine.py +913 -0
  15. quantark/asset/bond/engine/pde/__init__.py +14 -0
  16. quantark/asset/bond/engine/pde/convertible/__init__.py +21 -0
  17. quantark/asset/bond/engine/pde/convertible/jump_diffusion_engine.py +603 -0
  18. quantark/asset/bond/engine/pde/convertible/pde_params.py +59 -0
  19. quantark/asset/bond/engine/pde/convertible/tf_engine.py +546 -0
  20. quantark/asset/bond/engine/tree/__init__.py +14 -0
  21. quantark/asset/bond/engine/tree/convertible/__init__.py +21 -0
  22. quantark/asset/bond/engine/tree/convertible/binomial_engine.py +488 -0
  23. quantark/asset/bond/engine/tree/convertible/tree_params.py +72 -0
  24. quantark/asset/bond/engine/tree/convertible/trinomial_engine.py +1341 -0
  25. quantark/asset/bond/product/__init__.py +37 -0
  26. quantark/asset/bond/product/base_bond_product.py +114 -0
  27. quantark/asset/bond/product/convertible/__init__.py +16 -0
  28. quantark/asset/bond/product/convertible/convertible_bond.py +595 -0
  29. quantark/asset/bond/product/couponbond/__init__.py +12 -0
  30. quantark/asset/bond/product/couponbond/fixed_bond.py +285 -0
  31. quantark/asset/bond/product/couponbond/frn.py +538 -0
  32. quantark/asset/bond/product/forward/__init__.py +9 -0
  33. quantark/asset/bond/product/forward/base_bond_forward.py +92 -0
  34. quantark/asset/bond/product/forward/bond_forward.py +335 -0
  35. quantark/asset/bond/product/futures/__init__.py +8 -0
  36. quantark/asset/bond/product/futures/bond_futures.py +532 -0
  37. quantark/asset/bond/product/option/__init__.py +9 -0
  38. quantark/asset/bond/product/option/euro_short_term_bond_option.py +231 -0
  39. quantark/asset/bond/riskmeasures/__init__.py +13 -0
  40. quantark/asset/bond/riskmeasures/bond_greeks_calculator.py +484 -0
  41. quantark/asset/bond/schedule/__init__.py +21 -0
  42. quantark/asset/bond/schedule/cashflow.py +595 -0
  43. quantark/asset/equity/__init__.py +11 -0
  44. quantark/asset/equity/analysis/__init__.py +4 -0
  45. quantark/asset/equity/analysis/autocallable_path_analyzer.py +257 -0
  46. quantark/asset/equity/engine/__init__.py +84 -0
  47. quantark/asset/equity/engine/analytical/__init__.py +37 -0
  48. quantark/asset/equity/engine/analytical/american_option_engine.py +682 -0
  49. quantark/asset/equity/engine/analytical/asian_option_analytical_engine.py +1102 -0
  50. quantark/asset/equity/engine/analytical/barrier_analytical_engine.py +455 -0
  51. quantark/asset/equity/engine/analytical/black_scholes_engine.py +322 -0
  52. quantark/asset/equity/engine/analytical/deltaone_engine.py +340 -0
  53. quantark/asset/equity/engine/analytical/digital_option_engine.py +168 -0
  54. quantark/asset/equity/engine/analytical/double_barrier_option_engine.py +481 -0
  55. quantark/asset/equity/engine/analytical/double_sharkfin_option_analytical_engine.py +508 -0
  56. quantark/asset/equity/engine/analytical/one_touch_analytical_engine.py +302 -0
  57. quantark/asset/equity/engine/analytical/range_accrual_analytical_engine.py +396 -0
  58. quantark/asset/equity/engine/analytical/single_sharkfin_option_analytical_engine.py +229 -0
  59. quantark/asset/equity/engine/base_engine.py +137 -0
  60. quantark/asset/equity/engine/event_stats.py +85 -0
  61. quantark/asset/equity/engine/mc/__init__.py +31 -0
  62. quantark/asset/equity/engine/mc/american_option_mc_engine.py +485 -0
  63. quantark/asset/equity/engine/mc/asian_option_mc_engine.py +678 -0
  64. quantark/asset/equity/engine/mc/barrier_option_mc_engine.py +726 -0
  65. quantark/asset/equity/engine/mc/digital_option_mc_engine.py +419 -0
  66. quantark/asset/equity/engine/mc/double_sharkfin_option_mc_engine.py +676 -0
  67. quantark/asset/equity/engine/mc/euro_mc_engine.py +423 -0
  68. quantark/asset/equity/engine/mc/phoenix_mc_engine.py +1206 -0
  69. quantark/asset/equity/engine/mc/range_accrual_mc_engine.py +738 -0
  70. quantark/asset/equity/engine/mc/single_sharkfin_option_mc_engine.py +549 -0
  71. quantark/asset/equity/engine/mc/snowball_mc_engine.py +2250 -0
  72. quantark/asset/equity/engine/pde/__init__.py +36 -0
  73. quantark/asset/equity/engine/pde/american_pde_solver.py +211 -0
  74. quantark/asset/equity/engine/pde/barrier_pde_solver.py +692 -0
  75. quantark/asset/equity/engine/pde/base_pde_solver.py +994 -0
  76. quantark/asset/equity/engine/pde/double_barrier_pde_solver.py +510 -0
  77. quantark/asset/equity/engine/pde/double_one_touch_pde_solver.py +435 -0
  78. quantark/asset/equity/engine/pde/european_pde_solver.py +170 -0
  79. quantark/asset/equity/engine/pde/ko_reset_snowball_pde_solver.py +477 -0
  80. quantark/asset/equity/engine/pde/one_touch_pde_solver.py +439 -0
  81. quantark/asset/equity/engine/pde/phoenix_pde_solver.py +613 -0
  82. quantark/asset/equity/engine/pde/snowball_pde_solver.py +1810 -0
  83. quantark/asset/equity/engine/pde/spatial_grid.py +750 -0
  84. quantark/asset/equity/engine/pde/time_grid.py +308 -0
  85. quantark/asset/equity/engine/pde_engine.py +238 -0
  86. quantark/asset/equity/engine/quad/__init__.py +23 -0
  87. quantark/asset/equity/engine/quad/discrete_quad_engine.py +106 -0
  88. quantark/asset/equity/engine/quad/european_quad_engine.py +325 -0
  89. quantark/asset/equity/engine/quad/ko_reset_snowball_quad_engine.py +362 -0
  90. quantark/asset/equity/engine/quad/phoenix_quad_engine.py +614 -0
  91. quantark/asset/equity/engine/quad/quad_adapters.py +1260 -0
  92. quantark/asset/equity/engine/quad/quad_core.py +513 -0
  93. quantark/asset/equity/engine/quad/quad_math.py +219 -0
  94. quantark/asset/equity/engine/quad/snowball_quad_engine.py +1137 -0
  95. quantark/asset/equity/engine/validation/script/benchmark_check_american_analytical.py +117 -0
  96. quantark/asset/equity/engine/validation/script/benchmark_check_american_pde.py +114 -0
  97. quantark/asset/equity/engine/validation/script/benchmark_check_asian_analytical.py +440 -0
  98. quantark/asset/equity/engine/validation/script/benchmark_check_barrier_analytical.py +269 -0
  99. quantark/asset/equity/engine/validation/script/benchmark_check_barrier_pde_solver.py +636 -0
  100. quantark/asset/equity/engine/validation/script/benchmark_check_digital_option.py +256 -0
  101. quantark/asset/equity/engine/validation/script/benchmark_check_snowball_pde_solver.py +807 -0
  102. quantark/asset/equity/engine/validation/script/boundary_check_american_analytical.py +290 -0
  103. quantark/asset/equity/engine/validation/script/boundary_check_american_pde.py +242 -0
  104. quantark/asset/equity/engine/validation/script/boundary_check_asian_analytical.py +612 -0
  105. quantark/asset/equity/engine/validation/script/boundary_check_barrier_analytical.py +434 -0
  106. quantark/asset/equity/engine/validation/script/boundary_check_barrier_pde_solver.py +748 -0
  107. quantark/asset/equity/engine/validation/script/boundary_check_digital_option.py +575 -0
  108. quantark/asset/equity/engine/validation/script/boundary_check_snowball_pde_solver.py +1101 -0
  109. quantark/asset/equity/engine/validation/script/greeks_check_digital_option.py +349 -0
  110. quantark/asset/equity/engine/validation/script/mc_comparison_barrier_pde.py +270 -0
  111. quantark/asset/equity/engine/validation/script/quick_mc_compare.py +51 -0
  112. quantark/asset/equity/engine/validation/script/validation_stepdown_improved.py +97 -0
  113. quantark/asset/equity/param/__init__.py +24 -0
  114. quantark/asset/equity/param/engine_param_profiles.py +325 -0
  115. quantark/asset/equity/param/engine_params.py +728 -0
  116. quantark/asset/equity/process/__init__.py +7 -0
  117. quantark/asset/equity/process/bsm/__init__.py +7 -0
  118. quantark/asset/equity/process/bsm/bsm_process.py +108 -0
  119. quantark/asset/equity/process/bsm/qmc_brownian_bridge.py +401 -0
  120. quantark/asset/equity/process/bsm/qmc_path_generator.py +694 -0
  121. quantark/asset/equity/process/bsm/qmc_rqmc_driver.py +163 -0
  122. quantark/asset/equity/process/bsm/qmc_sobol.py +195 -0
  123. quantark/asset/equity/process/bsm/qmc_variance_reduction.py +292 -0
  124. quantark/asset/equity/product/__init__.py +8 -0
  125. quantark/asset/equity/product/base_equity_product.py +72 -0
  126. quantark/asset/equity/product/deltaone/__init__.py +22 -0
  127. quantark/asset/equity/product/deltaone/base_deltaone_product.py +147 -0
  128. quantark/asset/equity/product/deltaone/futures.py +485 -0
  129. quantark/asset/equity/product/deltaone/spot_instrument.py +118 -0
  130. quantark/asset/equity/product/option/__init__.py +104 -0
  131. quantark/asset/equity/product/option/american_option.py +114 -0
  132. quantark/asset/equity/product/option/asian_option.py +531 -0
  133. quantark/asset/equity/product/option/barrier_option.py +289 -0
  134. quantark/asset/equity/product/option/base_equity_option.py +659 -0
  135. quantark/asset/equity/product/option/digital_option.py +102 -0
  136. quantark/asset/equity/product/option/double_barrier_option.py +286 -0
  137. quantark/asset/equity/product/option/double_one_touch_option.py +310 -0
  138. quantark/asset/equity/product/option/double_sharkfin_option.py +466 -0
  139. quantark/asset/equity/product/option/european_vanilla_option.py +103 -0
  140. quantark/asset/equity/product/option/ko_reset_snowball_option.py +563 -0
  141. quantark/asset/equity/product/option/observation_schedule.py +530 -0
  142. quantark/asset/equity/product/option/one_touch_option.py +287 -0
  143. quantark/asset/equity/product/option/phoenix_config.py +116 -0
  144. quantark/asset/equity/product/option/phoenix_helpers.py +576 -0
  145. quantark/asset/equity/product/option/phoenix_option.py +1167 -0
  146. quantark/asset/equity/product/option/range_accrual_config.py +288 -0
  147. quantark/asset/equity/product/option/range_accrual_helpers.py +608 -0
  148. quantark/asset/equity/product/option/range_accrual_option.py +526 -0
  149. quantark/asset/equity/product/option/single_sharkfin_option.py +420 -0
  150. quantark/asset/equity/product/option/snowball_config.py +261 -0
  151. quantark/asset/equity/product/option/snowball_helpers.py +977 -0
  152. quantark/asset/equity/product/option/snowball_option.py +1242 -0
  153. quantark/asset/equity/report/__init__.py +15 -0
  154. quantark/asset/equity/report/autocallable_risk_report.py +2118 -0
  155. quantark/asset/equity/report/plotting.py +87 -0
  156. quantark/asset/equity/report/snowball_risk_comparison_report.py +2230 -0
  157. quantark/asset/equity/report/surfaces.py +123 -0
  158. quantark/asset/equity/report/term_structure.py +126 -0
  159. quantark/asset/equity/riskmeasures/__init__.py +7 -0
  160. quantark/asset/equity/riskmeasures/greeks_calculator.py +1204 -0
  161. quantark/asset/rate/__init__.py +58 -0
  162. quantark/asset/rate/engine/__init__.py +25 -0
  163. quantark/asset/rate/engine/cap_floor_engine.py +514 -0
  164. quantark/asset/rate/engine/fra_engine.py +286 -0
  165. quantark/asset/rate/engine/irs_discount_engine.py +891 -0
  166. quantark/asset/rate/engine/swaption_engine.py +587 -0
  167. quantark/asset/rate/product/__init__.py +67 -0
  168. quantark/asset/rate/product/cap_floor.py +550 -0
  169. quantark/asset/rate/product/fra.py +219 -0
  170. quantark/asset/rate/product/irs.py +1223 -0
  171. quantark/asset/rate/product/swaption.py +372 -0
  172. quantark/backtest/__init__.py +153 -0
  173. quantark/backtest/base.py +263 -0
  174. quantark/backtest/dashboard.py +874 -0
  175. quantark/backtest/equity/__init__.py +35 -0
  176. quantark/backtest/equity/config.py +118 -0
  177. quantark/backtest/equity/engine.py +408 -0
  178. quantark/backtest/equity/hedge_executor.py +374 -0
  179. quantark/backtest/equity/metrics.py +396 -0
  180. quantark/backtest/equity/results.py +232 -0
  181. quantark/backtest/equity/state.py +252 -0
  182. quantark/backtest/examples/__init__.py +4 -0
  183. quantark/backtest/examples/advanced_backtest.py +345 -0
  184. quantark/backtest/examples/basic_delta_hedge.py +246 -0
  185. quantark/backtest/examples/fi_dv01_hedge.py +267 -0
  186. quantark/backtest/fi/__init__.py +30 -0
  187. quantark/backtest/fi/config.py +114 -0
  188. quantark/backtest/fi/engine.py +378 -0
  189. quantark/backtest/fi/hedge_executor.py +254 -0
  190. quantark/backtest/fi/metrics.py +308 -0
  191. quantark/backtest/fi/results.py +193 -0
  192. quantark/backtest/fi/state.py +212 -0
  193. quantark/backtest/logger.py +393 -0
  194. quantark/backtest/otc/__init__.py +74 -0
  195. quantark/backtest/otc/_replay.py +637 -0
  196. quantark/backtest/otc/book_engine.py +587 -0
  197. quantark/backtest/otc/config.py +175 -0
  198. quantark/backtest/otc/dashboard.py +1006 -0
  199. quantark/backtest/otc/engine.py +420 -0
  200. quantark/backtest/otc/engine_factory.py +138 -0
  201. quantark/backtest/otc/market.py +216 -0
  202. quantark/backtest/otc/results.py +107 -0
  203. quantark/backtest/otc/state.py +166 -0
  204. quantark/backtest/report_generator.py +608 -0
  205. quantark/backtest/strategy/__init__.py +28 -0
  206. quantark/backtest/strategy/base_strategy.py +235 -0
  207. quantark/backtest/strategy/convexity_neutral_strategy.py +247 -0
  208. quantark/backtest/strategy/delta_neutral_strategy.py +283 -0
  209. quantark/backtest/strategy/dv01_neutral_strategy.py +283 -0
  210. quantark/backtest/transaction_costs.py +485 -0
  211. quantark/backtest/visualizer.py +1019 -0
  212. quantark/cashleg/__init__.py +31 -0
  213. quantark/cashleg/accrual_leg.py +120 -0
  214. quantark/cashleg/base.py +48 -0
  215. quantark/cashleg/base_amount.py +60 -0
  216. quantark/cashleg/deterministic_leg.py +39 -0
  217. quantark/cashleg/event_distribution.py +262 -0
  218. quantark/cashleg/fixed_payoff_leg.py +92 -0
  219. quantark/cashleg/leg_schedule.py +95 -0
  220. quantark/cashleg/leg_valuator.py +40 -0
  221. quantark/dynamicscenario/__init__.py +97 -0
  222. quantark/dynamicscenario/base.py +297 -0
  223. quantark/dynamicscenario/config.py +122 -0
  224. quantark/dynamicscenario/engine.py +703 -0
  225. quantark/dynamicscenario/equity/__init__.py +14 -0
  226. quantark/dynamicscenario/fi/__init__.py +24 -0
  227. quantark/dynamicscenario/fi/config.py +149 -0
  228. quantark/dynamicscenario/fi/engine.py +500 -0
  229. quantark/dynamicscenario/fi/results.py +503 -0
  230. quantark/dynamicscenario/path/__init__.py +17 -0
  231. quantark/dynamicscenario/path/day_path.py +397 -0
  232. quantark/dynamicscenario/path/fi_path_library.py +488 -0
  233. quantark/dynamicscenario/path/path_builder.py +726 -0
  234. quantark/dynamicscenario/path/path_library.py +620 -0
  235. quantark/dynamicscenario/report/__init__.py +12 -0
  236. quantark/dynamicscenario/report/dynamic_report.py +1175 -0
  237. quantark/dynamicscenario/report/visualizer.py +1586 -0
  238. quantark/dynamicscenario/results/__init__.py +19 -0
  239. quantark/dynamicscenario/results/dynamic_results.py +579 -0
  240. quantark/dynamicscenario/results/result_exporter.py +438 -0
  241. quantark/param/__init__.py +75 -0
  242. quantark/param/basis/__init__.py +19 -0
  243. quantark/param/basis/basis_yield.py +301 -0
  244. quantark/param/div/__init__.py +16 -0
  245. quantark/param/div/dividend_yield.py +123 -0
  246. quantark/param/index/__init__.py +52 -0
  247. quantark/param/index/rate_index.py +568 -0
  248. quantark/param/quote/__init__.py +7 -0
  249. quantark/param/quote/spot_quote.py +35 -0
  250. quantark/param/rrf/__init__.py +22 -0
  251. quantark/param/rrf/rate_curve.py +436 -0
  252. quantark/param/vol/__init__.py +6 -0
  253. quantark/param/vol/vol_surface.py +118 -0
  254. quantark/portfolio/__init__.py +61 -0
  255. quantark/portfolio/base.py +203 -0
  256. quantark/portfolio/equity/__init__.py +17 -0
  257. quantark/portfolio/equity/portfolio.py +391 -0
  258. quantark/portfolio/equity/position.py +368 -0
  259. quantark/portfolio/fi/__init__.py +14 -0
  260. quantark/portfolio/fi/portfolio.py +424 -0
  261. quantark/portfolio/fi/position.py +272 -0
  262. quantark/portfolio/portfolio_snapshot.py +221 -0
  263. quantark/portfolio/portfolio_storage.py +414 -0
  264. quantark/priceenv/__init__.py +7 -0
  265. quantark/priceenv/pricing_environment.py +196 -0
  266. quantark/rfq/__init__.py +32 -0
  267. quantark/rfq/builders.py +102 -0
  268. quantark/rfq/models.py +214 -0
  269. quantark/rfq/registry.py +611 -0
  270. quantark/rfq/service.py +237 -0
  271. quantark/simm/__init__.py +155 -0
  272. quantark/simm/calibration/__init__.py +206 -0
  273. quantark/simm/calibration/accessors.py +439 -0
  274. quantark/simm/calibration/commodity.py +156 -0
  275. quantark/simm/calibration/credit_non_qualifying.py +79 -0
  276. quantark/simm/calibration/credit_qualifying.py +130 -0
  277. quantark/simm/calibration/cross_risk.py +39 -0
  278. quantark/simm/calibration/equity.py +125 -0
  279. quantark/simm/calibration/fx.py +92 -0
  280. quantark/simm/calibration/ir.py +152 -0
  281. quantark/simm/calibration/version.py +33 -0
  282. quantark/simm/config.py +186 -0
  283. quantark/simm/crif/__init__.py +35 -0
  284. quantark/simm/crif/models.py +230 -0
  285. quantark/simm/crif/parser.py +585 -0
  286. quantark/simm/engines/__init__.py +62 -0
  287. quantark/simm/engines/aggregation/__init__.py +67 -0
  288. quantark/simm/engines/aggregation/addon.py +141 -0
  289. quantark/simm/engines/aggregation/bucket_aggregator.py +298 -0
  290. quantark/simm/engines/aggregation/concentration.py +349 -0
  291. quantark/simm/engines/aggregation/product_class_aggregator.py +183 -0
  292. quantark/simm/engines/aggregation/risk_class_aggregator.py +403 -0
  293. quantark/simm/engines/aggregation/simm_calculator.py +430 -0
  294. quantark/simm/engines/aggregation/weighted_sensitivity.py +272 -0
  295. quantark/simm/engines/base.py +231 -0
  296. quantark/simm/engines/classification/__init__.py +10 -0
  297. quantark/simm/engines/classification/bucket_mapper.py +347 -0
  298. quantark/simm/engines/factory.py +137 -0
  299. quantark/simm/engines/portfolio_adapter.py +336 -0
  300. quantark/simm/engines/result.py +176 -0
  301. quantark/simm/engines/risk_class/__init__.py +18 -0
  302. quantark/simm/engines/risk_class/equity_engine.py +263 -0
  303. quantark/simm/engines/risk_class/ir_engine.py +264 -0
  304. quantark/simm/report/__init__.py +17 -0
  305. quantark/simm/report/crif_export.py +284 -0
  306. quantark/simm/report/excel_generator.py +401 -0
  307. quantark/simm/report/html_generator.py +840 -0
  308. quantark/simm/results/__init__.py +38 -0
  309. quantark/simm/results/attribution.py +313 -0
  310. quantark/simm/results/simm_result.py +339 -0
  311. quantark/simm/results/whatif.py +268 -0
  312. quantark/simm/sensitivity.py +533 -0
  313. quantark/simm/taxonomy.py +416 -0
  314. quantark/stresstest/__init__.py +67 -0
  315. quantark/stresstest/base.py +116 -0
  316. quantark/stresstest/config.py +5 -0
  317. quantark/stresstest/engine.py +5 -0
  318. quantark/stresstest/equity/__init__.py +17 -0
  319. quantark/stresstest/equity/config.py +69 -0
  320. quantark/stresstest/equity/engine.py +272 -0
  321. quantark/stresstest/equity/report/__init__.py +7 -0
  322. quantark/stresstest/equity/report/report_generator.py +423 -0
  323. quantark/stresstest/equity/report/visualizer.py +328 -0
  324. quantark/stresstest/equity/results.py +145 -0
  325. quantark/stresstest/fi/__init__.py +15 -0
  326. quantark/stresstest/fi/config.py +59 -0
  327. quantark/stresstest/fi/engine.py +213 -0
  328. quantark/stresstest/fi/metrics.py +60 -0
  329. quantark/stresstest/fi/results.py +64 -0
  330. quantark/stresstest/report/__init__.py +12 -0
  331. quantark/stresstest/report/report_generator.py +5 -0
  332. quantark/stresstest/report/visualizer.py +5 -0
  333. quantark/stresstest/results/__init__.py +16 -0
  334. quantark/stresstest/results/result_aggregator.py +325 -0
  335. quantark/stresstest/results/result_exporter.py +286 -0
  336. quantark/stresstest/results/stress_results.py +5 -0
  337. quantark/stresstest/scenario/__init__.py +13 -0
  338. quantark/stresstest/scenario/scenario.py +242 -0
  339. quantark/stresstest/scenario/scenario_builder.py +376 -0
  340. quantark/stresstest/scenario/scenario_library.py +435 -0
  341. quantark/stresstest/scenario/scenario_storage.py +224 -0
  342. quantark/stresstest/stress/__init__.py +13 -0
  343. quantark/stresstest/stress/stress_applicator.py +590 -0
  344. quantark/stresstest/stress/stress_types.py +142 -0
  345. quantark/util/__init__.py +23 -0
  346. quantark/util/barrier_shift.py +44 -0
  347. quantark/util/calendar/__init__.py +27 -0
  348. quantark/util/calendar/business_calendar.py +584 -0
  349. quantark/util/calendar/day_counter.py +517 -0
  350. quantark/util/calendar/holidayfile/china.csv +1920 -0
  351. quantark/util/calendar/holidayfile/china_sse.csv +1462 -0
  352. quantark/util/enum/__init__.py +81 -0
  353. quantark/util/enum/bond_enums.py +112 -0
  354. quantark/util/enum/deltaone_enums.py +16 -0
  355. quantark/util/enum/engine_enums.py +137 -0
  356. quantark/util/enum/greeks_enums.py +29 -0
  357. quantark/util/enum/option_enums.py +221 -0
  358. quantark/util/exceptions.py +66 -0
  359. quantark/util/marketdata/__init__.py +39 -0
  360. quantark/util/marketdata/adapter/base_adapter.py +203 -0
  361. quantark/util/marketdata/adapter/mock_adapter.py +265 -0
  362. quantark/util/marketdata/converter.py +289 -0
  363. quantark/util/marketdata/example_usage.py +314 -0
  364. quantark/util/marketdata/generator/__init__.py +7 -0
  365. quantark/util/marketdata/generator/mock_generator.py +466 -0
  366. quantark/util/marketdata/models.py +358 -0
  367. quantark/util/marketdata/storage/__init__.py +7 -0
  368. quantark/util/marketdata/storage/parquet_storage.py +340 -0
  369. quantark/util/numerical/__init__.py +98 -0
  370. quantark/util/numerical/comparison.py +219 -0
  371. quantark/util/numerical/constants.py +98 -0
  372. quantark/util/numerical/formatting.py +380 -0
  373. quantark/util/numerical/pnl.py +17 -0
  374. quantark/util/numerical/safe_math.py +238 -0
  375. quantark/util/numerical/validation.py +315 -0
  376. quantark/var/__init__.py +39 -0
  377. quantark/var/attribution.py +398 -0
  378. quantark/var/backtest/__init__.py +7 -0
  379. quantark/var/backtest/var_backtester.py +309 -0
  380. quantark/var/base.py +63 -0
  381. quantark/var/config.py +219 -0
  382. quantark/var/engines/__init__.py +13 -0
  383. quantark/var/engines/historical.py +925 -0
  384. quantark/var/engines/monte_carlo.py +870 -0
  385. quantark/var/engines/parametric.py +1199 -0
  386. quantark/var/results/__init__.py +16 -0
  387. quantark/var/results/incremental_var_result.py +131 -0
  388. quantark/var/results/var_report.py +346 -0
  389. quantark/var/results/var_result.py +134 -0
  390. quantark/var/risk_factors/__init__.py +22 -0
  391. quantark/var/risk_factors/base.py +41 -0
  392. quantark/var/risk_factors/equity_factors.py +158 -0
  393. quantark/var/risk_factors/fi_factors.py +99 -0
  394. quantark-0.1.0.dist-info/METADATA +351 -0
  395. quantark-0.1.0.dist-info/RECORD +399 -0
  396. quantark-0.1.0.dist-info/WHEEL +4 -0
  397. quantark-0.1.0.dist-info/licenses/LICENSE +202 -0
  398. quantark-0.1.0.dist-info/licenses/NOTICE +2 -0
  399. quantark_compat.pth +1 -0
@@ -0,0 +1,163 @@
1
+ """
2
+ Created on Mon Nov 17 2025
3
+
4
+ @description: RQMC batching and adaptive stopping driver for Monte Carlo
5
+ pricing engines using GBMPathGenerator or similar path
6
+ generators.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from dataclasses import dataclass
12
+ from typing import Callable, Dict, Optional, Protocol, Tuple
13
+
14
+ import numpy as np
15
+
16
+
17
+ class PathGenerator(Protocol):
18
+ """
19
+ Minimal protocol for path generators compatible with the RQMC driver.
20
+ """
21
+
22
+ num_paths: int
23
+
24
+ def generate_paths(
25
+ self,
26
+ seed: Optional[int] = None,
27
+ batch_id: Optional[int] = None,
28
+ return_aux: bool = False,
29
+ ) -> Tuple[np.ndarray, Optional[Dict[str, np.ndarray]]]:
30
+ ...
31
+
32
+
33
+ PricerFn = Callable[[np.ndarray, Optional[Dict[str, np.ndarray]]], np.ndarray]
34
+
35
+
36
+ @dataclass
37
+ class RQMCResult:
38
+ """
39
+ Result container for RQMC batching runs.
40
+ """
41
+
42
+ price: float
43
+ std_error: float
44
+ total_paths: int
45
+ batches_used: int
46
+ batch_means: np.ndarray
47
+
48
+
49
+ def run_rqmc(
50
+ pricer_fn: PricerFn,
51
+ path_generator: PathGenerator,
52
+ max_batches: int,
53
+ target_std: float,
54
+ min_batches: int = 1,
55
+ ) -> RQMCResult:
56
+ """
57
+ Run randomized QMC (RQMC) in batches with adaptive stopping.
58
+
59
+ Each batch corresponds to an independent scrambled Sobol sequence (or
60
+ independent pseudorandom run), and the estimator is the average of the
61
+ batch means. The standard error is based on the sample variance of the
62
+ batch means.
63
+
64
+ Parameters
65
+ ----------
66
+ pricer_fn : callable
67
+ Function with signature pricer_fn(paths, aux) -> payoffs_per_path,
68
+ where paths is an array of shape (n_paths, n_steps + 1) and aux is
69
+ a dictionary with auxiliary data (e.g., importance weights).
70
+ path_generator : PathGenerator
71
+ Path generator implementing the PathGenerator protocol.
72
+ max_batches : int
73
+ Maximum number of RQMC batches to run.
74
+ target_std : float
75
+ Target standard error for stopping the simulation.
76
+ min_batches : int, optional
77
+ Minimum number of batches to run before checking the stopping
78
+ criterion.
79
+
80
+ Returns
81
+ -------
82
+ RQMCResult
83
+ Result object containing the estimated price, standard error,
84
+ total number of paths used, and per-batch means.
85
+ """
86
+ if max_batches <= 0:
87
+ raise ValueError("max_batches must be positive")
88
+ if min_batches <= 0:
89
+ raise ValueError("min_batches must be positive")
90
+ if min_batches > max_batches:
91
+ raise ValueError("min_batches cannot exceed max_batches")
92
+ if target_std <= 0.0:
93
+ raise ValueError("target_std must be positive")
94
+
95
+ batch_means = []
96
+ n_paths_per_batch = path_generator.num_paths
97
+
98
+ # Welford's algorithm over batch means
99
+ mean = 0.0
100
+ m2 = 0.0
101
+
102
+ for batch_id in range(max_batches):
103
+ paths, aux = path_generator.generate_paths(batch_id=batch_id, return_aux=True)
104
+ payoffs = pricer_fn(paths, aux)
105
+ payoffs = np.asarray(payoffs, dtype=float)
106
+ if payoffs.ndim != 1 or payoffs.shape[0] != n_paths_per_batch:
107
+ raise ValueError(
108
+ "pricer_fn must return a 1D array with one payoff per path "
109
+ f"(expected length {n_paths_per_batch}, got {payoffs.shape})."
110
+ )
111
+
112
+ batch_mean = float(payoffs.mean())
113
+ batch_means.append(batch_mean)
114
+
115
+ n = batch_id + 1
116
+ delta = batch_mean - mean
117
+ mean += delta / n
118
+ m2 += delta * (batch_mean - mean)
119
+
120
+ if n >= min_batches:
121
+ if n > 1:
122
+ variance = m2 / (n - 1)
123
+ else:
124
+ variance = 0.0
125
+ std_error = np.sqrt(variance / n)
126
+
127
+ if std_error <= target_std or n == max_batches:
128
+ return RQMCResult(
129
+ price=mean,
130
+ std_error=std_error,
131
+ total_paths=n * n_paths_per_batch,
132
+ batches_used=n,
133
+ batch_means=np.array(batch_means, dtype=float),
134
+ )
135
+
136
+ # Fallback (should not normally reach here)
137
+ if len(batch_means) == 0:
138
+ raise RuntimeError("No batches were run in RQMC driver.")
139
+
140
+ n = len(batch_means)
141
+ batch_means_arr = np.array(batch_means, dtype=float)
142
+ mean = float(batch_means_arr.mean())
143
+ if n > 1:
144
+ variance = float(batch_means_arr.var(ddof=1))
145
+ else:
146
+ variance = 0.0
147
+ std_error = np.sqrt(variance / n)
148
+
149
+ return RQMCResult(
150
+ price=mean,
151
+ std_error=std_error,
152
+ total_paths=n * n_paths_per_batch,
153
+ batches_used=n,
154
+ batch_means=batch_means_arr,
155
+ )
156
+
157
+
158
+ __all__ = [
159
+ "RQMCResult",
160
+ "run_rqmc",
161
+ ]
162
+
163
+
@@ -0,0 +1,195 @@
1
+ """
2
+ Created on Mon Nov 17 2025
3
+
4
+ @author: yaofuxin
5
+ @description: Sobol-based and pseudorandom normal generators for MC/QMC path
6
+ construction, with support for RQMC batching.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from dataclasses import dataclass
12
+ from typing import Optional, Protocol
13
+
14
+ import numpy as np
15
+
16
+ try:
17
+ from scipy import special
18
+ from scipy.stats import qmc
19
+
20
+ HAS_SCIPY_QMC = True
21
+ except ImportError: # pragma: no cover - fallback path without SciPy
22
+ HAS_SCIPY_QMC = False
23
+ special = None # type: ignore
24
+ qmc = None # type: ignore
25
+
26
+
27
+ class RandomStream(Protocol):
28
+ """
29
+ Minimal interface for random streams used by path generators.
30
+
31
+ Implementations should return standard normal random numbers with shape
32
+ (n_paths, dim). The optional batch_id is used to generate independent
33
+ randomized QMC batches.
34
+ """
35
+
36
+ def normal(
37
+ self, n_paths: int, dim: int, batch_id: Optional[int] = None
38
+ ) -> np.ndarray:
39
+ """
40
+ Generate standard normal random numbers.
41
+
42
+ Parameters
43
+ ----------
44
+ n_paths : int
45
+ Number of Monte Carlo paths.
46
+ dim : int
47
+ Dimension of each path (typically number of time steps).
48
+ batch_id : int, optional
49
+ Identifier for the RQMC batch. Implementations can use this to
50
+ produce independent randomized batches.
51
+
52
+ Returns
53
+ -------
54
+ np.ndarray
55
+ Array of shape (n_paths, dim) with N(0, 1) samples.
56
+ """
57
+ ...
58
+
59
+
60
+ @dataclass
61
+ class PseudoRandomNormalGenerator:
62
+ """
63
+ Pseudorandom standard normal generator based on NumPy.
64
+
65
+ This implements the RandomStream protocol and is suitable for classical
66
+ Monte Carlo simulations.
67
+ """
68
+
69
+ seed: Optional[int] = None
70
+
71
+ def __post_init__(self) -> None:
72
+ self._rng = np.random.default_rng(self.seed)
73
+
74
+ def normal(
75
+ self, n_paths: int, dim: int, batch_id: Optional[int] = None
76
+ ) -> np.ndarray:
77
+ """
78
+ Generate standard normal samples using NumPy's Generator.
79
+
80
+ The batch_id argument is accepted for API compatibility but ignored,
81
+ since independent batches are handled via the RNG state.
82
+ """
83
+ return self._rng.standard_normal(size=(n_paths, dim))
84
+
85
+
86
+ def _next_power_of_two(n: int) -> int:
87
+ """Return the smallest power of two >= n."""
88
+ if n <= 1:
89
+ return 1
90
+ return 1 << (n - 1).bit_length()
91
+
92
+
93
+ @dataclass
94
+ class SobolNormalGenerator:
95
+ """
96
+ Sobol-based standard normal generator with optional RQMC batching.
97
+
98
+ This class wraps scipy.stats.qmc.Sobol and uses scipy.special.ndtri to
99
+ transform low-discrepancy uniform samples into standard normals. To
100
+ preserve Sobol structure, the number of generated samples is always a
101
+ power of two (2**m). If the requested path count is not a power of two,
102
+ the generator will either round up and truncate or raise an error,
103
+ depending on the configuration.
104
+ """
105
+
106
+ base_seed: int = 1234
107
+ strict_power_of_two: bool = False
108
+
109
+ def _check_scipy(self) -> None:
110
+ if not HAS_SCIPY_QMC:
111
+ raise ImportError(
112
+ "SobolNormalGenerator requires scipy.stats.qmc and scipy.special.ndtri. "
113
+ "Please install SciPy or use PseudoRandomNormalGenerator instead."
114
+ )
115
+
116
+ def _make_engine(self, dim: int, batch_id: Optional[int]) -> "qmc.Sobol":
117
+ """
118
+ Create a new Sobol engine for the given dimension and batch.
119
+
120
+ A different seed is used for each batch_id to obtain independent
121
+ scrambled Sobol sequences (RQMC).
122
+ """
123
+ self._check_scipy()
124
+ # Use different seeds for different batches to obtain independent scrambles
125
+ if batch_id is None:
126
+ seed = self.base_seed
127
+ else:
128
+ seed = self.base_seed + int(batch_id)
129
+ return qmc.Sobol(d=dim, scramble=True, seed=seed) # type: ignore[call-arg]
130
+
131
+ def normal(
132
+ self, n_paths: int, dim: int, batch_id: Optional[int] = None
133
+ ) -> np.ndarray:
134
+ """
135
+ Generate standard normal samples using a scrambled Sobol sequence.
136
+
137
+ Parameters
138
+ ----------
139
+ n_paths : int
140
+ Requested number of paths.
141
+ dim : int
142
+ Dimension of each path (typically number of time steps).
143
+ batch_id : int, optional
144
+ Batch identifier for RQMC. Different batch_ids produce independent
145
+ randomized Sobol sequences.
146
+
147
+ Returns
148
+ -------
149
+ np.ndarray
150
+ Array of shape (n_paths, dim) containing N(0, 1) samples.
151
+ """
152
+ if n_paths <= 0:
153
+ raise ValueError("n_paths must be positive")
154
+ if dim <= 0:
155
+ raise ValueError("dim must be positive")
156
+
157
+ self._check_scipy()
158
+
159
+ # Ensure we use exactly 2**m Sobol points to preserve balance properties
160
+ n_total = _next_power_of_two(n_paths)
161
+ if self.strict_power_of_two and n_total != n_paths:
162
+ raise ValueError(
163
+ f"SobolNormalGenerator with strict_power_of_two=True requires "
164
+ f"n_paths to be a power of two, got {n_paths}."
165
+ )
166
+
167
+ m = int(np.log2(n_total))
168
+ engine = self._make_engine(dim=dim, batch_id=batch_id)
169
+
170
+ # Use random_base2 to get exactly 2**m points
171
+ u = engine.random_base2(m) # shape: (n_total, dim)
172
+ # Guard against 0/1 values that map to +/-inf under ndtri
173
+ eps = 1e-12
174
+ u = np.clip(u, eps, 1.0 - eps)
175
+
176
+ # Transform uniforms to standard normals using ndtri
177
+ if special is None:
178
+ # Fallback, should not happen if _check_scipy passed
179
+ from scipy.stats import norm # type: ignore
180
+
181
+ z = norm.ppf(u)
182
+ else:
183
+ z = special.ndtri(u)
184
+
185
+ if n_paths != n_total:
186
+ z = z[:n_paths]
187
+
188
+ return np.asarray(z, dtype=float)
189
+
190
+
191
+ __all__ = [
192
+ "RandomStream",
193
+ "PseudoRandomNormalGenerator",
194
+ "SobolNormalGenerator",
195
+ ]
@@ -0,0 +1,292 @@
1
+ """
2
+ Created on Mon Nov 17 2025
3
+
4
+ @author: yaofuxin
5
+ @description: Variance reduction utilities (antithetic variates, control
6
+ variates, importance sampling) designed to work with both
7
+ classical MC and QMC without destroying Sobol structure.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from dataclasses import dataclass
13
+ from typing import Optional, Tuple
14
+
15
+ import numpy as np
16
+
17
+
18
+ @dataclass
19
+ class VarianceReductionConfig:
20
+ """
21
+ Configuration for variance reduction techniques.
22
+
23
+ Parameters
24
+ ----------
25
+ antithetic : bool
26
+ Whether to use antithetic variates. This is intended for classical
27
+ MC (pseudorandom) and is disabled for QMC by default.
28
+ control_variate : bool
29
+ Whether to construct a GBM-based control variate.
30
+ importance_sampling : bool
31
+ Whether to apply a mean shift to the driving Brownian increments.
32
+ importance_shift : float
33
+ Shift parameter used in importance sampling. The same shift is applied
34
+ to all normal dimensions.
35
+ control_variate_mu : float, optional
36
+ Drift parameter for the GBM control variate.
37
+ control_variate_sigma : float, optional
38
+ Volatility for the GBM control variate.
39
+ control_variate_T : float, optional
40
+ Maturity of the GBM control variate.
41
+ """
42
+
43
+ antithetic: bool = False
44
+ control_variate: bool = False
45
+ importance_sampling: bool = False
46
+ importance_shift: float = 0.0
47
+ control_variate_mu: Optional[float] = None
48
+ control_variate_sigma: Optional[float] = None
49
+ control_variate_T: Optional[float] = None
50
+
51
+
52
+ def build_antithetic_pairs(z: np.ndarray) -> np.ndarray:
53
+ """
54
+ Construct antithetic variates from a base matrix of standard normals.
55
+
56
+ Parameters
57
+ ----------
58
+ z : np.ndarray
59
+ Base standard normals of shape (n_base_paths, dim).
60
+
61
+ Returns
62
+ -------
63
+ np.ndarray
64
+ Array of shape (2 * n_base_paths, dim) containing [z, -z].
65
+ """
66
+ z = np.asarray(z, dtype=float)
67
+ return np.concatenate([z, -z], axis=0)
68
+
69
+
70
+ def apply_importance_sampling_shift(z: np.ndarray, shift: float) -> np.ndarray:
71
+ """
72
+ Apply a constant mean shift to standard normal samples.
73
+
74
+ This function is safe for QMC usage because it preserves the low-discrepancy
75
+ structure of the underlying sequence while changing the sampling measure.
76
+
77
+ Parameters
78
+ ----------
79
+ z : np.ndarray
80
+ Standard normal samples of shape (n_paths, dim).
81
+ shift : float
82
+ Constant shift applied to all dimensions.
83
+
84
+ Returns
85
+ -------
86
+ np.ndarray
87
+ Shifted standard normals of the same shape as z.
88
+ """
89
+ z = np.asarray(z, dtype=float)
90
+ if shift == 0.0:
91
+ return z
92
+ return z + shift
93
+
94
+
95
+ def importance_sampling_weights(z_shifted: np.ndarray, shift: float) -> np.ndarray:
96
+ """
97
+ Compute likelihood ratio weights for Gaussian importance sampling.
98
+
99
+ If Z' ~ N(shift, 1), the Radon–Nikodym derivative dP/dQ at Z' is
100
+
101
+ w = exp(shift * Z' - 0.5 * shift^2)
102
+
103
+ For a d-dimensional vector of independent components, the total weight is
104
+
105
+ w = exp(shift * sum(Z'_j) - 0.5 * d * shift^2).
106
+
107
+ Parameters
108
+ ----------
109
+ z_shifted : np.ndarray
110
+ Shifted standard normal samples of shape (n_paths, dim), i.e., samples
111
+ drawn from N(shift, 1) along each dimension.
112
+ shift : float
113
+ Shift parameter used in the importance sampling scheme.
114
+
115
+ Returns
116
+ -------
117
+ np.ndarray
118
+ Weights of shape (n_paths,) to reweight payoffs back to the original
119
+ N(0, 1) measure.
120
+ """
121
+ z_shifted = np.asarray(z_shifted, dtype=float)
122
+ if z_shifted.ndim != 2:
123
+ raise ValueError("z_shifted must be a 2D array of shape (n_paths, dim)")
124
+ if shift == 0.0:
125
+ return np.ones(z_shifted.shape[0], dtype=float)
126
+
127
+ n_paths, dim = z_shifted.shape
128
+ sum_z = np.sum(z_shifted, axis=1)
129
+ exponent = shift * sum_z - 0.5 * (shift ** 2) * dim
130
+ return np.exp(exponent)
131
+
132
+
133
+ def gbm_control_variate(
134
+ z: np.ndarray,
135
+ mu: float,
136
+ sigma: float,
137
+ T: float,
138
+ ) -> np.ndarray:
139
+ """
140
+ Construct a GBM-based control variate using the same driving normals.
141
+
142
+ This control variate follows the exact GBM solution:
143
+
144
+ S_t = exp((mu - 0.5 * sigma^2) * t + sigma * sqrt(t) * Z)
145
+
146
+ using the column-wise time points.
147
+
148
+ Parameters
149
+ ----------
150
+ z : np.ndarray
151
+ Standard normal samples of shape (n_paths, n_steps).
152
+ mu : float
153
+ Drift parameter.
154
+ sigma : float
155
+ Volatility parameter.
156
+ T : float
157
+ Maturity.
158
+
159
+ Returns
160
+ -------
161
+ np.ndarray
162
+ Control variate array of shape (n_paths, n_steps), with one control
163
+ variate value per time step along each path.
164
+ """
165
+ z = np.asarray(z, dtype=float)
166
+ if z.ndim != 2:
167
+ raise ValueError("z must be a 2D array of shape (n_paths, n_steps)")
168
+
169
+ n_steps = z.shape[1]
170
+ if n_steps <= 0:
171
+ raise ValueError("n_steps must be positive")
172
+
173
+ dt = T / n_steps
174
+ t = np.arange(1, n_steps + 1, dtype=float) * dt
175
+
176
+ drift = (mu - 0.5 * sigma * sigma) * t
177
+ diffusion = sigma * np.sqrt(t)
178
+
179
+ drift = drift.reshape(1, -1)
180
+ diffusion = diffusion.reshape(1, -1)
181
+
182
+ return np.exp(drift + diffusion * z)
183
+
184
+
185
+ def apply_variance_reduction_to_normals(
186
+ n_paths: int,
187
+ dim: int,
188
+ base_normals: np.ndarray,
189
+ vr_config: Optional[VarianceReductionConfig],
190
+ is_qmc: bool,
191
+ ) -> Tuple[np.ndarray, Optional[np.ndarray], Optional[np.ndarray]]:
192
+ """
193
+ Apply configured variance reduction techniques to a matrix of normals.
194
+
195
+ Parameters
196
+ ----------
197
+ n_paths : int
198
+ Desired final number of paths.
199
+ dim : int
200
+ Dimension of each path.
201
+ base_normals : np.ndarray
202
+ Base N(0, 1) samples of shape (n_paths, dim) or smaller if antithetic
203
+ variates will expand the path count.
204
+ vr_config : VarianceReductionConfig, optional
205
+ Variance reduction configuration.
206
+ is_qmc : bool
207
+ Whether the underlying sampling is QMC (Sobol). Antithetic variates are
208
+ disabled by default in QMC mode to avoid destroying low-discrepancy
209
+ structure.
210
+
211
+ Returns
212
+ -------
213
+ z_processed : np.ndarray
214
+ Processed normals of shape (n_paths, dim) after applying antithetic
215
+ variates and/or importance sampling.
216
+ weights : np.ndarray or None
217
+ Importance sampling weights if enabled, otherwise None.
218
+ control_variate : np.ndarray or None
219
+ GBM-based control variate if enabled, otherwise None.
220
+ """
221
+ z = np.asarray(base_normals, dtype=float)
222
+ if z.ndim != 2:
223
+ raise ValueError("base_normals must be a 2D array")
224
+
225
+ weights: Optional[np.ndarray] = None
226
+ control_variate: Optional[np.ndarray] = None
227
+
228
+ if vr_config is None:
229
+ if z.shape != (n_paths, dim):
230
+ raise ValueError(
231
+ f"Expected base_normals to have shape ({n_paths}, {dim}) without "
232
+ f"variance reduction, got {z.shape}."
233
+ )
234
+ return z, weights, control_variate
235
+
236
+ # Antithetic variates: only for classical MC by default.
237
+ if vr_config.antithetic:
238
+ if is_qmc:
239
+ raise ValueError(
240
+ "Antithetic variates are not enabled for QMC mode to avoid "
241
+ "breaking Sobol low-discrepancy properties."
242
+ )
243
+ # base_normals is assumed to have shape (ceil(n_paths / 2), dim)
244
+ z = build_antithetic_pairs(z)
245
+ if z.shape[0] < n_paths:
246
+ raise ValueError(
247
+ "Antithetic base_normals must be large enough to reach n_paths."
248
+ )
249
+ z = z[:n_paths, :]
250
+
251
+ # Importance sampling (safe for both MC and QMC)
252
+ if vr_config.importance_sampling and vr_config.importance_shift != 0.0:
253
+ z = apply_importance_sampling_shift(z, vr_config.importance_shift)
254
+ weights = importance_sampling_weights(z, vr_config.importance_shift)
255
+
256
+ # Control variate based on GBM analytical solution
257
+ if vr_config.control_variate:
258
+ if (
259
+ vr_config.control_variate_mu is None
260
+ or vr_config.control_variate_sigma is None
261
+ or vr_config.control_variate_T is None
262
+ ):
263
+ raise ValueError(
264
+ "control_variate_mu, control_variate_sigma and control_variate_T "
265
+ "must be provided in VarianceReductionConfig when control_variate "
266
+ "is enabled."
267
+ )
268
+ control_variate = gbm_control_variate(
269
+ z,
270
+ mu=vr_config.control_variate_mu,
271
+ sigma=vr_config.control_variate_sigma,
272
+ T=vr_config.control_variate_T,
273
+ )
274
+
275
+ if z.shape != (n_paths, dim):
276
+ raise ValueError(
277
+ f"Processed normals have shape {z.shape}, expected ({n_paths}, {dim})."
278
+ )
279
+
280
+ return z, weights, control_variate
281
+
282
+
283
+ __all__ = [
284
+ "VarianceReductionConfig",
285
+ "build_antithetic_pairs",
286
+ "apply_importance_sampling_shift",
287
+ "importance_sampling_weights",
288
+ "gbm_control_variate",
289
+ "apply_variance_reduction_to_normals",
290
+ ]
291
+
292
+
@@ -0,0 +1,8 @@
1
+ """
2
+ Equity derivative products.
3
+ """
4
+ from .base_equity_product import BaseEquityProduct
5
+ from .option import BaseEquityOption, EuropeanVanillaOption, AsianOption
6
+
7
+ __all__ = ['BaseEquityProduct', 'BaseEquityOption', 'EuropeanVanillaOption', 'AsianOption']
8
+