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,1175 @@
1
+ """
2
+ HTML report generation for dynamic scenario analysis.
3
+ """
4
+
5
+ from pathlib import Path
6
+ from typing import Union, Optional
7
+ from datetime import datetime
8
+ import base64
9
+ from io import BytesIO
10
+
11
+ from quantark.dynamicscenario.results.dynamic_results import DynamicScenarioResults
12
+
13
+ # Type hint for FI results
14
+ try:
15
+ from quantark.dynamicscenario.fi.results import FIDynamicScenarioResults
16
+ except ImportError:
17
+ FIDynamicScenarioResults = None
18
+
19
+
20
+ class DynamicReportGenerator:
21
+ """
22
+ Generates comprehensive HTML reports for dynamic scenario results.
23
+
24
+ Creates a standalone HTML file with:
25
+ - Executive summary
26
+ - Day-by-day evolution table
27
+ - P&L chart
28
+ - Greeks evolution chart
29
+ - Position changes
30
+ - Market path visualization
31
+
32
+ Example:
33
+ >>> generator = DynamicReportGenerator()
34
+ >>> generator.generate_report(results, "output/report.html")
35
+ """
36
+
37
+ def __init__(self):
38
+ """Initialize report generator."""
39
+ pass
40
+
41
+ def generate_report(
42
+ self,
43
+ results: DynamicScenarioResults,
44
+ output_path: Union[str, Path],
45
+ title: Optional[str] = None,
46
+ include_charts: bool = True,
47
+ ) -> None:
48
+ """
49
+ Generate comprehensive HTML report.
50
+
51
+ Args:
52
+ results: Dynamic scenario results
53
+ output_path: Path for output HTML file
54
+ title: Optional custom title
55
+ include_charts: Whether to include embedded charts
56
+ """
57
+ output_path = Path(output_path)
58
+ output_path.parent.mkdir(parents=True, exist_ok=True)
59
+
60
+ if title is None:
61
+ title = f"Dynamic Scenario Report - {results.path_name}"
62
+
63
+ # Generate HTML content
64
+ html = self._generate_html(results, title, include_charts)
65
+
66
+ # Write to file
67
+ with open(output_path, "w") as f:
68
+ f.write(html)
69
+
70
+ print(f"Generated HTML report: {output_path}")
71
+
72
+ def _generate_html(
73
+ self, results: DynamicScenarioResults, title: str, include_charts: bool
74
+ ) -> str:
75
+ """Generate complete HTML content."""
76
+
77
+ html = f"""<!DOCTYPE html>
78
+ <html lang="en">
79
+ <head>
80
+ <meta charset="UTF-8">
81
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
82
+ <title>{title}</title>
83
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
84
+ <style>
85
+ {self._get_css()}
86
+ </style>
87
+ </head>
88
+ <body>
89
+ <div class="container">
90
+ <header>
91
+ <h1>{title}</h1>
92
+ <p class="timestamp">Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
93
+ </header>
94
+
95
+ {self._generate_executive_summary(results)}
96
+ {self._generate_performance_metrics(results)}
97
+ {self._generate_charts_section(results) if include_charts else ''}
98
+ {self._generate_day_by_day_table(results)}
99
+ {self._generate_trades_section(results)}
100
+
101
+ <footer>
102
+ <p>Generated by QuantArk Dynamic Scenario Analysis Module</p>
103
+ </footer>
104
+ </div>
105
+
106
+ {self._generate_chart_scripts(results) if include_charts else ''}
107
+ </body>
108
+ </html>"""
109
+
110
+ return html
111
+
112
+ def _get_css(self) -> str:
113
+ """Get CSS styles for report."""
114
+ return """
115
+ * {
116
+ margin: 0;
117
+ padding: 0;
118
+ box-sizing: border-box;
119
+ }
120
+
121
+ body {
122
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
123
+ line-height: 1.6;
124
+ color: #333;
125
+ background-color: #f4f4f4;
126
+ }
127
+
128
+ .container {
129
+ max-width: 1400px;
130
+ margin: 0 auto;
131
+ padding: 20px;
132
+ background-color: white;
133
+ box-shadow: 0 0 10px rgba(0,0,0,0.1);
134
+ }
135
+
136
+ header {
137
+ text-align: center;
138
+ padding: 20px 0;
139
+ border-bottom: 3px solid #1a5f7a;
140
+ margin-bottom: 30px;
141
+ background: linear-gradient(135deg, #1a5f7a 0%, #2d8ba4 100%);
142
+ color: white;
143
+ border-radius: 8px 8px 0 0;
144
+ margin: -20px -20px 30px -20px;
145
+ padding: 30px 20px;
146
+ }
147
+
148
+ h1 {
149
+ margin-bottom: 10px;
150
+ }
151
+
152
+ h2 {
153
+ color: #1a5f7a;
154
+ margin-top: 30px;
155
+ margin-bottom: 15px;
156
+ padding-bottom: 10px;
157
+ border-bottom: 2px solid #ecf0f1;
158
+ }
159
+
160
+ h3 {
161
+ color: #2d8ba4;
162
+ margin-top: 20px;
163
+ margin-bottom: 10px;
164
+ }
165
+
166
+ .timestamp {
167
+ opacity: 0.9;
168
+ font-style: italic;
169
+ }
170
+
171
+ .summary-grid {
172
+ display: grid;
173
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
174
+ gap: 20px;
175
+ margin: 20px 0;
176
+ }
177
+
178
+ .summary-card {
179
+ background-color: #f8f9fa;
180
+ padding: 20px;
181
+ border-radius: 8px;
182
+ border-left: 4px solid #1a5f7a;
183
+ transition: transform 0.2s;
184
+ }
185
+
186
+ .summary-card:hover {
187
+ transform: translateY(-2px);
188
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
189
+ }
190
+
191
+ .summary-card.negative {
192
+ border-left-color: #e74c3c;
193
+ }
194
+
195
+ .summary-card.positive {
196
+ border-left-color: #27ae60;
197
+ }
198
+
199
+ .summary-card h3 {
200
+ margin-top: 0;
201
+ font-size: 0.85em;
202
+ text-transform: uppercase;
203
+ color: #7f8c8d;
204
+ letter-spacing: 0.5px;
205
+ }
206
+
207
+ .summary-card .value {
208
+ font-size: 1.8em;
209
+ font-weight: bold;
210
+ color: #2c3e50;
211
+ }
212
+
213
+ .summary-card .value.negative {
214
+ color: #e74c3c;
215
+ }
216
+
217
+ .summary-card .value.positive {
218
+ color: #27ae60;
219
+ }
220
+
221
+ .chart-container {
222
+ margin: 30px 0;
223
+ padding: 20px;
224
+ background: #f8f9fa;
225
+ border-radius: 8px;
226
+ }
227
+
228
+ .chart-row {
229
+ display: grid;
230
+ grid-template-columns: 1fr 1fr;
231
+ gap: 20px;
232
+ margin: 20px 0;
233
+ }
234
+
235
+ @media (max-width: 900px) {
236
+ .chart-row {
237
+ grid-template-columns: 1fr;
238
+ }
239
+ }
240
+
241
+ table {
242
+ width: 100%;
243
+ border-collapse: collapse;
244
+ margin: 20px 0;
245
+ background-color: white;
246
+ font-size: 0.9em;
247
+ }
248
+
249
+ th {
250
+ background-color: #1a5f7a;
251
+ color: white;
252
+ padding: 12px 8px;
253
+ text-align: left;
254
+ font-weight: 600;
255
+ position: sticky;
256
+ top: 0;
257
+ }
258
+
259
+ td {
260
+ padding: 10px 8px;
261
+ border-bottom: 1px solid #ecf0f1;
262
+ }
263
+
264
+ tr:nth-child(even) {
265
+ background-color: #f8f9fa;
266
+ }
267
+
268
+ tr:hover {
269
+ background-color: #e8f4f8;
270
+ }
271
+
272
+ .positive {
273
+ color: #27ae60;
274
+ }
275
+
276
+ .negative {
277
+ color: #e74c3c;
278
+ }
279
+
280
+ .neutral {
281
+ color: #7f8c8d;
282
+ }
283
+
284
+ .metrics-grid {
285
+ display: grid;
286
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
287
+ gap: 20px;
288
+ margin: 20px 0;
289
+ }
290
+
291
+ .metric-box {
292
+ background: linear-gradient(135deg, #f8f9fa 0%, #ecf0f1 100%);
293
+ padding: 15px;
294
+ border-radius: 8px;
295
+ }
296
+
297
+ .metric-box h4 {
298
+ color: #1a5f7a;
299
+ margin-bottom: 10px;
300
+ font-size: 0.9em;
301
+ text-transform: uppercase;
302
+ }
303
+
304
+ .trades-section {
305
+ margin-top: 30px;
306
+ }
307
+
308
+ .no-trades {
309
+ padding: 20px;
310
+ background: #f8f9fa;
311
+ text-align: center;
312
+ color: #7f8c8d;
313
+ border-radius: 8px;
314
+ }
315
+
316
+ footer {
317
+ text-align: center;
318
+ padding: 20px 0;
319
+ margin-top: 40px;
320
+ border-top: 2px solid #ecf0f1;
321
+ color: #7f8c8d;
322
+ font-size: 0.9em;
323
+ }
324
+ """
325
+
326
+ def _generate_executive_summary(self, results: DynamicScenarioResults) -> str:
327
+ """Generate executive summary section."""
328
+ pnl_class = "positive" if results.total_pnl >= 0 else "negative"
329
+ net_pnl_class = "positive" if results.net_pnl >= 0 else "negative"
330
+
331
+ html = f"""
332
+ <section class="executive-summary">
333
+ <h2>Executive Summary</h2>
334
+ <div class="summary-grid">
335
+ <div class="summary-card">
336
+ <h3>Scenario Path</h3>
337
+ <div class="value">{results.path_name}</div>
338
+ <p style="font-size: 0.85em; color: #7f8c8d; margin-top: 5px;">{results.num_days} days</p>
339
+ </div>
340
+ <div class="summary-card">
341
+ <h3>Initial Value</h3>
342
+ <div class="value">${results.baseline_value:,.2f}</div>
343
+ </div>
344
+ <div class="summary-card">
345
+ <h3>Final Value</h3>
346
+ <div class="value">${results.final_value:,.2f}</div>
347
+ </div>
348
+ <div class="summary-card {pnl_class}">
349
+ <h3>Total P&L</h3>
350
+ <div class="value {pnl_class}">${results.total_pnl:+,.2f}</div>
351
+ <p style="font-size: 0.85em; margin-top: 5px;">{results.total_pnl_pct:+.2f}%</p>
352
+ </div>
353
+ <div class="summary-card">
354
+ <h3>Transaction Costs</h3>
355
+ <div class="value">${results.total_transaction_costs:,.2f}</div>
356
+ </div>
357
+ <div class="summary-card {net_pnl_class}">
358
+ <h3>Net P&L</h3>
359
+ <div class="value {net_pnl_class}">${results.net_pnl:+,.2f}</div>
360
+ </div>
361
+ </div>
362
+ </section>
363
+ """
364
+ return html
365
+
366
+ def _generate_performance_metrics(self, results: DynamicScenarioResults) -> str:
367
+ """Generate performance metrics section."""
368
+ best = results.get_best_day()
369
+ worst = results.get_worst_day()
370
+ dd_amt, dd_pct, peak_day, trough_day = results.get_max_drawdown()
371
+
372
+ html = f"""
373
+ <section class="performance-metrics">
374
+ <h2>Performance Metrics</h2>
375
+ <div class="metrics-grid">
376
+ <div class="metric-box">
377
+ <h4>Best Day</h4>
378
+ <p><strong>Day {best.day_index if best else 'N/A'}</strong></p>
379
+ <p class="positive">P&L: ${best.daily_pnl if best else 0:+,.2f}</p>
380
+ </div>
381
+ <div class="metric-box">
382
+ <h4>Worst Day</h4>
383
+ <p><strong>Day {worst.day_index if worst else 'N/A'}</strong></p>
384
+ <p class="negative">P&L: ${worst.daily_pnl if worst else 0:+,.2f}</p>
385
+ </div>
386
+ <div class="metric-box">
387
+ <h4>Maximum Drawdown</h4>
388
+ <p class="negative"><strong>${dd_amt:,.2f}</strong> ({dd_pct:.2f}%)</p>
389
+ <p style="font-size: 0.85em; color: #7f8c8d;">Peak: Day {peak_day}, Trough: Day {trough_day}</p>
390
+ </div>
391
+ <div class="metric-box">
392
+ <h4>Hedge Trades</h4>
393
+ <p><strong>{results.total_hedges}</strong> trades executed</p>
394
+ <p style="font-size: 0.85em; color: #7f8c8d;">Avg cost: ${results.total_transaction_costs/max(results.total_hedges,1):,.2f}/trade</p>
395
+ </div>
396
+ </div>
397
+ </section>
398
+ """
399
+ return html
400
+
401
+ def _generate_charts_section(self, results: DynamicScenarioResults) -> str:
402
+ """Generate charts section with placeholders for Chart.js."""
403
+ html = """
404
+ <section class="charts">
405
+ <h2>Evolution Charts</h2>
406
+
407
+ <div class="chart-row">
408
+ <div class="chart-container">
409
+ <h3>Portfolio Value & P&L</h3>
410
+ <canvas id="pnlChart" height="300"></canvas>
411
+ </div>
412
+ <div class="chart-container">
413
+ <h3>Greeks Evolution</h3>
414
+ <canvas id="greeksChart" height="300"></canvas>
415
+ </div>
416
+ </div>
417
+
418
+ <div class="chart-row">
419
+ <div class="chart-container">
420
+ <h3>Market Parameters</h3>
421
+ <canvas id="marketChart" height="300"></canvas>
422
+ </div>
423
+ <div class="chart-container">
424
+ <h3>Cumulative P&L vs Transaction Costs</h3>
425
+ <canvas id="costsChart" height="300"></canvas>
426
+ </div>
427
+ </div>
428
+ </section>
429
+ """
430
+ return html
431
+
432
+ def _generate_day_by_day_table(self, results: DynamicScenarioResults) -> str:
433
+ """Generate day-by-day results table."""
434
+ html = """
435
+ <section class="day-by-day">
436
+ <h2>Day-by-Day Evolution</h2>
437
+ <div style="overflow-x: auto;">
438
+ <table>
439
+ <thead>
440
+ <tr>
441
+ <th>Day</th>
442
+ <th>Date</th>
443
+ <th>Label</th>
444
+ <th>Portfolio Value</th>
445
+ <th>Daily P&L</th>
446
+ <th>Cumulative P&L</th>
447
+ <th>Delta</th>
448
+ <th>Gamma</th>
449
+ <th>Vega</th>
450
+ <th>Trades</th>
451
+ </tr>
452
+ </thead>
453
+ <tbody>
454
+ """
455
+
456
+ for day in results.day_results:
457
+ pnl_class = "positive" if day.daily_pnl >= 0 else "negative"
458
+ cum_pnl_class = "positive" if day.cumulative_pnl >= 0 else "negative"
459
+ date_str = day.date.strftime("%Y-%m-%d") if day.date else "-"
460
+ label = day.label or "-"
461
+
462
+ html += f"""
463
+ <tr>
464
+ <td><strong>{day.day_index}</strong></td>
465
+ <td>{date_str}</td>
466
+ <td>{label}</td>
467
+ <td>${day.portfolio_value:,.2f}</td>
468
+ <td class="{pnl_class}">${day.daily_pnl:+,.2f}</td>
469
+ <td class="{cum_pnl_class}">${day.cumulative_pnl:+,.2f}</td>
470
+ <td>{day.delta:,.1f}</td>
471
+ <td>{day.gamma:,.4f}</td>
472
+ <td>{day.vega:,.2f}</td>
473
+ <td>{day.num_trades}</td>
474
+ </tr>
475
+ """
476
+
477
+ html += """
478
+ </tbody>
479
+ </table>
480
+ </div>
481
+ </section>
482
+ """
483
+ return html
484
+
485
+ def _generate_trades_section(self, results: DynamicScenarioResults) -> str:
486
+ """Generate trades section."""
487
+ all_trades = []
488
+ for day in results.day_results:
489
+ for trade in day.trades:
490
+ all_trades.append((day.day_index, day.date, trade))
491
+
492
+ if not all_trades:
493
+ return """
494
+ <section class="trades-section">
495
+ <h2>Trade History</h2>
496
+ <div class="no-trades">No trades executed during this scenario</div>
497
+ </section>
498
+ """
499
+
500
+ html = """
501
+ <section class="trades-section">
502
+ <h2>Trade History</h2>
503
+ <table>
504
+ <thead>
505
+ <tr>
506
+ <th>Day</th>
507
+ <th>Date</th>
508
+ <th>Type</th>
509
+ <th>Underlying</th>
510
+ <th>Quantity</th>
511
+ <th>Price</th>
512
+ <th>Notional</th>
513
+ <th>Cost</th>
514
+ <th>Reason</th>
515
+ </tr>
516
+ </thead>
517
+ <tbody>
518
+ """
519
+
520
+ for day_idx, date, trade in all_trades:
521
+ date_str = date.strftime("%Y-%m-%d") if date else "-"
522
+ html += f"""
523
+ <tr>
524
+ <td>{day_idx}</td>
525
+ <td>{date_str}</td>
526
+ <td>{trade.trade_type}</td>
527
+ <td>{trade.underlying}</td>
528
+ <td>{trade.quantity:+,.2f}</td>
529
+ <td>${trade.price:,.2f}</td>
530
+ <td>${trade.notional:,.2f}</td>
531
+ <td>${trade.transaction_cost:,.2f}</td>
532
+ <td>{trade.reason}</td>
533
+ </tr>
534
+ """
535
+
536
+ html += """
537
+ </tbody>
538
+ </table>
539
+ </section>
540
+ """
541
+ return html
542
+
543
+ def _generate_chart_scripts(self, results: DynamicScenarioResults) -> str:
544
+ """Generate Chart.js scripts for charts."""
545
+ # Prepare data
546
+ days = [d.day_index for d in results.day_results]
547
+ portfolio_values = [d.portfolio_value for d in results.day_results]
548
+ daily_pnl = [d.daily_pnl for d in results.day_results]
549
+ cumulative_pnl = [d.cumulative_pnl for d in results.day_results]
550
+ transaction_costs = [
551
+ d.cumulative_transaction_costs for d in results.day_results
552
+ ]
553
+ net_pnl = [d.net_pnl for d in results.day_results]
554
+
555
+ # Greeks
556
+ deltas = [d.delta for d in results.day_results]
557
+ gammas = [d.gamma * 1000 for d in results.day_results] # Scale for visibility
558
+ vegas = [d.vega for d in results.day_results]
559
+
560
+ # Market data (first underlying)
561
+ spots = []
562
+ vols = []
563
+ for d in results.day_results:
564
+ if d.market_state:
565
+ spot_vals = list(d.market_state.spot.values())
566
+ vol_vals = list(d.market_state.volatility.values())
567
+ spots.append(spot_vals[0] if spot_vals else 0)
568
+ vols.append(vol_vals[0] * 100 if vol_vals else 0) # Convert to %
569
+ else:
570
+ spots.append(0)
571
+ vols.append(0)
572
+
573
+ script = f"""
574
+ <script>
575
+ // P&L Chart
576
+ new Chart(document.getElementById('pnlChart'), {{
577
+ type: 'line',
578
+ data: {{
579
+ labels: {days},
580
+ datasets: [
581
+ {{
582
+ label: 'Portfolio Value',
583
+ data: {portfolio_values},
584
+ borderColor: '#1a5f7a',
585
+ backgroundColor: 'rgba(26, 95, 122, 0.1)',
586
+ fill: true,
587
+ tension: 0.3,
588
+ yAxisID: 'y'
589
+ }},
590
+ {{
591
+ label: 'Cumulative P&L',
592
+ data: {cumulative_pnl},
593
+ borderColor: '#27ae60',
594
+ borderDash: [5, 5],
595
+ tension: 0.3,
596
+ yAxisID: 'y1'
597
+ }}
598
+ ]
599
+ }},
600
+ options: {{
601
+ responsive: true,
602
+ interaction: {{ intersect: false, mode: 'index' }},
603
+ scales: {{
604
+ y: {{ type: 'linear', position: 'left', title: {{ display: true, text: 'Portfolio Value ($)' }} }},
605
+ y1: {{ type: 'linear', position: 'right', title: {{ display: true, text: 'P&L ($)' }}, grid: {{ drawOnChartArea: false }} }}
606
+ }}
607
+ }}
608
+ }});
609
+
610
+ // Greeks Chart
611
+ new Chart(document.getElementById('greeksChart'), {{
612
+ type: 'line',
613
+ data: {{
614
+ labels: {days},
615
+ datasets: [
616
+ {{
617
+ label: 'Delta',
618
+ data: {deltas},
619
+ borderColor: '#e74c3c',
620
+ tension: 0.3,
621
+ yAxisID: 'y'
622
+ }},
623
+ {{
624
+ label: 'Gamma (x1000)',
625
+ data: {gammas},
626
+ borderColor: '#9b59b6',
627
+ tension: 0.3,
628
+ yAxisID: 'y1'
629
+ }},
630
+ {{
631
+ label: 'Vega',
632
+ data: {vegas},
633
+ borderColor: '#f39c12',
634
+ tension: 0.3,
635
+ yAxisID: 'y1'
636
+ }}
637
+ ]
638
+ }},
639
+ options: {{
640
+ responsive: true,
641
+ interaction: {{ intersect: false, mode: 'index' }},
642
+ scales: {{
643
+ y: {{ type: 'linear', position: 'left', title: {{ display: true, text: 'Delta' }} }},
644
+ y1: {{ type: 'linear', position: 'right', title: {{ display: true, text: 'Gamma/Vega' }}, grid: {{ drawOnChartArea: false }} }}
645
+ }}
646
+ }}
647
+ }});
648
+
649
+ // Market Chart
650
+ new Chart(document.getElementById('marketChart'), {{
651
+ type: 'line',
652
+ data: {{
653
+ labels: {days},
654
+ datasets: [
655
+ {{
656
+ label: 'Spot Price',
657
+ data: {spots},
658
+ borderColor: '#1a5f7a',
659
+ tension: 0.3,
660
+ yAxisID: 'y'
661
+ }},
662
+ {{
663
+ label: 'Volatility (%)',
664
+ data: {vols},
665
+ borderColor: '#e74c3c',
666
+ borderDash: [5, 5],
667
+ tension: 0.3,
668
+ yAxisID: 'y1'
669
+ }}
670
+ ]
671
+ }},
672
+ options: {{
673
+ responsive: true,
674
+ interaction: {{ intersect: false, mode: 'index' }},
675
+ scales: {{
676
+ y: {{ type: 'linear', position: 'left', title: {{ display: true, text: 'Spot Price ($)' }} }},
677
+ y1: {{ type: 'linear', position: 'right', title: {{ display: true, text: 'Volatility (%)' }}, grid: {{ drawOnChartArea: false }} }}
678
+ }}
679
+ }}
680
+ }});
681
+
682
+ // Costs Chart
683
+ new Chart(document.getElementById('costsChart'), {{
684
+ type: 'line',
685
+ data: {{
686
+ labels: {days},
687
+ datasets: [
688
+ {{
689
+ label: 'Cumulative P&L',
690
+ data: {cumulative_pnl},
691
+ borderColor: '#27ae60',
692
+ backgroundColor: 'rgba(39, 174, 96, 0.1)',
693
+ fill: true,
694
+ tension: 0.3
695
+ }},
696
+ {{
697
+ label: 'Net P&L (after costs)',
698
+ data: {net_pnl},
699
+ borderColor: '#1a5f7a',
700
+ tension: 0.3
701
+ }},
702
+ {{
703
+ label: 'Transaction Costs',
704
+ data: {transaction_costs},
705
+ borderColor: '#e74c3c',
706
+ borderDash: [3, 3],
707
+ tension: 0.3
708
+ }}
709
+ ]
710
+ }},
711
+ options: {{
712
+ responsive: true,
713
+ interaction: {{ intersect: false, mode: 'index' }},
714
+ scales: {{
715
+ y: {{ title: {{ display: true, text: 'Value ($)' }} }}
716
+ }}
717
+ }}
718
+ }});
719
+ </script>
720
+ """
721
+ return script
722
+
723
+ # =========================================================================
724
+ # FI-SPECIFIC REPORT GENERATION
725
+ # =========================================================================
726
+
727
+ def generate_fi_report(
728
+ self,
729
+ results: "FIDynamicScenarioResults",
730
+ output_path: Union[str, Path],
731
+ title: Optional[str] = None,
732
+ include_charts: bool = True,
733
+ ) -> None:
734
+ """
735
+ Generate comprehensive HTML report for FI dynamic scenario.
736
+
737
+ Args:
738
+ results: FI Dynamic scenario results
739
+ output_path: Path for output HTML file
740
+ title: Optional custom title
741
+ include_charts: Whether to include embedded charts
742
+ """
743
+ output_path = Path(output_path)
744
+ output_path.parent.mkdir(parents=True, exist_ok=True)
745
+
746
+ if title is None:
747
+ title = f"FI Dynamic Scenario Report - {results.path_name}"
748
+
749
+ # Generate HTML content
750
+ html = self._generate_fi_html(results, title, include_charts)
751
+
752
+ # Write to file
753
+ with open(output_path, "w") as f:
754
+ f.write(html)
755
+
756
+ print(f"Generated FI HTML report: {output_path}")
757
+
758
+ def _generate_fi_html(
759
+ self, results: "FIDynamicScenarioResults", title: str, include_charts: bool
760
+ ) -> str:
761
+ """Generate complete FI HTML content."""
762
+
763
+ html = f"""<!DOCTYPE html>
764
+ <html lang="en">
765
+ <head>
766
+ <meta charset="UTF-8">
767
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
768
+ <title>{title}</title>
769
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
770
+ <style>
771
+ {self._get_css()}
772
+ </style>
773
+ </head>
774
+ <body>
775
+ <div class="container">
776
+ <header>
777
+ <h1>{title}</h1>
778
+ <p class="timestamp">Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
779
+ </header>
780
+
781
+ {self._generate_fi_executive_summary(results)}
782
+ {self._generate_fi_risk_metrics(results)}
783
+ {self._generate_fi_charts_section(results) if include_charts else ''}
784
+ {self._generate_fi_day_by_day_table(results)}
785
+ {self._generate_fi_trades_section(results)}
786
+
787
+ <footer>
788
+ <p>Generated by QuantArk FI Dynamic Scenario Analysis Module</p>
789
+ </footer>
790
+ </div>
791
+
792
+ {self._generate_fi_chart_scripts(results) if include_charts else ''}
793
+ </body>
794
+ </html>"""
795
+
796
+ return html
797
+
798
+ def _generate_fi_executive_summary(
799
+ self, results: "FIDynamicScenarioResults"
800
+ ) -> str:
801
+ """Generate FI executive summary section."""
802
+ pnl_class = "positive" if results.total_pnl >= 0 else "negative"
803
+ net_pnl_class = "positive" if results.net_pnl >= 0 else "negative"
804
+
805
+ html = f"""
806
+ <section class="executive-summary">
807
+ <h2>Executive Summary</h2>
808
+ <div class="summary-grid">
809
+ <div class="summary-card">
810
+ <h3>Scenario Path</h3>
811
+ <div class="value">{results.path_name}</div>
812
+ <p style="font-size: 0.85em; color: #7f8c8d; margin-top: 5px;">{results.num_days} days</p>
813
+ </div>
814
+ <div class="summary-card">
815
+ <h3>Initial Value</h3>
816
+ <div class="value">${results.baseline_value:,.2f}</div>
817
+ </div>
818
+ <div class="summary-card">
819
+ <h3>Final Value</h3>
820
+ <div class="value">${results.final_value:,.2f}</div>
821
+ </div>
822
+ <div class="summary-card {pnl_class}">
823
+ <h3>Total P&L</h3>
824
+ <div class="value {pnl_class}">${results.total_pnl:+,.2f}</div>
825
+ <p style="font-size: 0.85em; margin-top: 5px;">{results.total_pnl_pct:+.2f}%</p>
826
+ </div>
827
+ <div class="summary-card">
828
+ <h3>Initial DV01</h3>
829
+ <div class="value">${results.baseline_dv01:,.2f}</div>
830
+ </div>
831
+ <div class="summary-card">
832
+ <h3>Final DV01</h3>
833
+ <div class="value">${results.final_dv01:,.2f}</div>
834
+ </div>
835
+ <div class="summary-card">
836
+ <h3>Initial Duration</h3>
837
+ <div class="value">{results.baseline_duration:.2f} yrs</div>
838
+ </div>
839
+ <div class="summary-card">
840
+ <h3>Final Duration</h3>
841
+ <div class="value">{results.final_duration:.2f} yrs</div>
842
+ </div>
843
+ </div>
844
+ </section>
845
+ """
846
+ return html
847
+
848
+ def _generate_fi_risk_metrics(self, results: "FIDynamicScenarioResults") -> str:
849
+ """Generate FI risk metrics section."""
850
+ best = results.get_best_day()
851
+ worst = results.get_worst_day()
852
+ dd_amt, dd_pct, peak_day, trough_day = results.get_max_drawdown()
853
+ effectiveness = results.get_hedge_effectiveness()
854
+
855
+ html = f"""
856
+ <section class="performance-metrics">
857
+ <h2>Performance & Risk Metrics</h2>
858
+ <div class="metrics-grid">
859
+ <div class="metric-box">
860
+ <h4>Best Day</h4>
861
+ <p><strong>Day {best.day_index if best else 'N/A'}</strong></p>
862
+ <p class="positive">P&L: ${best.daily_pnl if best else 0:+,.2f}</p>
863
+ </div>
864
+ <div class="metric-box">
865
+ <h4>Worst Day</h4>
866
+ <p><strong>Day {worst.day_index if worst else 'N/A'}</strong></p>
867
+ <p class="negative">P&L: ${worst.daily_pnl if worst else 0:+,.2f}</p>
868
+ </div>
869
+ <div class="metric-box">
870
+ <h4>Maximum Drawdown</h4>
871
+ <p class="negative"><strong>${dd_amt:,.2f}</strong> ({dd_pct:.2f}%)</p>
872
+ <p style="font-size: 0.85em; color: #7f8c8d;">Peak: Day {peak_day}, Trough: Day {trough_day}</p>
873
+ </div>
874
+ <div class="metric-box">
875
+ <h4>Hedge Trades</h4>
876
+ <p><strong>{results.total_hedges}</strong> trades executed</p>
877
+ <p style="font-size: 0.85em; color: #7f8c8d;">Hedge freq: {effectiveness.get('hedge_frequency', 0):.1%}</p>
878
+ </div>
879
+ <div class="metric-box">
880
+ <h4>DV01 Mean (Post-Hedge)</h4>
881
+ <p><strong>${effectiveness.get('dv01_mean', 0):,.2f}</strong></p>
882
+ <p style="font-size: 0.85em; color: #7f8c8d;">Tracking error: ${effectiveness.get('dv01_tracking_error', 0):,.2f}</p>
883
+ </div>
884
+ <div class="metric-box">
885
+ <h4>Transaction Costs</h4>
886
+ <p><strong>${results.total_transaction_costs:,.2f}</strong></p>
887
+ <p style="font-size: 0.85em; color: #7f8c8d;">Net P&L: ${results.net_pnl:+,.2f}</p>
888
+ </div>
889
+ </div>
890
+ </section>
891
+ """
892
+ return html
893
+
894
+ def _generate_fi_charts_section(self, results: "FIDynamicScenarioResults") -> str:
895
+ """Generate FI charts section."""
896
+ html = """
897
+ <section class="charts">
898
+ <h2>Evolution Charts</h2>
899
+
900
+ <div class="chart-row">
901
+ <div class="chart-container">
902
+ <h3>Portfolio Value & P&L</h3>
903
+ <canvas id="pnlChart" height="300"></canvas>
904
+ </div>
905
+ <div class="chart-container">
906
+ <h3>DV01 Evolution</h3>
907
+ <canvas id="dv01Chart" height="300"></canvas>
908
+ </div>
909
+ </div>
910
+
911
+ <div class="chart-row">
912
+ <div class="chart-container">
913
+ <h3>Duration & Convexity</h3>
914
+ <canvas id="durationChart" height="300"></canvas>
915
+ </div>
916
+ <div class="chart-container">
917
+ <h3>Rate Evolution</h3>
918
+ <canvas id="rateChart" height="300"></canvas>
919
+ </div>
920
+ </div>
921
+ </section>
922
+ """
923
+ return html
924
+
925
+ def _generate_fi_day_by_day_table(self, results: "FIDynamicScenarioResults") -> str:
926
+ """Generate FI day-by-day results table."""
927
+ html = """
928
+ <section class="day-by-day">
929
+ <h2>Day-by-Day Evolution</h2>
930
+ <div style="overflow-x: auto;">
931
+ <table>
932
+ <thead>
933
+ <tr>
934
+ <th>Day</th>
935
+ <th>Label</th>
936
+ <th>Portfolio Value</th>
937
+ <th>Daily P&L</th>
938
+ <th>DV01 (Pre)</th>
939
+ <th>DV01 (Post)</th>
940
+ <th>Duration</th>
941
+ <th>Convexity</th>
942
+ <th>Rate (%)</th>
943
+ <th>Trades</th>
944
+ </tr>
945
+ </thead>
946
+ <tbody>
947
+ """
948
+
949
+ for day in results.day_results:
950
+ pnl_class = "positive" if day.daily_pnl >= 0 else "negative"
951
+ label = day.label or "-"
952
+ rate = day.market_state.rate * 100 if day.market_state else 0
953
+
954
+ html += f"""
955
+ <tr>
956
+ <td><strong>{day.day_index}</strong></td>
957
+ <td>{label}</td>
958
+ <td>${day.portfolio_value:,.2f}</td>
959
+ <td class="{pnl_class}">${day.daily_pnl:+,.2f}</td>
960
+ <td>${day.dv01_pre_hedge:,.2f}</td>
961
+ <td>${day.dv01_post_hedge:,.2f}</td>
962
+ <td>{day.modified_duration:.2f}</td>
963
+ <td>{day.convexity:,.4f}</td>
964
+ <td>{rate:.3f}</td>
965
+ <td>{day.num_trades}</td>
966
+ </tr>
967
+ """
968
+
969
+ html += """
970
+ </tbody>
971
+ </table>
972
+ </div>
973
+ </section>
974
+ """
975
+ return html
976
+
977
+ def _generate_fi_trades_section(self, results: "FIDynamicScenarioResults") -> str:
978
+ """Generate FI trades section."""
979
+ all_trades = []
980
+ for day in results.day_results:
981
+ for trade in day.trades:
982
+ all_trades.append((day.day_index, day.date, trade))
983
+
984
+ if not all_trades:
985
+ return """
986
+ <section class="trades-section">
987
+ <h2>Hedge Trade History</h2>
988
+ <div class="no-trades">No hedge trades executed during this scenario</div>
989
+ </section>
990
+ """
991
+
992
+ html = """
993
+ <section class="trades-section">
994
+ <h2>Hedge Trade History</h2>
995
+ <table>
996
+ <thead>
997
+ <tr>
998
+ <th>Day</th>
999
+ <th>Type</th>
1000
+ <th>Instrument</th>
1001
+ <th>Contracts</th>
1002
+ <th>Price</th>
1003
+ <th>DV01 Impact</th>
1004
+ <th>Cost</th>
1005
+ <th>Reason</th>
1006
+ </tr>
1007
+ </thead>
1008
+ <tbody>
1009
+ """
1010
+
1011
+ for day_idx, date, trade in all_trades:
1012
+ html += f"""
1013
+ <tr>
1014
+ <td>{day_idx}</td>
1015
+ <td>{trade.trade_type}</td>
1016
+ <td>{trade.instrument}</td>
1017
+ <td>{trade.contracts:+,.0f}</td>
1018
+ <td>${trade.price:,.2f}</td>
1019
+ <td>${trade.dv01_impact:+,.2f}</td>
1020
+ <td>${trade.transaction_cost:,.2f}</td>
1021
+ <td>{trade.reason}</td>
1022
+ </tr>
1023
+ """
1024
+
1025
+ html += """
1026
+ </tbody>
1027
+ </table>
1028
+ </section>
1029
+ """
1030
+ return html
1031
+
1032
+ def _generate_fi_chart_scripts(self, results: "FIDynamicScenarioResults") -> str:
1033
+ """Generate Chart.js scripts for FI charts."""
1034
+ days = [d.day_index for d in results.day_results]
1035
+ portfolio_values = [d.portfolio_value for d in results.day_results]
1036
+ cumulative_pnl = [d.cumulative_pnl for d in results.day_results]
1037
+ dv01_pre = [d.dv01_pre_hedge for d in results.day_results]
1038
+ dv01_post = [d.dv01_post_hedge for d in results.day_results]
1039
+ durations = [d.modified_duration for d in results.day_results]
1040
+ convexities = [d.convexity for d in results.day_results]
1041
+
1042
+ rates = []
1043
+ for d in results.day_results:
1044
+ if d.market_state:
1045
+ rates.append(d.market_state.rate * 100)
1046
+ else:
1047
+ rates.append(0)
1048
+
1049
+ script = f"""
1050
+ <script>
1051
+ // P&L Chart
1052
+ new Chart(document.getElementById('pnlChart'), {{
1053
+ type: 'line',
1054
+ data: {{
1055
+ labels: {days},
1056
+ datasets: [
1057
+ {{
1058
+ label: 'Portfolio Value',
1059
+ data: {portfolio_values},
1060
+ borderColor: '#1a5f7a',
1061
+ backgroundColor: 'rgba(26, 95, 122, 0.1)',
1062
+ fill: true,
1063
+ tension: 0.3,
1064
+ yAxisID: 'y'
1065
+ }},
1066
+ {{
1067
+ label: 'Cumulative P&L',
1068
+ data: {cumulative_pnl},
1069
+ borderColor: '#27ae60',
1070
+ borderDash: [5, 5],
1071
+ tension: 0.3,
1072
+ yAxisID: 'y1'
1073
+ }}
1074
+ ]
1075
+ }},
1076
+ options: {{
1077
+ responsive: true,
1078
+ interaction: {{ intersect: false, mode: 'index' }},
1079
+ scales: {{
1080
+ y: {{ type: 'linear', position: 'left', title: {{ display: true, text: 'Portfolio Value ($)' }} }},
1081
+ y1: {{ type: 'linear', position: 'right', title: {{ display: true, text: 'P&L ($)' }}, grid: {{ drawOnChartArea: false }} }}
1082
+ }}
1083
+ }}
1084
+ }});
1085
+
1086
+ // DV01 Chart
1087
+ new Chart(document.getElementById('dv01Chart'), {{
1088
+ type: 'line',
1089
+ data: {{
1090
+ labels: {days},
1091
+ datasets: [
1092
+ {{
1093
+ label: 'DV01 Pre-Hedge',
1094
+ data: {dv01_pre},
1095
+ borderColor: '#1a5f7a',
1096
+ tension: 0.3
1097
+ }},
1098
+ {{
1099
+ label: 'DV01 Post-Hedge',
1100
+ data: {dv01_post},
1101
+ borderColor: '#27ae60',
1102
+ borderDash: [5, 5],
1103
+ tension: 0.3
1104
+ }}
1105
+ ]
1106
+ }},
1107
+ options: {{
1108
+ responsive: true,
1109
+ interaction: {{ intersect: false, mode: 'index' }},
1110
+ scales: {{
1111
+ y: {{ title: {{ display: true, text: 'DV01 ($)' }} }}
1112
+ }}
1113
+ }}
1114
+ }});
1115
+
1116
+ // Duration Chart
1117
+ new Chart(document.getElementById('durationChart'), {{
1118
+ type: 'line',
1119
+ data: {{
1120
+ labels: {days},
1121
+ datasets: [
1122
+ {{
1123
+ label: 'Modified Duration',
1124
+ data: {durations},
1125
+ borderColor: '#9b59b6',
1126
+ backgroundColor: 'rgba(155, 89, 182, 0.1)',
1127
+ fill: true,
1128
+ tension: 0.3,
1129
+ yAxisID: 'y'
1130
+ }},
1131
+ {{
1132
+ label: 'Convexity',
1133
+ data: {convexities},
1134
+ borderColor: '#f39c12',
1135
+ tension: 0.3,
1136
+ yAxisID: 'y1'
1137
+ }}
1138
+ ]
1139
+ }},
1140
+ options: {{
1141
+ responsive: true,
1142
+ interaction: {{ intersect: false, mode: 'index' }},
1143
+ scales: {{
1144
+ y: {{ type: 'linear', position: 'left', title: {{ display: true, text: 'Duration (years)' }} }},
1145
+ y1: {{ type: 'linear', position: 'right', title: {{ display: true, text: 'Convexity' }}, grid: {{ drawOnChartArea: false }} }}
1146
+ }}
1147
+ }}
1148
+ }});
1149
+
1150
+ // Rate Chart
1151
+ new Chart(document.getElementById('rateChart'), {{
1152
+ type: 'line',
1153
+ data: {{
1154
+ labels: {days},
1155
+ datasets: [
1156
+ {{
1157
+ label: 'Rate (%)',
1158
+ data: {rates},
1159
+ borderColor: '#e74c3c',
1160
+ backgroundColor: 'rgba(231, 76, 60, 0.1)',
1161
+ fill: true,
1162
+ tension: 0.3
1163
+ }}
1164
+ ]
1165
+ }},
1166
+ options: {{
1167
+ responsive: true,
1168
+ scales: {{
1169
+ y: {{ title: {{ display: true, text: 'Rate (%)' }} }}
1170
+ }}
1171
+ }}
1172
+ }});
1173
+ </script>
1174
+ """
1175
+ return script