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,14 @@
1
+ """
2
+ PDE-based pricing engines for bonds.
3
+ """
4
+ from quantark.asset.bond.engine.pde.convertible import (
5
+ ConvertibleBondPDEParams,
6
+ ConvertibleBondJumpDiffusionEngine,
7
+ ConvertibleBondTFEngine,
8
+ )
9
+
10
+ __all__ = [
11
+ "ConvertibleBondPDEParams",
12
+ "ConvertibleBondJumpDiffusionEngine",
13
+ "ConvertibleBondTFEngine",
14
+ ]
@@ -0,0 +1,21 @@
1
+ """
2
+ PDE-based pricing engines for convertible bonds.
3
+
4
+ This module provides:
5
+ - ConvertibleBondPDEParams: Configuration for PDE-based pricing
6
+ - ConvertibleBondJumpDiffusionEngine: Bloomberg OVCV jump-diffusion model
7
+ - ConvertibleBondTFEngine: Tsiveriotis-Fernandes decomposition model
8
+ """
9
+ from quantark.asset.bond.engine.pde.convertible.pde_params import ConvertibleBondPDEParams
10
+ from quantark.asset.bond.engine.pde.convertible.jump_diffusion_engine import (
11
+ ConvertibleBondJumpDiffusionEngine,
12
+ )
13
+ from quantark.asset.bond.engine.pde.convertible.tf_engine import (
14
+ ConvertibleBondTFEngine,
15
+ )
16
+
17
+ __all__ = [
18
+ "ConvertibleBondPDEParams",
19
+ "ConvertibleBondJumpDiffusionEngine",
20
+ "ConvertibleBondTFEngine",
21
+ ]
@@ -0,0 +1,603 @@
1
+ """
2
+ Jump-diffusion PDE engine for convertible bond pricing.
3
+
4
+ Implements the Bloomberg OVCV model where the stock follows a jump-diffusion
5
+ process with credit risk modeled via hazard rate.
6
+ """
7
+ import math
8
+ from dataclasses import dataclass
9
+ from datetime import datetime, timedelta
10
+ from typing import Dict, Optional, Tuple
11
+
12
+ import numpy as np
13
+ from scipy import sparse
14
+ from scipy.sparse.linalg import spsolve
15
+
16
+ from quantark.asset.bond.product.convertible.convertible_bond import ConvertibleBond
17
+ from quantark.asset.bond.engine.pde.convertible.pde_params import ConvertibleBondPDEParams
18
+ from quantark.priceenv import PricingEnvironment
19
+ from quantark.util.exceptions import ValidationError, PricingError
20
+ from quantark.util.numerical import Tolerance, safe_exp, safe_sqrt, safe_log, is_zero
21
+
22
+
23
+ @dataclass
24
+ class ConvertibleBondJumpDiffusionResult:
25
+ """
26
+ Result container for jump-diffusion PDE engine pricing.
27
+
28
+ Attributes:
29
+ price: Clean price of the convertible bond
30
+ dirty_price: Dirty price including accrued interest
31
+ delta: Price sensitivity to stock price
32
+ gamma: Second derivative of price with respect to stock
33
+ theta: Time decay (daily)
34
+ conversion_probability: Risk-neutral probability of eventual conversion
35
+ """
36
+
37
+ price: float
38
+ dirty_price: float
39
+ delta: float = 0.0
40
+ gamma: float = 0.0
41
+ theta: float = 0.0
42
+ conversion_probability: float = 0.0
43
+
44
+
45
+ class ConvertibleBondJumpDiffusionEngine:
46
+ """
47
+ Bloomberg OVCV jump-diffusion model for convertible bonds.
48
+
49
+ This engine implements a finite-difference PDE solver where the stock
50
+ follows:
51
+ dS = (r - q - lambda * eta) * S * dt + sigma * S * dW - eta * S * dN
52
+
53
+ where:
54
+ lambda = hazard rate (default intensity)
55
+ eta = stock price drop on default (stock_jump_on_default)
56
+ dN = Poisson process with intensity lambda
57
+
58
+ The convertible bond PDE becomes:
59
+ V_t + 0.5 * sigma^2 * S^2 * V_SS + (r - q - lambda*eta) * S * V_S
60
+ - r * V - lambda * (V - recovery) = 0
61
+
62
+ Boundary conditions:
63
+ - At S=0: V = recovery * face_value
64
+ - At S=S_max: V = conversion_ratio * S (pure equity)
65
+ - At maturity: V = max(face_value, conversion_ratio * S)
66
+ """
67
+
68
+ def __init__(
69
+ self,
70
+ pricing_env: PricingEnvironment,
71
+ params: Optional[ConvertibleBondPDEParams] = None,
72
+ ):
73
+ """
74
+ Initialize the jump-diffusion PDE engine.
75
+
76
+ Args:
77
+ pricing_env: Pricing environment with market data
78
+ params: PDE configuration parameters (optional)
79
+ """
80
+ if pricing_env is None:
81
+ raise ValidationError("Pricing environment is required")
82
+
83
+ self.pricing_env = pricing_env
84
+ self.params = params if params is not None else ConvertibleBondPDEParams()
85
+
86
+ def price(self, bond: ConvertibleBond) -> float:
87
+ """
88
+ Calculate the clean price of the convertible bond.
89
+
90
+ Args:
91
+ bond: Convertible bond to price
92
+
93
+ Returns:
94
+ Clean price of the bond
95
+ """
96
+ result = self.price_with_details(bond)
97
+ return result.price
98
+
99
+ def price_with_details(
100
+ self, bond: ConvertibleBond
101
+ ) -> ConvertibleBondJumpDiffusionResult:
102
+ """
103
+ Calculate price with detailed results including Greeks.
104
+
105
+ Args:
106
+ bond: Convertible bond to price
107
+
108
+ Returns:
109
+ ConvertibleBondJumpDiffusionResult with full pricing details
110
+ """
111
+ valuation_date = self.pricing_env.valuation_date
112
+
113
+ # Validate inputs
114
+ if bond.is_expired(valuation_date):
115
+ raise PricingError("Cannot price expired bond")
116
+
117
+ # Get market data
118
+ spot = self.pricing_env.spot
119
+ T = bond.time_to_maturity(valuation_date)
120
+
121
+ # Credit parameters from bond
122
+ lambda_h = bond.hazard_rate # Hazard rate
123
+ eta = bond.stock_jump_on_default # Stock drop on default
124
+ recovery = bond.recovery_rate
125
+
126
+ # Dividend yield
127
+ q = bond.continuous_dividend_yield
128
+
129
+ # Build grid
130
+ S_min = spot * self.params.min_stock_multiple
131
+ S_max = spot * self.params.max_stock_multiple
132
+ N_s = self.params.num_space_steps
133
+ N_t = self.params.num_time_steps
134
+ dt = T / N_t
135
+
136
+ # Use log-space grid for better accuracy
137
+ log_S_min = safe_log(S_min)
138
+ log_S_max = safe_log(S_max)
139
+ log_S = np.linspace(log_S_min, log_S_max, N_s + 1)
140
+ S = np.exp(log_S)
141
+ dS = S[1:] - S[:-1] # Variable spacing
142
+
143
+ # Initialize solution
144
+ V = self._terminal_condition(bond, S)
145
+ P = self._terminal_conversion_probability(bond, S)
146
+
147
+ # Get coupon schedule
148
+ coupon_schedule = self._build_coupon_schedule(bond, valuation_date)
149
+
150
+ # Time stepping - backward from T to 0
151
+ # All times are measured in years from valuation date
152
+ for n in range(N_t - 1, -1, -1):
153
+ t = n * dt # Current time (years from valuation)
154
+ t_next = t + dt # End of this step (closer to maturity)
155
+ node_date = valuation_date + timedelta(days=int(t * 365))
156
+
157
+ # Use Rannacher smoothing for first few steps
158
+ use_implicit = n >= N_t - self.params.rannacher_steps
159
+
160
+ # Query time-local forward rate for this step
161
+ r_local = self.pricing_env.rate_curve.get_forward_rate(t, t_next)
162
+
163
+ # Query time-local effective volatility for this step
164
+ vol_local = self.pricing_env.get_step_volatility(spot, t, t_next)
165
+
166
+ # Apply coupon payments (jump condition) to the known later-time state
167
+ coupon_amount = 0.0
168
+ for ct, ca in coupon_schedule:
169
+ if t < ct <= t_next:
170
+ coupon_amount += ca
171
+ if coupon_amount > 0.0:
172
+ V = V + coupon_amount
173
+
174
+ # Build discretization matrices with time-local parameters
175
+ A, b = self._build_matrices(
176
+ S, V, r_local, q, vol_local, lambda_h, eta, recovery, bond, dt, use_implicit
177
+ )
178
+
179
+ # Solve system
180
+ if use_implicit or self.params.scheme == "implicit_euler":
181
+ # Implicit: solve A * V_new = b
182
+ V = spsolve(A.tocsr(), b)
183
+ else:
184
+ # Crank-Nicolson or explicit
185
+ V = spsolve(A.tocsr(), b)
186
+
187
+ conversion_possible = bond.conversion_end_date >= node_date
188
+ p_upper_boundary = 1.0 if conversion_possible else 0.0
189
+ A_p, b_p = self._build_probability_matrices(
190
+ S,
191
+ P,
192
+ r_local,
193
+ q,
194
+ vol_local,
195
+ lambda_h,
196
+ eta,
197
+ dt,
198
+ use_implicit,
199
+ p_upper_boundary=p_upper_boundary,
200
+ )
201
+ P = spsolve(A_p.tocsr(), b_p)
202
+ P = np.clip(P, 0.0, 1.0)
203
+
204
+ # Apply early exercise constraints to both value and probability
205
+ V, P = self._apply_constraints(bond, S, V, node_date, P)
206
+ P = np.clip(P, 0.0, 1.0)
207
+
208
+ # Interpolate to get value at spot
209
+ spot_idx = np.searchsorted(S, spot)
210
+ if spot_idx == 0:
211
+ dirty_price = V[0]
212
+ conv_prob = P[0]
213
+ elif spot_idx >= len(S):
214
+ dirty_price = V[-1]
215
+ conv_prob = P[-1]
216
+ else:
217
+ # Linear interpolation
218
+ w = (spot - S[spot_idx - 1]) / (S[spot_idx] - S[spot_idx - 1])
219
+ dirty_price = (1 - w) * V[spot_idx - 1] + w * V[spot_idx]
220
+ conv_prob = (1 - w) * P[spot_idx - 1] + w * P[spot_idx]
221
+ conv_prob = float(np.clip(conv_prob, 0.0, 1.0))
222
+
223
+ # Calculate Greeks
224
+ delta, gamma = self._calculate_greeks(S, V, spot, spot_idx)
225
+ theta = self._calculate_theta(bond, dirty_price)
226
+
227
+ # Calculate accrued interest
228
+ accrued = bond.calculate_accrued_interest(valuation_date)
229
+ clean_price = dirty_price - accrued
230
+
231
+ return ConvertibleBondJumpDiffusionResult(
232
+ price=clean_price,
233
+ dirty_price=dirty_price,
234
+ delta=delta,
235
+ gamma=gamma,
236
+ theta=theta,
237
+ conversion_probability=conv_prob,
238
+ )
239
+
240
+ def _terminal_condition(
241
+ self, bond: ConvertibleBond, S: np.ndarray
242
+ ) -> np.ndarray:
243
+ """
244
+ Compute terminal condition at maturity.
245
+
246
+ At maturity: V = max(face_value, conversion_ratio * S)
247
+
248
+ Args:
249
+ bond: Convertible bond
250
+ S: Stock price grid
251
+
252
+ Returns:
253
+ Array of terminal values
254
+ """
255
+ face_value = bond.face_value
256
+ conversion_value = bond.conversion_ratio * S
257
+ if bond.is_convertible_at(bond.maturity_date):
258
+ return np.maximum(face_value, conversion_value)
259
+ return face_value * np.ones_like(S)
260
+
261
+ def _terminal_conversion_probability(
262
+ self, bond: ConvertibleBond, S: np.ndarray
263
+ ) -> np.ndarray:
264
+ """
265
+ Terminal condition for the eventual conversion probability.
266
+
267
+ At maturity: P = 1 if conversion is optimal and allowed, else 0.
268
+ """
269
+ if not bond.is_convertible_at(bond.maturity_date):
270
+ return np.zeros_like(S)
271
+ conversion_value = bond.conversion_ratio * S
272
+ return (conversion_value > bond.face_value).astype(float)
273
+
274
+ def _build_matrices(
275
+ self,
276
+ S: np.ndarray,
277
+ V: np.ndarray,
278
+ r: float,
279
+ q: float,
280
+ vol: float,
281
+ lambda_h: float,
282
+ eta: float,
283
+ recovery: float,
284
+ bond: ConvertibleBond,
285
+ dt: float,
286
+ use_implicit: bool,
287
+ ) -> Tuple[sparse.csr_matrix, np.ndarray]:
288
+ """
289
+ Build finite difference matrices for the PDE.
290
+
291
+ Args:
292
+ S: Stock price grid
293
+ V: Current solution
294
+ r, q: Rate and dividend yield
295
+ vol: Volatility
296
+ lambda_h: Hazard rate
297
+ eta: Stock jump on default
298
+ recovery: Recovery rate
299
+ bond: Convertible bond
300
+ dt: Time step
301
+ use_implicit: Whether to use fully implicit scheme
302
+
303
+ Returns:
304
+ Tuple of (A matrix, b vector) for A*V_new = b
305
+ """
306
+ N = len(S)
307
+ recovery_value = recovery * bond.face_value
308
+
309
+ # Coefficient for drift term (adjusted for default jump)
310
+ drift = r - q - lambda_h * eta
311
+
312
+ # Build tridiagonal matrix coefficients
313
+ # Using central differences for interior points
314
+ diag = np.zeros(N)
315
+ lower = np.zeros(N - 1)
316
+ upper = np.zeros(N - 1)
317
+
318
+ for i in range(1, N - 1):
319
+ h_minus = S[i] - S[i - 1]
320
+ h_plus = S[i + 1] - S[i]
321
+ h = 0.5 * (h_minus + h_plus)
322
+
323
+ # Diffusion coefficient: 0.5 * sigma^2 * S^2
324
+ D = 0.5 * vol * vol * S[i] * S[i]
325
+
326
+ # Convection coefficient: drift * S
327
+ C = drift * S[i]
328
+
329
+ # Reaction coefficient: -(r + lambda)
330
+ R = -(r + lambda_h)
331
+
332
+ # Central difference approximation
333
+ lower[i - 1] = D / (h_minus * h) - C / (2 * h)
334
+ diag[i] = -2 * D / (h_minus * h_plus) + R
335
+ upper[i] = D / (h_plus * h) + C / (2 * h)
336
+
337
+ # Boundary conditions
338
+ # At S=0: V = recovery_value (Dirichlet)
339
+ diag[0] = 1.0
340
+ upper[0] = 0.0
341
+
342
+ # At S_max: V = conversion_ratio * S (Dirichlet for deep ITM)
343
+ diag[-1] = 1.0
344
+ lower[-1] = 0.0
345
+
346
+ # Build matrices based on scheme
347
+ if use_implicit:
348
+ # Fully implicit: (I - dt*A) * V_new = V + dt * source
349
+ theta_scheme = 1.0
350
+ elif self.params.scheme == "explicit_euler":
351
+ theta_scheme = 0.0
352
+ else: # crank_nicolson
353
+ theta_scheme = 0.5
354
+
355
+ # A_new = I - theta * dt * L
356
+ # A_old = I + (1 - theta) * dt * L
357
+ # A_new * V_new = A_old * V + dt * source
358
+
359
+ L = sparse.diags(
360
+ [lower, diag, upper], [-1, 0, 1], shape=(N, N), format="csr"
361
+ )
362
+
363
+ I = sparse.eye(N, format="csr")
364
+
365
+ A = I - theta_scheme * dt * L
366
+
367
+ # Right-hand side
368
+ if theta_scheme < 1.0:
369
+ A_old = I + (1 - theta_scheme) * dt * L
370
+ b = A_old @ V
371
+ else:
372
+ b = V.copy()
373
+
374
+ # Add jump term contribution (recovery on default)
375
+ b[1:-1] += dt * lambda_h * recovery_value
376
+
377
+ # Apply boundary conditions
378
+ b[0] = recovery_value
379
+ b[-1] = bond.conversion_ratio * S[-1]
380
+
381
+ return A, b
382
+
383
+ def _build_probability_matrices(
384
+ self,
385
+ S: np.ndarray,
386
+ P: np.ndarray,
387
+ r: float,
388
+ q: float,
389
+ vol: float,
390
+ lambda_h: float,
391
+ eta: float,
392
+ dt: float,
393
+ use_implicit: bool,
394
+ p_upper_boundary: float,
395
+ ) -> Tuple[sparse.csr_matrix, np.ndarray]:
396
+ """
397
+ Build finite difference matrices for the eventual conversion probability.
398
+
399
+ The probability PDE incorporates default as a killing term (-lambda * P)
400
+ and uses Dirichlet boundaries at the grid endpoints.
401
+ """
402
+ N = len(S)
403
+
404
+ drift = r - q - lambda_h * eta
405
+
406
+ diag = np.zeros(N)
407
+ lower = np.zeros(N - 1)
408
+ upper = np.zeros(N - 1)
409
+
410
+ for i in range(1, N - 1):
411
+ h_minus = S[i] - S[i - 1]
412
+ h_plus = S[i + 1] - S[i]
413
+ h = 0.5 * (h_minus + h_plus)
414
+
415
+ D = 0.5 * vol * vol * S[i] * S[i]
416
+ C = drift * S[i]
417
+ R = -lambda_h
418
+
419
+ lower[i - 1] = D / (h_minus * h) - C / (2 * h)
420
+ diag[i] = -2 * D / (h_minus * h_plus) + R
421
+ upper[i] = D / (h_plus * h) + C / (2 * h)
422
+
423
+ # Dirichlet boundaries: enforce via zero operator rows
424
+ diag[0] = 0.0
425
+ diag[-1] = 0.0
426
+ upper[0] = 0.0
427
+ lower[-1] = 0.0
428
+
429
+ if use_implicit:
430
+ theta_scheme = 1.0
431
+ elif self.params.scheme == "explicit_euler":
432
+ theta_scheme = 0.0
433
+ else: # crank_nicolson
434
+ theta_scheme = 0.5
435
+
436
+ L = sparse.diags(
437
+ [lower, diag, upper], [-1, 0, 1], shape=(N, N), format="csr"
438
+ )
439
+ I = sparse.eye(N, format="csr")
440
+
441
+ A = I - theta_scheme * dt * L
442
+
443
+ if theta_scheme < 1.0:
444
+ A_old = I + (1 - theta_scheme) * dt * L
445
+ b = A_old @ P
446
+ else:
447
+ b = P.copy()
448
+
449
+ b[0] = 0.0
450
+ b[-1] = p_upper_boundary
451
+
452
+ return A, b
453
+
454
+ def _apply_constraints(
455
+ self,
456
+ bond: ConvertibleBond,
457
+ S: np.ndarray,
458
+ V: np.ndarray,
459
+ node_date: datetime,
460
+ P: np.ndarray,
461
+ ) -> Tuple[np.ndarray, np.ndarray]:
462
+ """
463
+ Apply early exercise constraints (conversion, call, put).
464
+
465
+ Args:
466
+ bond: Convertible bond
467
+ S: Stock price grid
468
+ V: Current values
469
+ node_date: Current date in backward induction
470
+
471
+ Returns:
472
+ Tuple of (updated values, updated conversion probability) with
473
+ constraints applied
474
+ """
475
+ V_new = V.copy()
476
+ P_new = P.copy()
477
+
478
+ # Conversion constraint (holder's right)
479
+ if bond.is_convertible_at(node_date):
480
+ conversion_value = bond.conversion_ratio * S
481
+ convert_mask = conversion_value >= (V_new - Tolerance.PRECISION)
482
+ V_new = np.maximum(V_new, conversion_value)
483
+ if np.any(convert_mask):
484
+ P_new[convert_mask] = 1.0
485
+
486
+ # Call constraint (issuer's right)
487
+ call_price = bond.get_call_price_at(node_date)
488
+ if call_price is not None:
489
+ for i, stock in enumerate(S):
490
+ if bond.is_callable_at(node_date, stock):
491
+ # Issuer calls if V > call_price
492
+ # Holder chooses max(conversion, call_price)
493
+ if V_new[i] > call_price:
494
+ conversion_value = bond.conversion_ratio * stock
495
+ if bond.is_convertible_at(node_date):
496
+ V_new[i] = max(conversion_value, call_price)
497
+ if conversion_value > call_price:
498
+ P_new[i] = 1.0
499
+ else:
500
+ P_new[i] = 0.0
501
+ else:
502
+ V_new[i] = call_price
503
+ P_new[i] = 0.0
504
+
505
+ # Put constraint (holder's right)
506
+ put_price = bond.get_put_price_at(node_date)
507
+ if put_price is not None:
508
+ put_mask = put_price >= (V_new - Tolerance.PRECISION)
509
+ V_new = np.maximum(V_new, put_price)
510
+ if np.any(put_mask):
511
+ P_new[put_mask] = 0.0
512
+
513
+ return V_new, P_new
514
+
515
+ def _build_coupon_schedule(
516
+ self, bond: ConvertibleBond, valuation_date: datetime
517
+ ) -> list:
518
+ """
519
+ Build list of (time, amount) tuples for coupon payments.
520
+
521
+ Args:
522
+ bond: Convertible bond
523
+ valuation_date: Valuation date
524
+
525
+ Returns:
526
+ List of (time_to_payment, coupon_amount) tuples
527
+ """
528
+ T = bond.time_to_maturity(valuation_date)
529
+ schedule = []
530
+
531
+ for cf in bond.get_all_cashflows():
532
+ cf_time = (cf.payment_date - valuation_date).days / 365.0
533
+ if 0 < cf_time <= T:
534
+ # Extract just the coupon (not principal)
535
+ coupon_amount = cf.amount
536
+ if cf.payment_date >= bond.maturity_date:
537
+ coupon_amount -= bond.face_value
538
+ if coupon_amount > 0:
539
+ schedule.append((cf_time, coupon_amount))
540
+
541
+ return schedule
542
+
543
+ def _calculate_greeks(
544
+ self,
545
+ S: np.ndarray,
546
+ V: np.ndarray,
547
+ spot: float,
548
+ spot_idx: int,
549
+ ) -> Tuple[float, float]:
550
+ """
551
+ Calculate delta and gamma from the PDE grid.
552
+
553
+ Args:
554
+ S: Stock price grid
555
+ V: Solution values
556
+ spot: Current spot price
557
+ spot_idx: Index of spot in grid
558
+
559
+ Returns:
560
+ Tuple of (delta, gamma)
561
+ """
562
+ if spot_idx <= 1 or spot_idx >= len(S) - 1:
563
+ return 0.0, 0.0
564
+
565
+ # Use central differences
566
+ i = spot_idx
567
+ h_minus = S[i] - S[i - 1]
568
+ h_plus = S[i + 1] - S[i]
569
+
570
+ # Delta: dV/dS
571
+ delta = (V[i + 1] - V[i - 1]) / (h_plus + h_minus)
572
+
573
+ # Gamma: d2V/dS2
574
+ gamma = 2.0 * (
575
+ V[i + 1] / (h_plus * (h_plus + h_minus))
576
+ - V[i] / (h_plus * h_minus)
577
+ + V[i - 1] / (h_minus * (h_plus + h_minus))
578
+ )
579
+
580
+ return delta, gamma
581
+
582
+ def _calculate_theta(
583
+ self, bond: ConvertibleBond, current_price: float
584
+ ) -> float:
585
+ """
586
+ Calculate theta (time decay) by finite difference.
587
+
588
+ Args:
589
+ bond: Convertible bond
590
+ current_price: Current dirty price
591
+
592
+ Returns:
593
+ Daily theta
594
+ """
595
+ # Theta calculation would require pricing at t+dt
596
+ # For now, return 0 (could be implemented with bump-and-reprice)
597
+ return 0.0
598
+
599
+ def __repr__(self):
600
+ return (
601
+ f"ConvertibleBondJumpDiffusionEngine("
602
+ f"scheme={self.params.scheme})"
603
+ )
@@ -0,0 +1,59 @@
1
+ """
2
+ Configuration parameters for PDE-based convertible bond pricing engines.
3
+ """
4
+ from dataclasses import dataclass
5
+ from quantark.util.enum.engine_enums import PDEMethod
6
+
7
+
8
+ @dataclass
9
+ class ConvertibleBondPDEParams:
10
+ """
11
+ Configuration parameters for PDE-based convertible bond pricing.
12
+
13
+ Attributes:
14
+ num_space_steps: Number of grid points in stock price dimension (default: 200)
15
+ num_time_steps: Number of time steps (default: 500)
16
+ scheme: PDE scheme (crank_nicolson, implicit_euler, explicit_euler)
17
+ min_stock_multiple: Minimum stock price as multiple of current (default: 0.01)
18
+ max_stock_multiple: Maximum stock price as multiple of current (default: 5.0)
19
+ rannacher_steps: Number of initial Rannacher smoothing steps (default: 4)
20
+ bump_size: Bump size for finite difference Greeks (default: 0.01)
21
+ tolerance: Convergence tolerance for iterative schemes (default: 1e-8)
22
+ """
23
+
24
+ num_space_steps: int = 200
25
+ num_time_steps: int = 500
26
+ scheme: str = "crank_nicolson"
27
+ min_stock_multiple: float = 0.01
28
+ max_stock_multiple: float = 5.0
29
+ rannacher_steps: int = 4
30
+ bump_size: float = 0.01
31
+ tolerance: float = 1e-8
32
+
33
+ def __post_init__(self):
34
+ """Validate parameters."""
35
+ if self.num_space_steps < 10:
36
+ raise ValueError(
37
+ f"num_space_steps must be >= 10, got {self.num_space_steps}"
38
+ )
39
+ if self.num_time_steps < 10:
40
+ raise ValueError(
41
+ f"num_time_steps must be >= 10, got {self.num_time_steps}"
42
+ )
43
+ valid_schemes = ["crank_nicolson", "implicit_euler", "explicit_euler"]
44
+ if self.scheme not in valid_schemes:
45
+ raise ValueError(
46
+ f"scheme must be one of {valid_schemes}, got {self.scheme}"
47
+ )
48
+ if self.min_stock_multiple <= 0:
49
+ raise ValueError(
50
+ f"min_stock_multiple must be positive, got {self.min_stock_multiple}"
51
+ )
52
+ if self.max_stock_multiple <= 1:
53
+ raise ValueError(
54
+ f"max_stock_multiple must be > 1, got {self.max_stock_multiple}"
55
+ )
56
+ if self.rannacher_steps < 0:
57
+ raise ValueError(
58
+ f"rannacher_steps must be >= 0, got {self.rannacher_steps}"
59
+ )