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,612 @@
1
+ """
2
+ Boundary Check Script for Asian Option Analytical Engine
3
+ Generated: 2024-12-23
4
+
5
+ Tests extreme market cases and theoretical relationships for Asian options.
6
+ """
7
+ import numpy as np
8
+ import sys
9
+ from pathlib import Path
10
+ from datetime import datetime
11
+
12
+ sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent.parent.parent.parent))
13
+
14
+ from quantark.asset.equity.product.option import AsianOption, EuropeanVanillaOption
15
+ from quantark.asset.equity.engine.analytical import AsianOptionAnalyticalEngine, BlackScholesEngine
16
+ from quantark.asset.equity.engine.mc import AsianOptionMCEngine
17
+ from quantark.param import SpotQuote, FlatVolSurface, FlatRateCurve, ContinuousDividendYield
18
+ from quantark.priceenv import PricingEnvironment
19
+ from quantark.util.enum import OptionType, AveragingType, AsianStrikeType
20
+ from quantark.util.enum.engine_enums import AsianAnalyticalMethod, MonteCarloMethod, EngineType
21
+ from quantark.util.exceptions import ValidationError
22
+
23
+
24
+ class BoundaryCheckResults:
25
+ def __init__(self):
26
+ self.passed = []
27
+ self.failed = []
28
+ self.warnings = []
29
+
30
+ def add_result(self, test_name: str, passed: bool, message: str):
31
+ if passed:
32
+ self.passed.append((test_name, message))
33
+ else:
34
+ self.failed.append((test_name, message))
35
+
36
+ def add_warning(self, test_name: str, message: str):
37
+ self.warnings.append((test_name, message))
38
+
39
+ def summary(self):
40
+ total = len(self.passed) + len(self.failed)
41
+ print(f"\n{'='*60}")
42
+ print(f"BOUNDARY CHECK SUMMARY")
43
+ print(f"{'='*60}")
44
+ print(f"Total Tests: {total}")
45
+ print(f"Passed: {len(self.passed)} ({100*len(self.passed)/total if total > 0 else 0:.1f}%)")
46
+ print(f"Failed: {len(self.failed)} ({100*len(self.failed)/total if total > 0 else 0:.1f}%)")
47
+ print(f"Warnings: {len(self.warnings)}")
48
+
49
+ if self.failed:
50
+ print(f"\nFailed Tests:")
51
+ for name, msg in self.failed:
52
+ print(f" - {name}: {msg}")
53
+
54
+ if self.warnings:
55
+ print(f"\nWarnings:")
56
+ for name, msg in self.warnings:
57
+ print(f" - {name}: {msg}")
58
+
59
+ return len(self.failed) == 0
60
+
61
+
62
+ def create_pricing_env(spot=100.0, rate=0.05, vol=0.20, div=0.0, valuation_date=None):
63
+ """Helper to create pricing environment."""
64
+ if valuation_date is None:
65
+ valuation_date = datetime(2024, 1, 1)
66
+ return PricingEnvironment(
67
+ spot_quote=SpotQuote(spot=spot),
68
+ vol_surface=FlatVolSurface(volatility=vol),
69
+ rate_curve=FlatRateCurve(rate=rate),
70
+ div_yield=ContinuousDividendYield(div_yield=div),
71
+ valuation_date=valuation_date,
72
+ )
73
+
74
+
75
+ # ============================================================
76
+ # EXTREME MARKET CASE TESTS
77
+ # ============================================================
78
+
79
+ def test_low_volatility(results: BoundaryCheckResults):
80
+ """Test: Low volatility → intrinsic value (for deep ITM/OTM)."""
81
+ pricing_env = create_pricing_env(spot=100.0, rate=0.05, vol=0.001)
82
+
83
+ # Deep ITM call with low vol
84
+ option = AsianOption(
85
+ strike=80.0,
86
+ option_type=OptionType.CALL,
87
+ averaging_type=AveragingType.ARITHMETIC,
88
+ asian_strike_type=AsianStrikeType.FIXED,
89
+ maturity=1.0,
90
+ num_observations=12,
91
+ )
92
+
93
+ engine = AsianOptionAnalyticalEngine(method=AsianAnalyticalMethod.TURNBULL_WAKEMAN)
94
+ price = engine.price(option, pricing_env)
95
+
96
+ # With very low volatility, price should approach intrinsic value
97
+ # For Asian: intrinsic is max(S - K, 0) = 20, but averaging reduces this
98
+ # A rough check: price should be positive and reasonable
99
+ passed = price > 15 and price < 25
100
+ results.add_result(
101
+ "Low volatility (σ→0)",
102
+ passed,
103
+ f"Price=${price:.4f}, expected ~$20 (intrinsic)"
104
+ )
105
+
106
+
107
+ def test_high_volatility(results: BoundaryCheckResults):
108
+ """Test: High volatility → higher option value."""
109
+ pricing_env = create_pricing_env(spot=100.0, rate=0.05, vol=1.0)
110
+
111
+ option = AsianOption(
112
+ strike=100.0,
113
+ option_type=OptionType.CALL,
114
+ averaging_type=AveragingType.ARITHMETIC,
115
+ asian_strike_type=AsianStrikeType.FIXED,
116
+ maturity=1.0,
117
+ num_observations=12,
118
+ )
119
+
120
+ engine = AsianOptionAnalyticalEngine(method=AsianAnalyticalMethod.TURNBULL_WAKEMAN)
121
+ price = engine.price(option, pricing_env)
122
+
123
+ passed = price > 0 and not np.isnan(price) and not np.isinf(price)
124
+ results.add_result(
125
+ "High volatility (σ=100%)",
126
+ passed,
127
+ f"Price=${price:.4f}, should be finite and positive"
128
+ )
129
+
130
+
131
+ def test_near_expiry(results: BoundaryCheckResults):
132
+ """Test: Near expiry → payoff."""
133
+ pricing_env = create_pricing_env(spot=100.0, rate=0.05, vol=0.20)
134
+
135
+ option = AsianOption(
136
+ strike=95.0,
137
+ option_type=OptionType.CALL,
138
+ averaging_type=AveragingType.ARITHMETIC,
139
+ asian_strike_type=AsianStrikeType.FIXED,
140
+ maturity=1e-8, # Very close to expiry
141
+ num_observations=1,
142
+ )
143
+
144
+ engine = AsianOptionAnalyticalEngine(method=AsianAnalyticalMethod.TURNBULL_WAKEMAN)
145
+ price = engine.price(option, pricing_env)
146
+
147
+ # Near expiry, should be close to intrinsic
148
+ intrinsic = max(100.0 - 95.0, 0)
149
+ passed = abs(price - intrinsic) < 1.0
150
+ results.add_result(
151
+ "Near expiry (T→0)",
152
+ passed,
153
+ f"Price=${price:.4f}, intrinsic=${intrinsic}"
154
+ )
155
+
156
+
157
+ def test_deep_itm(results: BoundaryCheckResults):
158
+ """Test: Deep ITM → high value."""
159
+ pricing_env = create_pricing_env(spot=150.0, rate=0.05, vol=0.20)
160
+
161
+ option = AsianOption(
162
+ strike=100.0,
163
+ option_type=OptionType.CALL,
164
+ averaging_type=AveragingType.ARITHMETIC,
165
+ asian_strike_type=AsianStrikeType.FIXED,
166
+ maturity=1.0,
167
+ num_observations=12,
168
+ )
169
+
170
+ engine = AsianOptionAnalyticalEngine(method=AsianAnalyticalMethod.TURNBULL_WAKEMAN)
171
+ price = engine.price(option, pricing_env)
172
+
173
+ passed = price > 30 and price < 60
174
+ results.add_result(
175
+ "Deep ITM call",
176
+ passed,
177
+ f"Price=${price:.4f}, expected $30-60"
178
+ )
179
+
180
+
181
+ def test_deep_otm(results: BoundaryCheckResults):
182
+ """Test: Deep OTM → low value."""
183
+ pricing_env = create_pricing_env(spot=50.0, rate=0.05, vol=0.20)
184
+
185
+ option = AsianOption(
186
+ strike=100.0,
187
+ option_type=OptionType.CALL,
188
+ averaging_type=AveragingType.ARITHMETIC,
189
+ asian_strike_type=AsianStrikeType.FIXED,
190
+ maturity=1.0,
191
+ num_observations=12,
192
+ )
193
+
194
+ engine = AsianOptionAnalyticalEngine(method=AsianAnalyticalMethod.TURNBULL_WAKEMAN)
195
+ price = engine.price(option, pricing_env)
196
+
197
+ passed = price >= 0 and price < 5.0
198
+ results.add_result(
199
+ "Deep OTM call",
200
+ passed,
201
+ f"Price=${price:.4f}, expected $0-5"
202
+ )
203
+
204
+
205
+ def test_zero_interest_rate(results: BoundaryCheckResults):
206
+ """Test: r=0 → no discounting effect."""
207
+ pricing_env = create_pricing_env(spot=100.0, rate=0.0, vol=0.20)
208
+
209
+ option = AsianOption(
210
+ strike=100.0,
211
+ option_type=OptionType.CALL,
212
+ averaging_type=AveragingType.ARITHMETIC,
213
+ asian_strike_type=AsianStrikeType.FIXED,
214
+ maturity=1.0,
215
+ num_observations=12,
216
+ )
217
+
218
+ engine = AsianOptionAnalyticalEngine(method=AsianAnalyticalMethod.TURNBULL_WAKEMAN)
219
+ price = engine.price(option, pricing_env)
220
+
221
+ passed = price > 0 and not np.isnan(price)
222
+ results.add_result(
223
+ "Zero interest rate (r=0)",
224
+ passed,
225
+ f"Price=${price:.4f}"
226
+ )
227
+
228
+
229
+ def test_zero_cost_of_carry(results: BoundaryCheckResults):
230
+ """Test: b=0 (r=q) special case."""
231
+ pricing_env = create_pricing_env(spot=100.0, rate=0.05, vol=0.20, div=0.05) # b = r - q = 0
232
+
233
+ option = AsianOption(
234
+ strike=100.0,
235
+ option_type=OptionType.CALL,
236
+ averaging_type=AveragingType.ARITHMETIC,
237
+ asian_strike_type=AsianStrikeType.FIXED,
238
+ maturity=1.0,
239
+ num_observations=12,
240
+ )
241
+
242
+ engine = AsianOptionAnalyticalEngine(method=AsianAnalyticalMethod.TURNBULL_WAKEMAN)
243
+ price = engine.price(option, pricing_env)
244
+
245
+ passed = price > 0 and not np.isnan(price)
246
+ results.add_result(
247
+ "Zero cost-of-carry (b=0)",
248
+ passed,
249
+ f"Price=${price:.4f}"
250
+ )
251
+
252
+
253
+ # ============================================================
254
+ # THEORETICAL RELATIONSHIP TESTS
255
+ # ============================================================
256
+
257
+ def test_geometric_cheaper_than_arithmetic(results: BoundaryCheckResults):
258
+ """Test: Geometric average ≤ Arithmetic average (Jensen's inequality)."""
259
+ pricing_env = create_pricing_env(spot=100.0, rate=0.05, vol=0.20)
260
+
261
+ geo_option = AsianOption(
262
+ strike=100.0,
263
+ option_type=OptionType.CALL,
264
+ averaging_type=AveragingType.GEOMETRIC,
265
+ asian_strike_type=AsianStrikeType.FIXED,
266
+ maturity=1.0,
267
+ num_observations=12,
268
+ )
269
+
270
+ arith_option = AsianOption(
271
+ strike=100.0,
272
+ option_type=OptionType.CALL,
273
+ averaging_type=AveragingType.ARITHMETIC,
274
+ asian_strike_type=AsianStrikeType.FIXED,
275
+ maturity=1.0,
276
+ num_observations=12,
277
+ )
278
+
279
+ kv_engine = AsianOptionAnalyticalEngine(method=AsianAnalyticalMethod.KEMNA_VORST)
280
+ tw_engine = AsianOptionAnalyticalEngine(method=AsianAnalyticalMethod.TURNBULL_WAKEMAN)
281
+
282
+ geo_price = kv_engine.price(geo_option, pricing_env)
283
+ arith_price = tw_engine.price(arith_option, pricing_env)
284
+
285
+ passed = geo_price <= arith_price
286
+ results.add_result(
287
+ "Geometric ≤ Arithmetic",
288
+ passed,
289
+ f"Geometric=${geo_price:.4f}, Arithmetic=${arith_price:.4f}"
290
+ )
291
+
292
+
293
+ def test_asian_cheaper_than_vanilla(results: BoundaryCheckResults):
294
+ """Test: Asian option ≤ Vanilla option (averaging reduces volatility)."""
295
+ pricing_env = create_pricing_env(spot=100.0, rate=0.05, vol=0.20)
296
+
297
+ # Asian call (arithmetic)
298
+ asian_option = AsianOption(
299
+ strike=100.0,
300
+ option_type=OptionType.CALL,
301
+ averaging_type=AveragingType.ARITHMETIC,
302
+ asian_strike_type=AsianStrikeType.FIXED,
303
+ maturity=1.0,
304
+ num_observations=12,
305
+ )
306
+
307
+ # Vanilla call
308
+ vanilla_option = EuropeanVanillaOption(
309
+ strike=100.0,
310
+ option_type=OptionType.CALL,
311
+ maturity=1.0,
312
+ )
313
+
314
+ asian_engine = AsianOptionAnalyticalEngine(method=AsianAnalyticalMethod.TURNBULL_WAKEMAN)
315
+ bs_engine = BlackScholesEngine()
316
+
317
+ asian_price = asian_engine.price(asian_option, pricing_env)
318
+ vanilla_price = bs_engine.price(vanilla_option, pricing_env)
319
+
320
+ passed = asian_price <= vanilla_price
321
+ results.add_result(
322
+ "Asian ≤ Vanilla",
323
+ passed,
324
+ f"Asian=${asian_price:.4f}, Vanilla=${vanilla_price:.4f}"
325
+ )
326
+
327
+
328
+ def test_atm_call_positive(results: BoundaryCheckResults):
329
+ """Test: ATM call should have positive value."""
330
+ pricing_env = create_pricing_env(spot=100.0, rate=0.05, vol=0.20)
331
+
332
+ option = AsianOption(
333
+ strike=100.0,
334
+ option_type=OptionType.CALL,
335
+ averaging_type=AveragingType.ARITHMETIC,
336
+ asian_strike_type=AsianStrikeType.FIXED,
337
+ maturity=1.0,
338
+ num_observations=12,
339
+ )
340
+
341
+ engine = AsianOptionAnalyticalEngine(method=AsianAnalyticalMethod.TURNBULL_WAKEMAN)
342
+ price = engine.price(option, pricing_env)
343
+
344
+ passed = price > 0
345
+ results.add_result(
346
+ "ATM call > 0",
347
+ passed,
348
+ f"Price=${price:.4f}"
349
+ )
350
+
351
+
352
+ def test_monotonicity_in_strike(results: BoundaryCheckResults):
353
+ """Test: Higher strike → lower call value."""
354
+ pricing_env = create_pricing_env(spot=100.0, rate=0.05, vol=0.20)
355
+
356
+ strikes = [90.0, 100.0, 110.0]
357
+ prices = []
358
+
359
+ engine = AsianOptionAnalyticalEngine(method=AsianAnalyticalMethod.TURNBULL_WAKEMAN)
360
+
361
+ for strike in strikes:
362
+ option = AsianOption(
363
+ strike=strike,
364
+ option_type=OptionType.CALL,
365
+ averaging_type=AveragingType.ARITHMETIC,
366
+ asian_strike_type=AsianStrikeType.FIXED,
367
+ maturity=1.0,
368
+ num_observations=12,
369
+ )
370
+ prices.append(engine.price(option, pricing_env))
371
+
372
+ # Prices should be decreasing with strike
373
+ passed = prices[0] >= prices[1] >= prices[2]
374
+ results.add_result(
375
+ "Call monotonicity in strike",
376
+ passed,
377
+ f"K=90: ${prices[0]:.4f}, K=100: ${prices[1]:.4f}, K=110: ${prices[2]:.4f}"
378
+ )
379
+
380
+
381
+ def test_monotonicity_in_volatility(results: BoundaryCheckResults):
382
+ """Test: Higher volatility → higher call value."""
383
+ pricing_env_base = create_pricing_env(spot=100.0, rate=0.05, vol=0.10)
384
+
385
+ option = AsianOption(
386
+ strike=100.0,
387
+ option_type=OptionType.CALL,
388
+ averaging_type=AveragingType.ARITHMETIC,
389
+ asian_strike_type=AsianStrikeType.FIXED,
390
+ maturity=1.0,
391
+ num_observations=12,
392
+ )
393
+
394
+ engine = AsianOptionAnalyticalEngine(method=AsianAnalyticalMethod.TURNBULL_WAKEMAN)
395
+
396
+ # Test with different volatilities
397
+ env_low = create_pricing_env(spot=100.0, rate=0.05, vol=0.10)
398
+ env_high = create_pricing_env(spot=100.0, rate=0.05, vol=0.30)
399
+
400
+ price_low = engine.price(option, env_low)
401
+ price_high = engine.price(option, env_high)
402
+
403
+ passed = price_high >= price_low
404
+ results.add_result(
405
+ "Call monotonicity in volatility",
406
+ passed,
407
+ f"σ=10%: ${price_low:.4f}, σ=30%: ${price_high:.4f}"
408
+ )
409
+
410
+
411
+ def test_put_call_symmetry_approximation(results: BoundaryCheckResults):
412
+ """Test: Put-call relationship (no exact parity for Asian)."""
413
+ pricing_env = create_pricing_env(spot=100.0, rate=0.05, vol=0.20)
414
+
415
+ call_option = AsianOption(
416
+ strike=100.0,
417
+ option_type=OptionType.CALL,
418
+ averaging_type=AveragingType.ARITHMETIC,
419
+ asian_strike_type=AsianStrikeType.FIXED,
420
+ maturity=1.0,
421
+ num_observations=12,
422
+ )
423
+
424
+ put_option = AsianOption(
425
+ strike=100.0,
426
+ option_type=OptionType.PUT,
427
+ averaging_type=AveragingType.ARITHMETIC,
428
+ asian_strike_type=AsianStrikeType.FIXED,
429
+ maturity=1.0,
430
+ num_observations=12,
431
+ )
432
+
433
+ engine = AsianOptionAnalyticalEngine(method=AsianAnalyticalMethod.TURNBULL_WAKEMAN)
434
+
435
+ call_price = engine.price(call_option, pricing_env)
436
+ put_price = engine.price(put_option, pricing_env)
437
+
438
+ # Both ATM options should have similar value
439
+ ratio = call_price / put_price if put_price > 0 else 0
440
+ passed = 0.5 < ratio < 2.0
441
+ results.add_result(
442
+ "ATM put-call relationship",
443
+ passed,
444
+ f"Call=${call_price:.4f}, Put=${put_price:.4f}, Ratio={ratio:.2f}"
445
+ )
446
+
447
+
448
+ def test_floating_strike_symmetry(results: BoundaryCheckResults):
449
+ """Test: Henderson-Wojakowski symmetry for floating-strike."""
450
+ pricing_env = create_pricing_env(spot=100.0, rate=0.05, vol=0.20, div=0.02) # b=0.03
451
+
452
+ # Floating call should equal fixed put with transformed parameters
453
+ floating_call = AsianOption(
454
+ strike=0.0,
455
+ option_type=OptionType.CALL,
456
+ averaging_type=AveragingType.ARITHMETIC,
457
+ asian_strike_type=AsianStrikeType.FLOATING,
458
+ maturity=1.0,
459
+ num_observations=12,
460
+ )
461
+
462
+ engine = AsianOptionAnalyticalEngine(method=AsianAnalyticalMethod.TURNBULL_WAKEMAN)
463
+ price = engine.price(floating_call, pricing_env)
464
+
465
+ passed = price > 0 and not np.isnan(price)
466
+ results.add_result(
467
+ "Floating-strike symmetry",
468
+ passed,
469
+ f"Floating call price=${price:.4f}"
470
+ )
471
+
472
+
473
+ # ============================================================
474
+ # METHOD SPECIFIC TESTS
475
+ # ============================================================
476
+
477
+ def test_kemna_vorst_geometric(results: BoundaryCheckResults):
478
+ """Test: Kemna-Vorst should match BSM with adjusted parameters."""
479
+ pricing_env = create_pricing_env(spot=100.0, rate=0.05, vol=0.20)
480
+
481
+ option = AsianOption(
482
+ strike=100.0,
483
+ option_type=OptionType.CALL,
484
+ averaging_type=AveragingType.GEOMETRIC,
485
+ asian_strike_type=AsianStrikeType.FIXED,
486
+ maturity=1.0,
487
+ num_observations=12,
488
+ )
489
+
490
+ engine = AsianOptionAnalyticalEngine(method=AsianAnalyticalMethod.KEMNA_VORST)
491
+ price = engine.price(option, pricing_env)
492
+
493
+ passed = price > 0 and not np.isnan(price)
494
+ results.add_result(
495
+ "Kemna-Vorst geometric",
496
+ passed,
497
+ f"Geometric call price=${price:.4f}"
498
+ )
499
+
500
+
501
+ def test_levy_b_not_zero(results: BoundaryCheckResults):
502
+ """Test: LEVY method should fail for b=0."""
503
+ pricing_env = create_pricing_env(spot=100.0, rate=0.05, vol=0.20, div=0.05) # b=0
504
+
505
+ option = AsianOption(
506
+ strike=100.0,
507
+ option_type=OptionType.CALL,
508
+ averaging_type=AveragingType.ARITHMETIC,
509
+ asian_strike_type=AsianStrikeType.FIXED,
510
+ maturity=1.0,
511
+ num_observations=12,
512
+ )
513
+
514
+ try:
515
+ engine = AsianOptionAnalyticalEngine(method=AsianAnalyticalMethod.LEVY)
516
+ price = engine.price(option, pricing_env)
517
+ # If we get here, LEVY handled b=0 (or implementation doesn't check)
518
+ results.add_warning(
519
+ "LEVY b=0 check",
520
+ f"LEVY allowed b=0, price=${price:.4f} (should fail per Haug)"
521
+ )
522
+ except ValidationError as e:
523
+ results.add_result(
524
+ "LEVY b=0 check",
525
+ True,
526
+ f"Correctly rejects b=0: {str(e)[:50]}"
527
+ )
528
+
529
+
530
+ def test_all_methods_produce_finite_prices(results: BoundaryCheckResults):
531
+ """Test: All methods produce finite, positive prices."""
532
+ pricing_env = create_pricing_env(spot=100.0, rate=0.05, vol=0.20, div=0.02)
533
+
534
+ option = AsianOption(
535
+ strike=100.0,
536
+ option_type=OptionType.CALL,
537
+ averaging_type=AveragingType.ARITHMETIC,
538
+ asian_strike_type=AsianStrikeType.FIXED,
539
+ maturity=1.0,
540
+ num_observations=12,
541
+ )
542
+
543
+ methods = [
544
+ AsianAnalyticalMethod.TURNBULL_WAKEMAN,
545
+ AsianAnalyticalMethod.LEVY,
546
+ AsianAnalyticalMethod.CURRAN,
547
+ AsianAnalyticalMethod.DISCRETE_HHM,
548
+ ]
549
+
550
+ all_passed = True
551
+ prices = {}
552
+
553
+ for method in methods:
554
+ try:
555
+ engine = AsianOptionAnalyticalEngine(method=method)
556
+ price = engine.price(option, pricing_env)
557
+ prices[method.value] = price
558
+ if not (price > 0 and not np.isnan(price) and not np.isinf(price)):
559
+ all_passed = False
560
+ except Exception as e:
561
+ prices[method.value] = f"Error: {e}"
562
+ all_passed = False
563
+
564
+ results.add_result(
565
+ "All methods finite prices",
566
+ all_passed,
567
+ f"Prices: {prices}"
568
+ )
569
+
570
+
571
+ # ============================================================
572
+ # MAIN
573
+ # ============================================================
574
+
575
+ if __name__ == "__main__":
576
+ results = BoundaryCheckResults()
577
+
578
+ print("Running Boundary Checks for Asian Option Analytical Engine")
579
+ print("="*60)
580
+
581
+ # Extreme market cases
582
+ print("\n1. Extreme Market Cases")
583
+ print("-"*40)
584
+ test_low_volatility(results)
585
+ test_high_volatility(results)
586
+ test_near_expiry(results)
587
+ test_deep_itm(results)
588
+ test_deep_otm(results)
589
+ test_zero_interest_rate(results)
590
+ test_zero_cost_of_carry(results)
591
+
592
+ # Theoretical relationships
593
+ print("\n2. Theoretical Relationships")
594
+ print("-"*40)
595
+ test_geometric_cheaper_than_arithmetic(results)
596
+ test_asian_cheaper_than_vanilla(results)
597
+ test_atm_call_positive(results)
598
+ test_monotonicity_in_strike(results)
599
+ test_monotonicity_in_volatility(results)
600
+ test_put_call_symmetry_approximation(results)
601
+ test_floating_strike_symmetry(results)
602
+
603
+ # Method-specific tests
604
+ print("\n3. Method-Specific Tests")
605
+ print("-"*40)
606
+ test_kemna_vorst_geometric(results)
607
+ test_levy_b_not_zero(results)
608
+ test_all_methods_produce_finite_prices(results)
609
+
610
+ # Print summary
611
+ success = results.summary()
612
+ sys.exit(0 if success else 1)