@cj-tech-master/excelts 9.2.1 → 9.3.0

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 (383) hide show
  1. package/README.md +25 -2
  2. package/README_zh.md +29 -6
  3. package/dist/browser/index.browser.d.ts +1 -1
  4. package/dist/browser/index.browser.js +4 -0
  5. package/dist/browser/index.d.ts +1 -1
  6. package/dist/browser/index.js +4 -0
  7. package/dist/browser/modules/excel/cell.d.ts +17 -3
  8. package/dist/browser/modules/excel/cell.js +170 -22
  9. package/dist/browser/modules/excel/defined-names.d.ts +96 -1
  10. package/dist/browser/modules/excel/defined-names.js +411 -21
  11. package/dist/browser/modules/excel/image.d.ts +11 -0
  12. package/dist/browser/modules/excel/image.js +24 -1
  13. package/dist/browser/modules/excel/stream/workbook-reader.browser.d.ts +9 -3
  14. package/dist/browser/modules/excel/stream/workbook-reader.browser.js +14 -0
  15. package/dist/browser/modules/excel/stream/workbook-reader.d.ts +2 -1
  16. package/dist/browser/modules/excel/stream/workbook-writer.browser.d.ts +39 -5
  17. package/dist/browser/modules/excel/stream/workbook-writer.browser.js +48 -1
  18. package/dist/browser/modules/excel/stream/workbook-writer.d.ts +3 -2
  19. package/dist/browser/modules/excel/stream/worksheet-reader.js +17 -1
  20. package/dist/browser/modules/excel/stream/worksheet-writer.d.ts +39 -6
  21. package/dist/browser/modules/excel/stream/worksheet-writer.js +45 -5
  22. package/dist/browser/modules/excel/table.js +15 -2
  23. package/dist/browser/modules/excel/types.d.ts +133 -2
  24. package/dist/browser/modules/excel/utils/col-cache.d.ts +1 -0
  25. package/dist/browser/modules/excel/utils/col-cache.js +15 -0
  26. package/dist/browser/modules/excel/utils/drawing-utils.d.ts +3 -3
  27. package/dist/browser/modules/excel/utils/drawing-utils.js +4 -0
  28. package/dist/browser/modules/excel/utils/external-link-formula.d.ts +76 -0
  29. package/dist/browser/modules/excel/utils/external-link-formula.js +208 -0
  30. package/dist/browser/modules/excel/utils/iterate-stream.d.ts +9 -3
  31. package/dist/browser/modules/excel/utils/iterate-stream.js +3 -1
  32. package/dist/browser/modules/excel/utils/ooxml-paths.d.ts +19 -0
  33. package/dist/browser/modules/excel/utils/ooxml-paths.js +37 -2
  34. package/dist/browser/modules/excel/utils/shared-strings.d.ts +8 -3
  35. package/dist/browser/modules/excel/utils/shared-strings.js +21 -2
  36. package/dist/browser/modules/excel/utils/workbook-protection.d.ts +30 -0
  37. package/dist/browser/modules/excel/utils/workbook-protection.js +30 -0
  38. package/dist/browser/modules/excel/workbook.browser.d.ts +257 -6
  39. package/dist/browser/modules/excel/workbook.browser.js +318 -34
  40. package/dist/browser/modules/excel/workbook.d.ts +1 -1
  41. package/dist/browser/modules/excel/worksheet.d.ts +3 -1
  42. package/dist/browser/modules/excel/worksheet.js +21 -2
  43. package/dist/browser/modules/excel/xlsx/rel-type.d.ts +15 -0
  44. package/dist/browser/modules/excel/xlsx/rel-type.js +16 -1
  45. package/dist/browser/modules/excel/xlsx/xform/book/defined-name-xform.d.ts +6 -5
  46. package/dist/browser/modules/excel/xlsx/xform/book/defined-name-xform.js +21 -86
  47. package/dist/browser/modules/excel/xlsx/xform/book/external-link-xform.d.ts +84 -0
  48. package/dist/browser/modules/excel/xlsx/xform/book/external-link-xform.js +330 -0
  49. package/dist/browser/modules/excel/xlsx/xform/book/external-reference-xform.d.ts +17 -0
  50. package/dist/browser/modules/excel/xlsx/xform/book/external-reference-xform.js +24 -0
  51. package/dist/browser/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.d.ts +3 -0
  52. package/dist/browser/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.js +11 -2
  53. package/dist/browser/modules/excel/xlsx/xform/book/workbook-protection-xform.d.ts +20 -0
  54. package/dist/browser/modules/excel/xlsx/xform/book/workbook-protection-xform.js +66 -0
  55. package/dist/browser/modules/excel/xlsx/xform/book/workbook-xform.js +38 -5
  56. package/dist/browser/modules/excel/xlsx/xform/core/content-types-xform.js +19 -1
  57. package/dist/browser/modules/excel/xlsx/xform/core/metadata-xform.d.ts +56 -0
  58. package/dist/browser/modules/excel/xlsx/xform/core/metadata-xform.js +158 -0
  59. package/dist/browser/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.d.ts +26 -0
  60. package/dist/browser/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +105 -0
  61. package/dist/browser/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +3 -0
  62. package/dist/browser/modules/excel/xlsx/xform/drawing/drawing-xform.js +10 -2
  63. package/dist/browser/modules/excel/xlsx/xform/sheet/cell-xform.d.ts +1 -1
  64. package/dist/browser/modules/excel/xlsx/xform/sheet/cell-xform.js +166 -8
  65. package/dist/browser/modules/excel/xlsx/xform/sheet/data-validations-xform.js +1 -1
  66. package/dist/browser/modules/excel/xlsx/xform/sheet/ignored-errors-xform.d.ts +21 -0
  67. package/dist/browser/modules/excel/xlsx/xform/sheet/ignored-errors-xform.js +80 -0
  68. package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +9 -4
  69. package/dist/browser/modules/excel/xlsx/xform/style/border-xform.js +4 -1
  70. package/dist/browser/modules/excel/xlsx/xlsx.browser.d.ts +172 -13
  71. package/dist/browser/modules/excel/xlsx/xlsx.browser.js +410 -20
  72. package/dist/browser/modules/excel/xlsx/xlsx.d.ts +7 -4
  73. package/dist/browser/modules/excel/xlsx/xlsx.js +4 -5
  74. package/dist/browser/modules/formula/compile/address-utils.d.ts +62 -0
  75. package/dist/browser/modules/formula/compile/address-utils.js +83 -0
  76. package/dist/browser/modules/formula/compile/binder.d.ts +42 -0
  77. package/dist/browser/modules/formula/compile/binder.js +487 -0
  78. package/dist/browser/modules/formula/compile/bound-ast.d.ts +230 -0
  79. package/dist/browser/modules/formula/compile/bound-ast.js +80 -0
  80. package/dist/browser/modules/formula/compile/compiled-formula.d.ts +137 -0
  81. package/dist/browser/modules/formula/compile/compiled-formula.js +383 -0
  82. package/dist/browser/modules/formula/compile/dependency-analysis.d.ts +93 -0
  83. package/dist/browser/modules/formula/compile/dependency-analysis.js +432 -0
  84. package/dist/browser/modules/formula/compile/structured-ref-utils.d.ts +93 -0
  85. package/dist/browser/modules/formula/compile/structured-ref-utils.js +136 -0
  86. package/dist/browser/modules/formula/default-syntax-probe.d.ts +79 -0
  87. package/dist/browser/modules/formula/default-syntax-probe.js +83 -0
  88. package/dist/browser/modules/formula/functions/_date-context.d.ts +4 -0
  89. package/dist/browser/modules/formula/functions/_date-context.js +29 -0
  90. package/dist/browser/modules/formula/functions/_shared.d.ts +121 -0
  91. package/dist/browser/modules/formula/functions/_shared.js +381 -0
  92. package/dist/browser/modules/formula/functions/conditional.d.ts +27 -0
  93. package/dist/browser/modules/formula/functions/conditional.js +343 -0
  94. package/dist/browser/modules/formula/functions/database.d.ts +37 -0
  95. package/dist/browser/modules/formula/functions/database.js +274 -0
  96. package/dist/browser/modules/formula/functions/date.d.ts +61 -0
  97. package/dist/browser/modules/formula/functions/date.js +855 -0
  98. package/dist/browser/modules/formula/functions/dynamic-array.d.ts +23 -0
  99. package/dist/browser/modules/formula/functions/dynamic-array.js +860 -0
  100. package/dist/browser/modules/formula/functions/engineering.d.ts +57 -0
  101. package/dist/browser/modules/formula/functions/engineering.js +1128 -0
  102. package/dist/browser/modules/formula/functions/financial.d.ts +202 -0
  103. package/dist/browser/modules/formula/functions/financial.js +2296 -0
  104. package/dist/browser/modules/formula/functions/lookup.d.ts +18 -0
  105. package/dist/browser/modules/formula/functions/lookup.js +886 -0
  106. package/dist/browser/modules/formula/functions/math.d.ts +114 -0
  107. package/dist/browser/modules/formula/functions/math.js +1406 -0
  108. package/dist/browser/modules/formula/functions/statistical.d.ts +193 -0
  109. package/dist/browser/modules/formula/functions/statistical.js +3390 -0
  110. package/dist/browser/modules/formula/functions/text.d.ts +86 -0
  111. package/dist/browser/modules/formula/functions/text.js +1845 -0
  112. package/dist/browser/modules/formula/host-registry.d.ts +53 -0
  113. package/dist/browser/modules/formula/host-registry.js +69 -0
  114. package/dist/browser/modules/formula/index.d.ts +39 -0
  115. package/dist/browser/modules/formula/index.js +49 -0
  116. package/dist/browser/modules/formula/install.d.ts +62 -0
  117. package/dist/browser/modules/formula/install.js +88 -0
  118. package/dist/browser/modules/formula/integration/apply-writeback-plan.d.ts +26 -0
  119. package/dist/browser/modules/formula/integration/apply-writeback-plan.js +210 -0
  120. package/dist/browser/modules/formula/integration/calculate-formulas-impl.d.ts +30 -0
  121. package/dist/browser/modules/formula/integration/calculate-formulas-impl.js +616 -0
  122. package/dist/browser/modules/formula/integration/calculate-formulas.d.ts +67 -0
  123. package/dist/browser/modules/formula/integration/calculate-formulas.js +68 -0
  124. package/dist/browser/modules/formula/integration/formula-instance.d.ts +64 -0
  125. package/dist/browser/modules/formula/integration/formula-instance.js +79 -0
  126. package/dist/browser/modules/formula/integration/workbook-adapter.d.ts +26 -0
  127. package/dist/browser/modules/formula/integration/workbook-adapter.js +324 -0
  128. package/dist/browser/modules/formula/integration/workbook-snapshot.d.ts +267 -0
  129. package/dist/browser/modules/formula/integration/workbook-snapshot.js +77 -0
  130. package/dist/browser/modules/formula/materialize/build-writeback-plan.d.ts +34 -0
  131. package/dist/browser/modules/formula/materialize/build-writeback-plan.js +473 -0
  132. package/dist/browser/modules/formula/materialize/spill-engine.d.ts +9 -0
  133. package/dist/browser/modules/formula/materialize/spill-engine.js +38 -0
  134. package/dist/browser/modules/formula/materialize/types.d.ts +179 -0
  135. package/dist/browser/modules/formula/materialize/types.js +29 -0
  136. package/dist/browser/modules/formula/materialize/writeback-plan.d.ts +167 -0
  137. package/dist/browser/modules/formula/materialize/writeback-plan.js +27 -0
  138. package/dist/browser/modules/formula/runtime/evaluator.d.ts +151 -0
  139. package/dist/browser/modules/formula/runtime/evaluator.js +2291 -0
  140. package/dist/browser/modules/formula/runtime/function-registry.d.ts +47 -0
  141. package/dist/browser/modules/formula/runtime/function-registry.js +840 -0
  142. package/dist/browser/modules/formula/runtime/values.d.ts +211 -0
  143. package/dist/browser/modules/formula/runtime/values.js +385 -0
  144. package/dist/browser/modules/formula/syntax/ast.d.ts +129 -0
  145. package/dist/browser/modules/formula/syntax/ast.js +28 -0
  146. package/dist/browser/modules/formula/syntax/parser.d.ts +18 -0
  147. package/dist/browser/modules/formula/syntax/parser.js +439 -0
  148. package/dist/browser/modules/formula/syntax/token-types.d.ts +153 -0
  149. package/dist/browser/modules/formula/syntax/token-types.js +59 -0
  150. package/dist/browser/modules/formula/syntax/tokenizer.d.ts +10 -0
  151. package/dist/browser/modules/formula/syntax/tokenizer.js +1074 -0
  152. package/dist/browser/modules/pdf/excel-bridge.js +9 -0
  153. package/dist/cjs/index.js +4 -0
  154. package/dist/cjs/modules/excel/cell.js +170 -22
  155. package/dist/cjs/modules/excel/defined-names.js +411 -21
  156. package/dist/cjs/modules/excel/image.js +24 -1
  157. package/dist/cjs/modules/excel/stream/workbook-reader.browser.js +14 -0
  158. package/dist/cjs/modules/excel/stream/workbook-writer.browser.js +48 -1
  159. package/dist/cjs/modules/excel/stream/worksheet-reader.js +17 -1
  160. package/dist/cjs/modules/excel/stream/worksheet-writer.js +45 -5
  161. package/dist/cjs/modules/excel/table.js +15 -2
  162. package/dist/cjs/modules/excel/utils/col-cache.js +15 -0
  163. package/dist/cjs/modules/excel/utils/drawing-utils.js +4 -0
  164. package/dist/cjs/modules/excel/utils/external-link-formula.js +212 -0
  165. package/dist/cjs/modules/excel/utils/iterate-stream.js +3 -1
  166. package/dist/cjs/modules/excel/utils/ooxml-paths.js +42 -2
  167. package/dist/cjs/modules/excel/utils/shared-strings.js +21 -2
  168. package/dist/cjs/modules/excel/utils/workbook-protection.js +33 -0
  169. package/dist/cjs/modules/excel/workbook.browser.js +318 -34
  170. package/dist/cjs/modules/excel/worksheet.js +20 -1
  171. package/dist/cjs/modules/excel/xlsx/rel-type.js +16 -1
  172. package/dist/cjs/modules/excel/xlsx/xform/book/defined-name-xform.js +21 -86
  173. package/dist/cjs/modules/excel/xlsx/xform/book/external-link-xform.js +333 -0
  174. package/dist/cjs/modules/excel/xlsx/xform/book/external-reference-xform.js +27 -0
  175. package/dist/cjs/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.js +11 -2
  176. package/dist/cjs/modules/excel/xlsx/xform/book/workbook-protection-xform.js +69 -0
  177. package/dist/cjs/modules/excel/xlsx/xform/book/workbook-xform.js +38 -5
  178. package/dist/cjs/modules/excel/xlsx/xform/core/content-types-xform.js +18 -0
  179. package/dist/cjs/modules/excel/xlsx/xform/core/metadata-xform.js +161 -0
  180. package/dist/cjs/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +108 -0
  181. package/dist/cjs/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +3 -0
  182. package/dist/cjs/modules/excel/xlsx/xform/drawing/drawing-xform.js +10 -2
  183. package/dist/cjs/modules/excel/xlsx/xform/sheet/cell-xform.js +166 -8
  184. package/dist/cjs/modules/excel/xlsx/xform/sheet/data-validations-xform.js +1 -1
  185. package/dist/cjs/modules/excel/xlsx/xform/sheet/ignored-errors-xform.js +83 -0
  186. package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +9 -4
  187. package/dist/cjs/modules/excel/xlsx/xform/style/border-xform.js +4 -1
  188. package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +408 -18
  189. package/dist/cjs/modules/excel/xlsx/xlsx.js +4 -5
  190. package/dist/cjs/modules/formula/compile/address-utils.js +89 -0
  191. package/dist/cjs/modules/formula/compile/binder.js +489 -0
  192. package/dist/cjs/modules/formula/compile/bound-ast.js +68 -0
  193. package/dist/cjs/modules/formula/compile/compiled-formula.js +387 -0
  194. package/dist/cjs/modules/formula/compile/dependency-analysis.js +437 -0
  195. package/dist/cjs/modules/formula/compile/structured-ref-utils.js +141 -0
  196. package/dist/cjs/modules/formula/default-syntax-probe.js +87 -0
  197. package/dist/cjs/modules/formula/functions/_date-context.js +33 -0
  198. package/dist/cjs/modules/formula/functions/_shared.js +396 -0
  199. package/dist/cjs/modules/formula/functions/conditional.js +354 -0
  200. package/dist/cjs/modules/formula/functions/database.js +288 -0
  201. package/dist/cjs/modules/formula/functions/date.js +883 -0
  202. package/dist/cjs/modules/formula/functions/dynamic-array.js +881 -0
  203. package/dist/cjs/modules/formula/functions/engineering.js +1183 -0
  204. package/dist/cjs/modules/formula/functions/financial.js +2348 -0
  205. package/dist/cjs/modules/formula/functions/lookup.js +902 -0
  206. package/dist/cjs/modules/formula/functions/math.js +1487 -0
  207. package/dist/cjs/modules/formula/functions/statistical.js +3488 -0
  208. package/dist/cjs/modules/formula/functions/text.js +1889 -0
  209. package/dist/cjs/modules/formula/host-registry.js +75 -0
  210. package/dist/cjs/modules/formula/index.js +58 -0
  211. package/dist/cjs/modules/formula/install.js +93 -0
  212. package/dist/cjs/modules/formula/integration/apply-writeback-plan.js +213 -0
  213. package/dist/cjs/modules/formula/integration/calculate-formulas-impl.js +619 -0
  214. package/dist/cjs/modules/formula/integration/calculate-formulas.js +71 -0
  215. package/dist/cjs/modules/formula/integration/formula-instance.js +82 -0
  216. package/dist/cjs/modules/formula/integration/workbook-adapter.js +327 -0
  217. package/dist/cjs/modules/formula/integration/workbook-snapshot.js +84 -0
  218. package/dist/cjs/modules/formula/materialize/build-writeback-plan.js +475 -0
  219. package/dist/cjs/modules/formula/materialize/spill-engine.js +42 -0
  220. package/dist/cjs/modules/formula/materialize/types.js +32 -0
  221. package/dist/cjs/modules/formula/materialize/writeback-plan.js +28 -0
  222. package/dist/cjs/modules/formula/runtime/evaluator.js +2298 -0
  223. package/dist/cjs/modules/formula/runtime/function-registry.js +846 -0
  224. package/dist/cjs/modules/formula/runtime/values.js +385 -0
  225. package/dist/cjs/modules/formula/syntax/ast.js +8 -0
  226. package/dist/cjs/modules/formula/syntax/parser.js +440 -0
  227. package/dist/cjs/modules/formula/syntax/token-types.js +32 -0
  228. package/dist/cjs/modules/formula/syntax/tokenizer.js +1076 -0
  229. package/dist/cjs/modules/pdf/excel-bridge.js +9 -0
  230. package/dist/esm/index.browser.js +4 -0
  231. package/dist/esm/index.js +4 -0
  232. package/dist/esm/modules/excel/cell.js +170 -22
  233. package/dist/esm/modules/excel/defined-names.js +411 -21
  234. package/dist/esm/modules/excel/image.js +24 -1
  235. package/dist/esm/modules/excel/stream/workbook-reader.browser.js +14 -0
  236. package/dist/esm/modules/excel/stream/workbook-writer.browser.js +48 -1
  237. package/dist/esm/modules/excel/stream/worksheet-reader.js +17 -1
  238. package/dist/esm/modules/excel/stream/worksheet-writer.js +45 -5
  239. package/dist/esm/modules/excel/table.js +15 -2
  240. package/dist/esm/modules/excel/utils/col-cache.js +15 -0
  241. package/dist/esm/modules/excel/utils/drawing-utils.js +4 -0
  242. package/dist/esm/modules/excel/utils/external-link-formula.js +208 -0
  243. package/dist/esm/modules/excel/utils/iterate-stream.js +3 -1
  244. package/dist/esm/modules/excel/utils/ooxml-paths.js +37 -2
  245. package/dist/esm/modules/excel/utils/shared-strings.js +21 -2
  246. package/dist/esm/modules/excel/utils/workbook-protection.js +30 -0
  247. package/dist/esm/modules/excel/workbook.browser.js +318 -34
  248. package/dist/esm/modules/excel/worksheet.js +21 -2
  249. package/dist/esm/modules/excel/xlsx/rel-type.js +16 -1
  250. package/dist/esm/modules/excel/xlsx/xform/book/defined-name-xform.js +21 -86
  251. package/dist/esm/modules/excel/xlsx/xform/book/external-link-xform.js +330 -0
  252. package/dist/esm/modules/excel/xlsx/xform/book/external-reference-xform.js +24 -0
  253. package/dist/esm/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.js +11 -2
  254. package/dist/esm/modules/excel/xlsx/xform/book/workbook-protection-xform.js +66 -0
  255. package/dist/esm/modules/excel/xlsx/xform/book/workbook-xform.js +38 -5
  256. package/dist/esm/modules/excel/xlsx/xform/core/content-types-xform.js +19 -1
  257. package/dist/esm/modules/excel/xlsx/xform/core/metadata-xform.js +158 -0
  258. package/dist/esm/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +105 -0
  259. package/dist/esm/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +3 -0
  260. package/dist/esm/modules/excel/xlsx/xform/drawing/drawing-xform.js +10 -2
  261. package/dist/esm/modules/excel/xlsx/xform/sheet/cell-xform.js +166 -8
  262. package/dist/esm/modules/excel/xlsx/xform/sheet/data-validations-xform.js +1 -1
  263. package/dist/esm/modules/excel/xlsx/xform/sheet/ignored-errors-xform.js +80 -0
  264. package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +9 -4
  265. package/dist/esm/modules/excel/xlsx/xform/style/border-xform.js +4 -1
  266. package/dist/esm/modules/excel/xlsx/xlsx.browser.js +410 -20
  267. package/dist/esm/modules/excel/xlsx/xlsx.js +4 -5
  268. package/dist/esm/modules/formula/compile/address-utils.js +83 -0
  269. package/dist/esm/modules/formula/compile/binder.js +487 -0
  270. package/dist/esm/modules/formula/compile/bound-ast.js +80 -0
  271. package/dist/esm/modules/formula/compile/compiled-formula.js +383 -0
  272. package/dist/esm/modules/formula/compile/dependency-analysis.js +432 -0
  273. package/dist/esm/modules/formula/compile/structured-ref-utils.js +136 -0
  274. package/dist/esm/modules/formula/default-syntax-probe.js +83 -0
  275. package/dist/esm/modules/formula/functions/_date-context.js +29 -0
  276. package/dist/esm/modules/formula/functions/_shared.js +381 -0
  277. package/dist/esm/modules/formula/functions/conditional.js +343 -0
  278. package/dist/esm/modules/formula/functions/database.js +274 -0
  279. package/dist/esm/modules/formula/functions/date.js +855 -0
  280. package/dist/esm/modules/formula/functions/dynamic-array.js +860 -0
  281. package/dist/esm/modules/formula/functions/engineering.js +1128 -0
  282. package/dist/esm/modules/formula/functions/financial.js +2296 -0
  283. package/dist/esm/modules/formula/functions/lookup.js +886 -0
  284. package/dist/esm/modules/formula/functions/math.js +1406 -0
  285. package/dist/esm/modules/formula/functions/statistical.js +3390 -0
  286. package/dist/esm/modules/formula/functions/text.js +1845 -0
  287. package/dist/esm/modules/formula/host-registry.js +69 -0
  288. package/dist/esm/modules/formula/index.js +49 -0
  289. package/dist/esm/modules/formula/install.js +88 -0
  290. package/dist/esm/modules/formula/integration/apply-writeback-plan.js +210 -0
  291. package/dist/esm/modules/formula/integration/calculate-formulas-impl.js +616 -0
  292. package/dist/esm/modules/formula/integration/calculate-formulas.js +68 -0
  293. package/dist/esm/modules/formula/integration/formula-instance.js +79 -0
  294. package/dist/esm/modules/formula/integration/workbook-adapter.js +324 -0
  295. package/dist/esm/modules/formula/integration/workbook-snapshot.js +77 -0
  296. package/dist/esm/modules/formula/materialize/build-writeback-plan.js +473 -0
  297. package/dist/esm/modules/formula/materialize/spill-engine.js +38 -0
  298. package/dist/esm/modules/formula/materialize/types.js +29 -0
  299. package/dist/esm/modules/formula/materialize/writeback-plan.js +27 -0
  300. package/dist/esm/modules/formula/runtime/evaluator.js +2291 -0
  301. package/dist/esm/modules/formula/runtime/function-registry.js +840 -0
  302. package/dist/esm/modules/formula/runtime/values.js +385 -0
  303. package/dist/esm/modules/formula/syntax/ast.js +28 -0
  304. package/dist/esm/modules/formula/syntax/parser.js +439 -0
  305. package/dist/esm/modules/formula/syntax/token-types.js +59 -0
  306. package/dist/esm/modules/formula/syntax/tokenizer.js +1074 -0
  307. package/dist/esm/modules/pdf/excel-bridge.js +9 -0
  308. package/dist/iife/excelts.iife.js +2302 -373
  309. package/dist/iife/excelts.iife.js.map +1 -1
  310. package/dist/iife/excelts.iife.min.js +34 -34
  311. package/dist/types/index.browser.d.ts +1 -1
  312. package/dist/types/index.d.ts +1 -1
  313. package/dist/types/modules/excel/cell.d.ts +17 -3
  314. package/dist/types/modules/excel/defined-names.d.ts +96 -1
  315. package/dist/types/modules/excel/image.d.ts +11 -0
  316. package/dist/types/modules/excel/stream/workbook-reader.browser.d.ts +9 -3
  317. package/dist/types/modules/excel/stream/workbook-reader.d.ts +2 -1
  318. package/dist/types/modules/excel/stream/workbook-writer.browser.d.ts +39 -5
  319. package/dist/types/modules/excel/stream/workbook-writer.d.ts +3 -2
  320. package/dist/types/modules/excel/stream/worksheet-writer.d.ts +39 -6
  321. package/dist/types/modules/excel/types.d.ts +133 -2
  322. package/dist/types/modules/excel/utils/col-cache.d.ts +1 -0
  323. package/dist/types/modules/excel/utils/drawing-utils.d.ts +3 -3
  324. package/dist/types/modules/excel/utils/external-link-formula.d.ts +76 -0
  325. package/dist/types/modules/excel/utils/iterate-stream.d.ts +9 -3
  326. package/dist/types/modules/excel/utils/ooxml-paths.d.ts +19 -0
  327. package/dist/types/modules/excel/utils/shared-strings.d.ts +8 -3
  328. package/dist/types/modules/excel/utils/workbook-protection.d.ts +30 -0
  329. package/dist/types/modules/excel/workbook.browser.d.ts +257 -6
  330. package/dist/types/modules/excel/workbook.d.ts +1 -1
  331. package/dist/types/modules/excel/worksheet.d.ts +3 -1
  332. package/dist/types/modules/excel/xlsx/rel-type.d.ts +15 -0
  333. package/dist/types/modules/excel/xlsx/xform/book/defined-name-xform.d.ts +6 -5
  334. package/dist/types/modules/excel/xlsx/xform/book/external-link-xform.d.ts +84 -0
  335. package/dist/types/modules/excel/xlsx/xform/book/external-reference-xform.d.ts +17 -0
  336. package/dist/types/modules/excel/xlsx/xform/book/workbook-calc-properties-xform.d.ts +3 -0
  337. package/dist/types/modules/excel/xlsx/xform/book/workbook-protection-xform.d.ts +20 -0
  338. package/dist/types/modules/excel/xlsx/xform/core/metadata-xform.d.ts +56 -0
  339. package/dist/types/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.d.ts +26 -0
  340. package/dist/types/modules/excel/xlsx/xform/sheet/cell-xform.d.ts +1 -1
  341. package/dist/types/modules/excel/xlsx/xform/sheet/ignored-errors-xform.d.ts +21 -0
  342. package/dist/types/modules/excel/xlsx/xlsx.browser.d.ts +172 -13
  343. package/dist/types/modules/excel/xlsx/xlsx.d.ts +7 -4
  344. package/dist/types/modules/formula/compile/address-utils.d.ts +62 -0
  345. package/dist/types/modules/formula/compile/binder.d.ts +42 -0
  346. package/dist/types/modules/formula/compile/bound-ast.d.ts +230 -0
  347. package/dist/types/modules/formula/compile/compiled-formula.d.ts +137 -0
  348. package/dist/types/modules/formula/compile/dependency-analysis.d.ts +93 -0
  349. package/dist/types/modules/formula/compile/structured-ref-utils.d.ts +93 -0
  350. package/dist/types/modules/formula/default-syntax-probe.d.ts +79 -0
  351. package/dist/types/modules/formula/functions/_date-context.d.ts +4 -0
  352. package/dist/types/modules/formula/functions/_shared.d.ts +121 -0
  353. package/dist/types/modules/formula/functions/conditional.d.ts +27 -0
  354. package/dist/types/modules/formula/functions/database.d.ts +37 -0
  355. package/dist/types/modules/formula/functions/date.d.ts +61 -0
  356. package/dist/types/modules/formula/functions/dynamic-array.d.ts +23 -0
  357. package/dist/types/modules/formula/functions/engineering.d.ts +57 -0
  358. package/dist/types/modules/formula/functions/financial.d.ts +202 -0
  359. package/dist/types/modules/formula/functions/lookup.d.ts +18 -0
  360. package/dist/types/modules/formula/functions/math.d.ts +114 -0
  361. package/dist/types/modules/formula/functions/statistical.d.ts +193 -0
  362. package/dist/types/modules/formula/functions/text.d.ts +86 -0
  363. package/dist/types/modules/formula/host-registry.d.ts +53 -0
  364. package/dist/types/modules/formula/index.d.ts +39 -0
  365. package/dist/types/modules/formula/install.d.ts +62 -0
  366. package/dist/types/modules/formula/integration/apply-writeback-plan.d.ts +26 -0
  367. package/dist/types/modules/formula/integration/calculate-formulas-impl.d.ts +30 -0
  368. package/dist/types/modules/formula/integration/calculate-formulas.d.ts +67 -0
  369. package/dist/types/modules/formula/integration/formula-instance.d.ts +64 -0
  370. package/dist/types/modules/formula/integration/workbook-adapter.d.ts +26 -0
  371. package/dist/types/modules/formula/integration/workbook-snapshot.d.ts +267 -0
  372. package/dist/types/modules/formula/materialize/build-writeback-plan.d.ts +34 -0
  373. package/dist/types/modules/formula/materialize/spill-engine.d.ts +9 -0
  374. package/dist/types/modules/formula/materialize/types.d.ts +179 -0
  375. package/dist/types/modules/formula/materialize/writeback-plan.d.ts +167 -0
  376. package/dist/types/modules/formula/runtime/evaluator.d.ts +151 -0
  377. package/dist/types/modules/formula/runtime/function-registry.d.ts +47 -0
  378. package/dist/types/modules/formula/runtime/values.d.ts +211 -0
  379. package/dist/types/modules/formula/syntax/ast.d.ts +129 -0
  380. package/dist/types/modules/formula/syntax/parser.d.ts +18 -0
  381. package/dist/types/modules/formula/syntax/token-types.d.ts +153 -0
  382. package/dist/types/modules/formula/syntax/tokenizer.d.ts +10 -0
  383. package/package.json +28 -28
@@ -0,0 +1,2348 @@
1
+ "use strict";
2
+ /**
3
+ * Financial Functions — Native RuntimeValue implementation.
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.fnCOUPDAYS = exports.fnCOUPDAYBS = exports.fnCOUPDAYSNC = exports.fnCOUPNUM = exports.fnCOUPPCD = exports.fnCOUPNCD = exports.fnYIELDMAT = exports.fnPRICEMAT = exports.fnTBILLEQ = exports.fnTBILLYIELD = exports.fnTBILLPRICE = exports.fnACCRINTM = exports.fnACCRINT = exports.fnMDURATION = exports.fnDURATION = exports.fnYIELD = exports.fnPRICE = exports.fnINTRATE = exports.fnRECEIVED = exports.fnYIELDDISC = exports.fnPRICEDISC = exports.fnDISC = exports.fnDOLLARFR = exports.fnDOLLARDE = exports.fnCUMIPMT = exports.fnCUMPRINC = exports.fnISPMT = exports.fnMIRR = exports.fnXIRR = exports.fnXNPV = exports.fnNOMINAL = exports.fnEFFECT = exports.fnRRI = exports.fnPDURATION = exports.fnFVSCHEDULE = exports.fnPPMT = exports.fnIPMT = exports.fnDDB = exports.fnDB = exports.fnVDB = exports.fnSYD = exports.fnSLN = exports.fnRATE = exports.fnNPER = exports.fnIRR = exports.fnNPV = exports.fnPV = exports.fnFV = exports.fnPMT = void 0;
7
+ const utils_base_1 = require("../../../utils/utils.base.js");
8
+ const values_1 = require("../runtime/values");
9
+ const _date_context_1 = require("./_date-context");
10
+ const _shared_1 = require("./_shared");
11
+ /**
12
+ * Convert an Excel serial to a UTC `Date`, honouring the active date1904 mode.
13
+ *
14
+ * All date math in this module must read fields with the `getUTC*` family
15
+ * because `excelToDate()` returns a `Date` whose UTC timeline corresponds
16
+ * to the Excel serial. Using local-time accessors would make bond maths
17
+ * drift by a day whenever the host lives west of UTC.
18
+ */
19
+ function toDate(serial) {
20
+ return (0, utils_base_1.excelToDate)(serial, (0, _date_context_1.isDate1904)());
21
+ }
22
+ // ============================================================================
23
+ // Financial Functions
24
+ // ============================================================================
25
+ const fnPMT = args => {
26
+ const rate = (0, values_1.toNumberRV)(args[0]);
27
+ if ((0, values_1.isError)(rate)) {
28
+ return rate;
29
+ }
30
+ const nper = (0, values_1.toNumberRV)(args[1]);
31
+ if ((0, values_1.isError)(nper)) {
32
+ return nper;
33
+ }
34
+ const pv = (0, values_1.toNumberRV)(args[2]);
35
+ if ((0, values_1.isError)(pv)) {
36
+ return pv;
37
+ }
38
+ const fv = args.length > 3 ? (0, values_1.toNumberRV)(args[3]) : (0, values_1.rvNumber)(0);
39
+ if ((0, values_1.isError)(fv)) {
40
+ return fv;
41
+ }
42
+ const type = args.length > 4 ? (0, values_1.toNumberRV)(args[4]) : (0, values_1.rvNumber)(0);
43
+ if ((0, values_1.isError)(type)) {
44
+ return type;
45
+ }
46
+ // Excel collapses any truthy `type` to 1 and any falsy to 0. Passing a
47
+ // non-binary number silently changed the PMT result; normalise up front
48
+ // so callers can't smuggle intermediate values in.
49
+ const typeBit = type.value ? 1 : 0;
50
+ // The simplified closed-form PMT divides by `nper` (rate=0 branch) or by
51
+ // `(1+rate)^nper - 1` (rate!=0 branch). Both collapse at nper=0, so we
52
+ // return #DIV/0! before attempting the math. The explicit guard matches
53
+ // Excel's behaviour on PMT(0, 0, …) and PMT(r, 0, …).
54
+ // Any `nper = 0` collapses the annuity equation (there are no periods
55
+ // over which to amortise). Regardless of `rate`, this is #DIV/0! —
56
+ // previously only the `rate = 0 && nper = 0` branch was guarded, so
57
+ // `PMT(0.05, 0, 1000)` produced NaN and silently serialised as null.
58
+ if (nper.value === 0) {
59
+ return values_1.ERRORS.DIV0;
60
+ }
61
+ if (rate.value === 0) {
62
+ return (0, values_1.rvNumber)(-(pv.value + fv.value) / nper.value);
63
+ }
64
+ const pvif = Math.pow(1 + rate.value, nper.value);
65
+ return (0, values_1.rvNumber)(-(rate.value * (pv.value * pvif + fv.value)) / (pvif - 1) / (1 + rate.value * typeBit));
66
+ };
67
+ exports.fnPMT = fnPMT;
68
+ const fnFV = args => {
69
+ const rate = (0, values_1.toNumberRV)(args[0]);
70
+ if ((0, values_1.isError)(rate)) {
71
+ return rate;
72
+ }
73
+ const nper = (0, values_1.toNumberRV)(args[1]);
74
+ if ((0, values_1.isError)(nper)) {
75
+ return nper;
76
+ }
77
+ const pmt = (0, values_1.toNumberRV)(args[2]);
78
+ if ((0, values_1.isError)(pmt)) {
79
+ return pmt;
80
+ }
81
+ const pv = args.length > 3 ? (0, values_1.toNumberRV)(args[3]) : (0, values_1.rvNumber)(0);
82
+ if ((0, values_1.isError)(pv)) {
83
+ return pv;
84
+ }
85
+ const type = args.length > 4 ? (0, values_1.toNumberRV)(args[4]) : (0, values_1.rvNumber)(0);
86
+ if ((0, values_1.isError)(type)) {
87
+ return type;
88
+ }
89
+ const typeBit = type.value ? 1 : 0;
90
+ // At rate=0 the formula reduces to `-(pv + pmt*nper)`; when both rate
91
+ // and nper are zero the underlying annuity equation is mathematically
92
+ // undefined (no periods over which to accrue), so Excel returns
93
+ // #DIV/0! — guard explicitly to keep the behaviour obvious.
94
+ if (nper.value === 0 && rate.value === 0) {
95
+ return values_1.ERRORS.DIV0;
96
+ }
97
+ if (rate.value === 0) {
98
+ return (0, values_1.rvNumber)(-(pv.value + pmt.value * nper.value));
99
+ }
100
+ const pvif = Math.pow(1 + rate.value, nper.value);
101
+ return (0, values_1.rvNumber)(-(pv.value * pvif + pmt.value * (1 + rate.value * typeBit) * ((pvif - 1) / rate.value)));
102
+ };
103
+ exports.fnFV = fnFV;
104
+ const fnPV = args => {
105
+ const rate = (0, values_1.toNumberRV)(args[0]);
106
+ if ((0, values_1.isError)(rate)) {
107
+ return rate;
108
+ }
109
+ const nper = (0, values_1.toNumberRV)(args[1]);
110
+ if ((0, values_1.isError)(nper)) {
111
+ return nper;
112
+ }
113
+ const pmt = (0, values_1.toNumberRV)(args[2]);
114
+ if ((0, values_1.isError)(pmt)) {
115
+ return pmt;
116
+ }
117
+ const fv = args.length > 3 ? (0, values_1.toNumberRV)(args[3]) : (0, values_1.rvNumber)(0);
118
+ if ((0, values_1.isError)(fv)) {
119
+ return fv;
120
+ }
121
+ const type = args.length > 4 ? (0, values_1.toNumberRV)(args[4]) : (0, values_1.rvNumber)(0);
122
+ if ((0, values_1.isError)(type)) {
123
+ return type;
124
+ }
125
+ const typeBit = type.value ? 1 : 0;
126
+ // Same boundary as FV/PMT: at rate=0 and nper=0 the annuity is
127
+ // mathematically undefined, so surface #DIV/0! instead of the
128
+ // coincidentally-finite `-fv` that the formula would otherwise yield.
129
+ if (nper.value === 0 && rate.value === 0) {
130
+ return values_1.ERRORS.DIV0;
131
+ }
132
+ if (rate.value === 0) {
133
+ return (0, values_1.rvNumber)(-pmt.value * nper.value - fv.value);
134
+ }
135
+ const pvif = Math.pow(1 + rate.value, nper.value);
136
+ return (0, values_1.rvNumber)(-(fv.value + pmt.value * (1 + rate.value * typeBit) * ((pvif - 1) / rate.value)) / pvif);
137
+ };
138
+ exports.fnPV = fnPV;
139
+ const fnNPV = args => {
140
+ const rate = (0, values_1.toNumberRV)(args[0]);
141
+ if ((0, values_1.isError)(rate)) {
142
+ return rate;
143
+ }
144
+ // NPV divides each cash flow by `(1 + rate)^i`. When `1 + rate == 0`
145
+ // (i.e. rate == -1) the discount factor is zero for every period and
146
+ // the series is undefined — Excel returns #DIV/0!. Guard explicitly so
147
+ // we don't fall through to Infinity/NaN in the pow-and-divide below.
148
+ if (1 + rate.value === 0) {
149
+ return values_1.ERRORS.DIV0;
150
+ }
151
+ const values = [];
152
+ for (let i = 1; i < args.length; i++) {
153
+ const a = args[i];
154
+ if ((0, values_1.isArray)(a)) {
155
+ for (const row of a.rows) {
156
+ for (const cell of row) {
157
+ if (cell.kind === 1 /* RVKind.Number */) {
158
+ values.push(cell.value);
159
+ }
160
+ }
161
+ }
162
+ }
163
+ else {
164
+ const n = (0, values_1.toNumberRV)(a);
165
+ if ((0, values_1.isError)(n)) {
166
+ return n;
167
+ }
168
+ values.push(n.value);
169
+ }
170
+ }
171
+ // Excel requires at least one cash flow; `NPV(rate)` with nothing to
172
+ // discount is a #VALUE! in the desktop app. (The previous code happily
173
+ // returned 0, which could mask buggy callers.)
174
+ if (values.length === 0) {
175
+ return values_1.ERRORS.VALUE;
176
+ }
177
+ let npv = 0;
178
+ for (let i = 0; i < values.length; i++) {
179
+ npv += values[i] / Math.pow(1 + rate.value, i + 1);
180
+ }
181
+ return isFinite(npv) ? (0, values_1.rvNumber)(npv) : values_1.ERRORS.NUM;
182
+ };
183
+ exports.fnNPV = fnNPV;
184
+ const fnIRR = args => {
185
+ if (!(0, values_1.isArray)(args[0])) {
186
+ return values_1.ERRORS.VALUE;
187
+ }
188
+ const values = [];
189
+ for (const row of args[0].rows) {
190
+ for (const cell of row) {
191
+ if (cell.kind === 1 /* RVKind.Number */) {
192
+ values.push(cell.value);
193
+ }
194
+ }
195
+ }
196
+ if (values.length < 2) {
197
+ return values_1.ERRORS.NUM;
198
+ }
199
+ // Excel requires at least one sign change in the cash-flow series — an
200
+ // IRR cannot exist for an all-positive or all-negative stream. Without
201
+ // this guard, Newton would drift toward an asymptote near g = -1 and
202
+ // might return a spurious "converged" value.
203
+ let hasPos = false;
204
+ let hasNeg = false;
205
+ for (const v of values) {
206
+ if (v > 0) {
207
+ hasPos = true;
208
+ }
209
+ if (v < 0) {
210
+ hasNeg = true;
211
+ }
212
+ }
213
+ if (!hasPos || !hasNeg) {
214
+ return values_1.ERRORS.NUM;
215
+ }
216
+ const guessRV = args.length > 1 ? (0, values_1.toNumberRV)(args[1]) : (0, values_1.rvNumber)(0.1);
217
+ if ((0, values_1.isError)(guessRV)) {
218
+ return guessRV;
219
+ }
220
+ const npvAt = (g) => {
221
+ let s = 0;
222
+ for (let i = 0; i < values.length; i++) {
223
+ s += values[i] / Math.pow(1 + g, i);
224
+ }
225
+ return s;
226
+ };
227
+ // Bracket a root by scanning a reasonable rate range once; then Newton
228
+ // refines within the bracket. This is more robust than pure Newton from
229
+ // a single starting point, which for multi-sign flows can wander to a
230
+ // different root or diverge.
231
+ let g = guessRV.value;
232
+ for (let iter = 0; iter < 100; iter++) {
233
+ let npv = 0;
234
+ let dnpv = 0;
235
+ for (let i = 0; i < values.length; i++) {
236
+ const p = Math.pow(1 + g, i);
237
+ if (!isFinite(p) || p === 0) {
238
+ break;
239
+ }
240
+ npv += values[i] / p;
241
+ dnpv -= (i * values[i]) / (p * (1 + g));
242
+ }
243
+ if (!isFinite(npv) || !isFinite(dnpv) || Math.abs(dnpv) < 1e-15) {
244
+ break;
245
+ }
246
+ const newGuess = g - npv / dnpv;
247
+ if (!isFinite(newGuess) || newGuess <= -1) {
248
+ break;
249
+ }
250
+ if (Math.abs(newGuess - g) < 1e-10) {
251
+ return (0, values_1.rvNumber)(newGuess);
252
+ }
253
+ g = newGuess;
254
+ }
255
+ // Newton failed to converge: fall back to a bracketing search over a
256
+ // wide range to at least find *some* root.
257
+ const sampleRates = [-0.99, -0.5, -0.1, 0, 0.1, 0.25, 0.5, 1, 2, 5, 10];
258
+ let prevG = sampleRates[0];
259
+ let prevV = npvAt(prevG);
260
+ for (let i = 1; i < sampleRates.length; i++) {
261
+ const currG = sampleRates[i];
262
+ const currV = npvAt(currG);
263
+ if (isFinite(prevV) && isFinite(currV) && prevV * currV < 0) {
264
+ // Bisection within [prevG, currG].
265
+ let lo = prevG;
266
+ let hi = currG;
267
+ let loV = prevV;
268
+ for (let b = 0; b < 100; b++) {
269
+ const mid = (lo + hi) / 2;
270
+ const midV = npvAt(mid);
271
+ if (!isFinite(midV)) {
272
+ break;
273
+ }
274
+ if (Math.abs(midV) < 1e-10 || hi - lo < 1e-12) {
275
+ return (0, values_1.rvNumber)(mid);
276
+ }
277
+ if (loV * midV < 0) {
278
+ hi = mid;
279
+ }
280
+ else {
281
+ lo = mid;
282
+ loV = midV;
283
+ }
284
+ }
285
+ return (0, values_1.rvNumber)((lo + hi) / 2);
286
+ }
287
+ prevG = currG;
288
+ prevV = currV;
289
+ }
290
+ return values_1.ERRORS.NUM;
291
+ };
292
+ exports.fnIRR = fnIRR;
293
+ const fnNPER = args => {
294
+ const rate = (0, values_1.toNumberRV)(args[0]);
295
+ if ((0, values_1.isError)(rate)) {
296
+ return rate;
297
+ }
298
+ const pmt = (0, values_1.toNumberRV)(args[1]);
299
+ if ((0, values_1.isError)(pmt)) {
300
+ return pmt;
301
+ }
302
+ const pv = (0, values_1.toNumberRV)(args[2]);
303
+ if ((0, values_1.isError)(pv)) {
304
+ return pv;
305
+ }
306
+ const fv = args.length > 3 ? (0, values_1.toNumberRV)(args[3]) : (0, values_1.rvNumber)(0);
307
+ if ((0, values_1.isError)(fv)) {
308
+ return fv;
309
+ }
310
+ const type = args.length > 4 ? (0, values_1.toNumberRV)(args[4]) : (0, values_1.rvNumber)(0);
311
+ if ((0, values_1.isError)(type)) {
312
+ return type;
313
+ }
314
+ // When rate=0 NPER reduces to `-(pv + fv) / pmt`; a zero payment on a
315
+ // zero-rate loan has no solution (no periods can make the balance
316
+ // amortise), so surface #DIV/0! rather than NaN.
317
+ if (rate.value === 0 && pmt.value === 0) {
318
+ return values_1.ERRORS.DIV0;
319
+ }
320
+ if (rate.value === 0) {
321
+ return (0, values_1.rvNumber)(-(pv.value + fv.value) / pmt.value);
322
+ }
323
+ const num = pmt.value * (1 + rate.value * type.value) - fv.value * rate.value;
324
+ const den = pv.value * rate.value + pmt.value * (1 + rate.value * type.value);
325
+ if (num / den <= 0) {
326
+ return values_1.ERRORS.NUM;
327
+ }
328
+ const result = Math.log(num / den) / Math.log(1 + rate.value);
329
+ // A negative NPER is mathematically valid (it represents "how many
330
+ // periods ago did this cashflow happen") but has no financial meaning
331
+ // in Excel — the documented convention is that NPER models a forward-
332
+ // looking loan/annuity. Reject negatives rather than returning them as
333
+ // silently-wrong period counts. (R8 quirk from worker audit)
334
+ if (!Number.isFinite(result) || result < 0) {
335
+ return values_1.ERRORS.NUM;
336
+ }
337
+ return (0, values_1.rvNumber)(result);
338
+ };
339
+ exports.fnNPER = fnNPER;
340
+ const fnRATE = args => {
341
+ const nper = (0, values_1.toNumberRV)(args[0]);
342
+ if ((0, values_1.isError)(nper)) {
343
+ return nper;
344
+ }
345
+ const pmt = (0, values_1.toNumberRV)(args[1]);
346
+ if ((0, values_1.isError)(pmt)) {
347
+ return pmt;
348
+ }
349
+ const pv = (0, values_1.toNumberRV)(args[2]);
350
+ if ((0, values_1.isError)(pv)) {
351
+ return pv;
352
+ }
353
+ const fv = args.length > 3 ? (0, values_1.toNumberRV)(args[3]) : (0, values_1.rvNumber)(0);
354
+ if ((0, values_1.isError)(fv)) {
355
+ return fv;
356
+ }
357
+ const type = args.length > 4 ? (0, values_1.toNumberRV)(args[4]) : (0, values_1.rvNumber)(0);
358
+ if ((0, values_1.isError)(type)) {
359
+ return type;
360
+ }
361
+ const guess = args.length > 5 ? (0, values_1.toNumberRV)(args[5]) : (0, values_1.rvNumber)(0.1);
362
+ if ((0, values_1.isError)(guess)) {
363
+ return guess;
364
+ }
365
+ let g = guess.value;
366
+ const nperV = nper.value;
367
+ const pmtV = pmt.value;
368
+ const pvV = pv.value;
369
+ const fvV = fv.value;
370
+ const typeV = type.value;
371
+ if (nperV <= 0) {
372
+ return values_1.ERRORS.NUM;
373
+ }
374
+ // Newton-Raphson. f(g) = PV·(1+g)^n + PMT·(1+g·type)·((1+g)^n − 1)/g + FV.
375
+ //
376
+ // The analytic derivative of the PMT term contains a 1/g² factor and a
377
+ // `(1+g)^n − 1` cancellation, both of which lose precision as g → 0. To
378
+ // handle that regime robustly we switch to a centred finite difference
379
+ // when |g| is small; the finite-difference derivative is exactly what the
380
+ // Newton iteration needs and stays well-conditioned down to g = 0 (we
381
+ // pick the step size ~sqrt(eps) to balance truncation vs rounding error).
382
+ const EPS = 1e-7;
383
+ const f = (x) => {
384
+ if (Math.abs(x) < 1e-12) {
385
+ // g = 0 limit: f(0) = PV + PMT·n + FV (the (1+g·type) factor → 1 and
386
+ // the ((1+g)^n − 1)/g factor → n).
387
+ return pvV + pmtV * nperV + fvV;
388
+ }
389
+ const pvifLocal = Math.pow(1 + x, nperV);
390
+ return pvV * pvifLocal + pmtV * (1 + x * typeV) * ((pvifLocal - 1) / x) + fvV;
391
+ };
392
+ for (let iter = 0; iter < 100; iter++) {
393
+ if (g <= -1) {
394
+ g = -0.99;
395
+ }
396
+ const fg = f(g);
397
+ let df;
398
+ if (Math.abs(g) < EPS) {
399
+ const h = 1e-5;
400
+ df = (f(g + h) - f(g - h)) / (2 * h);
401
+ }
402
+ else {
403
+ const pvif = Math.pow(1 + g, nperV);
404
+ const fvifa = (pvif - 1) / g;
405
+ df =
406
+ nperV * pvV * Math.pow(1 + g, nperV - 1) +
407
+ (pmtV * (1 + g * typeV) * (nperV * Math.pow(1 + g, nperV - 1) * g - pvif + 1)) / (g * g) +
408
+ (typeV ? pmtV * fvifa : 0);
409
+ }
410
+ if (!isFinite(df) || Math.abs(df) < 1e-15) {
411
+ break;
412
+ }
413
+ const newGuess = g - fg / df;
414
+ if (!isFinite(newGuess)) {
415
+ break;
416
+ }
417
+ if (Math.abs(newGuess - g) < 1e-10) {
418
+ return (0, values_1.rvNumber)(newGuess);
419
+ }
420
+ g = newGuess;
421
+ }
422
+ // Did not converge after 100 iterations
423
+ return values_1.ERRORS.NUM;
424
+ };
425
+ exports.fnRATE = fnRATE;
426
+ const fnSLN = args => {
427
+ const cost = (0, values_1.toNumberRV)(args[0]);
428
+ if ((0, values_1.isError)(cost)) {
429
+ return cost;
430
+ }
431
+ const salvage = (0, values_1.toNumberRV)(args[1]);
432
+ if ((0, values_1.isError)(salvage)) {
433
+ return salvage;
434
+ }
435
+ const life = (0, values_1.toNumberRV)(args[2]);
436
+ if ((0, values_1.isError)(life)) {
437
+ return life;
438
+ }
439
+ if (life.value === 0) {
440
+ return values_1.ERRORS.DIV0;
441
+ }
442
+ return (0, values_1.rvNumber)((cost.value - salvage.value) / life.value);
443
+ };
444
+ exports.fnSLN = fnSLN;
445
+ /**
446
+ * SYD — Sum-of-years'-digits depreciation.
447
+ *
448
+ * SYD(cost, salvage, life, per)
449
+ *
450
+ * The depreciation assigned to period `per` by the sum-of-years'-digits
451
+ * method: `(cost - salvage) * (life - per + 1) * 2 / (life * (life + 1))`.
452
+ * Excel rejects `life = 0` and period outside [1, life] with #NUM!.
453
+ */
454
+ const fnSYD = args => {
455
+ const cost = (0, values_1.toNumberRV)(args[0]);
456
+ if ((0, values_1.isError)(cost)) {
457
+ return cost;
458
+ }
459
+ const salvage = (0, values_1.toNumberRV)(args[1]);
460
+ if ((0, values_1.isError)(salvage)) {
461
+ return salvage;
462
+ }
463
+ const life = (0, values_1.toNumberRV)(args[2]);
464
+ if ((0, values_1.isError)(life)) {
465
+ return life;
466
+ }
467
+ const per = (0, values_1.toNumberRV)(args[3]);
468
+ if ((0, values_1.isError)(per)) {
469
+ return per;
470
+ }
471
+ if (life.value <= 0 || per.value < 1 || per.value > life.value) {
472
+ return values_1.ERRORS.NUM;
473
+ }
474
+ return (0, values_1.rvNumber)(((cost.value - salvage.value) * (life.value - per.value + 1) * 2) /
475
+ (life.value * (life.value + 1)));
476
+ };
477
+ exports.fnSYD = fnSYD;
478
+ /**
479
+ * VDB — Variable Declining Balance depreciation.
480
+ *
481
+ * VDB(cost, salvage, life, start_period, end_period, [factor], [no_switch])
482
+ *
483
+ * Applies declining-balance depreciation (with a default factor of 2
484
+ * for double-declining) between two fractional periods. By default
485
+ * (`no_switch = FALSE`) the method silently switches to straight-line
486
+ * when that yields a larger deduction, matching Excel's documented
487
+ * behaviour. `no_switch = TRUE` forces declining-balance for all
488
+ * periods.
489
+ */
490
+ const fnVDB = args => {
491
+ const cost = (0, values_1.toNumberRV)(args[0]);
492
+ if ((0, values_1.isError)(cost)) {
493
+ return cost;
494
+ }
495
+ const salvage = (0, values_1.toNumberRV)(args[1]);
496
+ if ((0, values_1.isError)(salvage)) {
497
+ return salvage;
498
+ }
499
+ const life = (0, values_1.toNumberRV)(args[2]);
500
+ if ((0, values_1.isError)(life)) {
501
+ return life;
502
+ }
503
+ const start = (0, values_1.toNumberRV)(args[3]);
504
+ if ((0, values_1.isError)(start)) {
505
+ return start;
506
+ }
507
+ const end = (0, values_1.toNumberRV)(args[4]);
508
+ if ((0, values_1.isError)(end)) {
509
+ return end;
510
+ }
511
+ const factorRV = args.length > 5 ? (0, values_1.toNumberRV)(args[5]) : (0, values_1.rvNumber)(2);
512
+ if ((0, values_1.isError)(factorRV)) {
513
+ return factorRV;
514
+ }
515
+ const noSwitchRV = args.length > 6 ? (0, values_1.toBooleanRV)(args[6]) : (0, values_1.rvBoolean)(false);
516
+ if ((0, values_1.isError)(noSwitchRV)) {
517
+ return noSwitchRV;
518
+ }
519
+ const c = cost.value;
520
+ const s = salvage.value;
521
+ const l = life.value;
522
+ const sp = start.value;
523
+ const ep = end.value;
524
+ const factor = factorRV.value;
525
+ const noSwitch = noSwitchRV.value;
526
+ if (c < 0 || s < 0 || l <= 0 || factor <= 0 || sp < 0 || ep <= sp || ep > l) {
527
+ return values_1.ERRORS.NUM;
528
+ }
529
+ // Walk the integer period boundaries that lie inside [sp, ep] and
530
+ // accumulate the declining-balance depreciation per whole period; clip
531
+ // the fractional period pieces at the two endpoints. Excel's DDB
532
+ // behaviour is already "clamp so book value never dips below salvage",
533
+ // so we replicate that here.
534
+ const periodDepn = (book, periodLen) => {
535
+ const decline = Math.min(book * (factor / l) * periodLen, book - s);
536
+ return decline < 0 ? 0 : decline;
537
+ };
538
+ let book = c;
539
+ let total = 0;
540
+ // Optional switch to straight-line: once DB deduction would be less
541
+ // than the equivalent SL deduction, Excel keeps running SL for the
542
+ // remaining whole periods. Handled by tracking `switched` below.
543
+ let switched = false;
544
+ let p = 0;
545
+ while (p < l) {
546
+ const lo = Math.max(sp, p);
547
+ const hi = Math.min(ep, p + 1);
548
+ if (hi > lo) {
549
+ const frac = hi - lo;
550
+ let dep;
551
+ if (!noSwitch && !switched) {
552
+ const dbDep = book * (factor / l) * frac;
553
+ const slDep = ((book - s) / (l - p)) * frac;
554
+ if (slDep > dbDep) {
555
+ switched = true;
556
+ dep = slDep;
557
+ }
558
+ else {
559
+ dep = periodDepn(book, frac);
560
+ }
561
+ }
562
+ else if (switched) {
563
+ dep = ((book - s) / Math.max(1e-10, l - p)) * frac;
564
+ }
565
+ else {
566
+ dep = periodDepn(book, frac);
567
+ }
568
+ total += dep;
569
+ book -= dep;
570
+ }
571
+ p++;
572
+ if (book <= s || p >= ep) {
573
+ break;
574
+ }
575
+ }
576
+ return (0, values_1.rvNumber)(total);
577
+ };
578
+ exports.fnVDB = fnVDB;
579
+ const fnDB = args => {
580
+ const cost = (0, values_1.toNumberRV)(args[0]);
581
+ if ((0, values_1.isError)(cost)) {
582
+ return cost;
583
+ }
584
+ const salvage = (0, values_1.toNumberRV)(args[1]);
585
+ if ((0, values_1.isError)(salvage)) {
586
+ return salvage;
587
+ }
588
+ const life = (0, values_1.toNumberRV)(args[2]);
589
+ if ((0, values_1.isError)(life)) {
590
+ return life;
591
+ }
592
+ const period = (0, values_1.toNumberRV)(args[3]);
593
+ if ((0, values_1.isError)(period)) {
594
+ return period;
595
+ }
596
+ const month = args.length > 4 ? (0, values_1.toNumberRV)(args[4]) : (0, values_1.rvNumber)(12);
597
+ if ((0, values_1.isError)(month)) {
598
+ return month;
599
+ }
600
+ // Excel validates: cost >= 0, salvage >= 0, life > 0, period > 0, 1 <= month <= 12.
601
+ // Also period must be <= life + 1 (the "stub" trailing month period).
602
+ if (cost.value < 0 ||
603
+ salvage.value < 0 ||
604
+ life.value <= 0 ||
605
+ period.value < 1 ||
606
+ month.value < 1 ||
607
+ month.value > 12 ||
608
+ period.value > life.value + 1) {
609
+ return values_1.ERRORS.NUM;
610
+ }
611
+ if (cost.value === 0) {
612
+ return (0, values_1.rvNumber)(0);
613
+ }
614
+ // Depreciation rate rounded to 3 decimal places per Excel's published formula.
615
+ const rate = salvage.value === 0
616
+ ? 1
617
+ : Math.round((1 - Math.pow(salvage.value / cost.value, 1 / life.value)) * 1000) / 1000;
618
+ // The "trailing stub" period is ceil(life) + 1 when the first year is partial
619
+ // (month < 12) and equals ceil(life) when month === 12.
620
+ const stubPeriod = month.value === 12 ? Math.ceil(life.value) : Math.ceil(life.value) + 1;
621
+ let totalDepreciation = 0;
622
+ let depn = 0;
623
+ const periods = Math.min(Math.floor(period.value), stubPeriod);
624
+ for (let p = 1; p <= periods; p++) {
625
+ if (p === 1) {
626
+ depn = (cost.value * rate * month.value) / 12;
627
+ }
628
+ else if (p === stubPeriod) {
629
+ depn = ((cost.value - totalDepreciation) * rate * (12 - month.value)) / 12;
630
+ }
631
+ else {
632
+ depn = (cost.value - totalDepreciation) * rate;
633
+ }
634
+ totalDepreciation += depn;
635
+ }
636
+ return (0, values_1.rvNumber)(depn);
637
+ };
638
+ exports.fnDB = fnDB;
639
+ const fnDDB = args => {
640
+ const cost = (0, values_1.toNumberRV)(args[0]);
641
+ if ((0, values_1.isError)(cost)) {
642
+ return cost;
643
+ }
644
+ const salvage = (0, values_1.toNumberRV)(args[1]);
645
+ if ((0, values_1.isError)(salvage)) {
646
+ return salvage;
647
+ }
648
+ const life = (0, values_1.toNumberRV)(args[2]);
649
+ if ((0, values_1.isError)(life)) {
650
+ return life;
651
+ }
652
+ const period = (0, values_1.toNumberRV)(args[3]);
653
+ if ((0, values_1.isError)(period)) {
654
+ return period;
655
+ }
656
+ const factor = args.length > 4 ? (0, values_1.toNumberRV)(args[4]) : (0, values_1.rvNumber)(2);
657
+ if ((0, values_1.isError)(factor)) {
658
+ return factor;
659
+ }
660
+ // Excel validates: cost, salvage >= 0; life, period, factor > 0; period <= life.
661
+ if (cost.value < 0 ||
662
+ salvage.value < 0 ||
663
+ life.value <= 0 ||
664
+ period.value < 1 ||
665
+ period.value > life.value ||
666
+ factor.value <= 0) {
667
+ return values_1.ERRORS.NUM;
668
+ }
669
+ let bookValue = cost.value;
670
+ let depn = 0;
671
+ const periods = Math.floor(period.value);
672
+ for (let p = 1; p <= periods; p++) {
673
+ depn = Math.min(bookValue * (factor.value / life.value), bookValue - salvage.value);
674
+ if (depn < 0) {
675
+ depn = 0;
676
+ }
677
+ bookValue -= depn;
678
+ }
679
+ return (0, values_1.rvNumber)(depn);
680
+ };
681
+ exports.fnDDB = fnDDB;
682
+ /** Internal PMT computation that returns a raw number (for IPMT/PPMT/CUMPRINC/CUMIPMT). */
683
+ function pmtRaw(rate, nper, pv, fv, type) {
684
+ if (rate === 0) {
685
+ return -(pv + fv) / nper;
686
+ }
687
+ const pvif = Math.pow(1 + rate, nper);
688
+ return -(rate * (pv * pvif + fv)) / (pvif - 1) / (1 + rate * type);
689
+ }
690
+ /**
691
+ * Internal IPMT computation that returns a raw number.
692
+ *
693
+ * Excel's IPMT reports the interest portion of a period as a cash flow
694
+ * *out* — i.e. negative when the loan principal is positive (you pay
695
+ * interest). The balance-accumulation loop above produces a positive
696
+ * running balance for a positive `pv`, so the `bal * rate` product
697
+ * would also be positive; we negate at return to match Excel's sign
698
+ * convention. The CUMIPMT / PPMT paths that consume this helper rely
699
+ * on the sign being correct so `IPMT + PPMT ≡ PMT` holds.
700
+ */
701
+ function ipmtRaw(rate, per, nper, pv, fv, type) {
702
+ const pmt = pmtRaw(rate, nper, pv, fv, type);
703
+ if (rate === 0) {
704
+ return 0;
705
+ }
706
+ // Compute FV of original loan at period (per-1)
707
+ let bal = pv;
708
+ for (let i = 1; i < per; i++) {
709
+ bal = bal * (1 + rate) + pmt * (1 + rate * type);
710
+ }
711
+ const ipmt = type === 1 && per === 1 ? 0 : -bal * rate;
712
+ return type === 1 ? ipmt / (1 + rate) : ipmt;
713
+ }
714
+ const fnIPMT = args => {
715
+ const rate = (0, values_1.toNumberRV)(args[0]);
716
+ if ((0, values_1.isError)(rate)) {
717
+ return rate;
718
+ }
719
+ const per = (0, values_1.toNumberRV)(args[1]);
720
+ if ((0, values_1.isError)(per)) {
721
+ return per;
722
+ }
723
+ const nper = (0, values_1.toNumberRV)(args[2]);
724
+ if ((0, values_1.isError)(nper)) {
725
+ return nper;
726
+ }
727
+ const pv = (0, values_1.toNumberRV)(args[3]);
728
+ if ((0, values_1.isError)(pv)) {
729
+ return pv;
730
+ }
731
+ const fv = args.length > 4 ? (0, values_1.toNumberRV)(args[4]) : (0, values_1.rvNumber)(0);
732
+ if ((0, values_1.isError)(fv)) {
733
+ return fv;
734
+ }
735
+ const type = args.length > 5 ? (0, values_1.toNumberRV)(args[5]) : (0, values_1.rvNumber)(0);
736
+ if ((0, values_1.isError)(type)) {
737
+ return type;
738
+ }
739
+ return (0, values_1.rvNumber)(ipmtRaw(rate.value, per.value, nper.value, pv.value, fv.value, type.value));
740
+ };
741
+ exports.fnIPMT = fnIPMT;
742
+ const fnPPMT = args => {
743
+ const rate = (0, values_1.toNumberRV)(args[0]);
744
+ if ((0, values_1.isError)(rate)) {
745
+ return rate;
746
+ }
747
+ const per = (0, values_1.toNumberRV)(args[1]);
748
+ if ((0, values_1.isError)(per)) {
749
+ return per;
750
+ }
751
+ const nper = (0, values_1.toNumberRV)(args[2]);
752
+ if ((0, values_1.isError)(nper)) {
753
+ return nper;
754
+ }
755
+ const pv = (0, values_1.toNumberRV)(args[3]);
756
+ if ((0, values_1.isError)(pv)) {
757
+ return pv;
758
+ }
759
+ const fv = args.length > 4 ? (0, values_1.toNumberRV)(args[4]) : (0, values_1.rvNumber)(0);
760
+ if ((0, values_1.isError)(fv)) {
761
+ return fv;
762
+ }
763
+ const type = args.length > 5 ? (0, values_1.toNumberRV)(args[5]) : (0, values_1.rvNumber)(0);
764
+ if ((0, values_1.isError)(type)) {
765
+ return type;
766
+ }
767
+ const pmtVal = pmtRaw(rate.value, nper.value, pv.value, fv.value, type.value);
768
+ const ipmtVal = ipmtRaw(rate.value, per.value, nper.value, pv.value, fv.value, type.value);
769
+ return (0, values_1.rvNumber)(pmtVal - ipmtVal);
770
+ };
771
+ exports.fnPPMT = fnPPMT;
772
+ /**
773
+ * FVSCHEDULE — future value with a schedule of varying rates.
774
+ *
775
+ * FVSCHEDULE(principal, schedule)
776
+ *
777
+ * Compounds `principal` through each rate in `schedule`:
778
+ * FV = principal · ∏(1 + rᵢ)
779
+ *
780
+ * Excel treats blanks in the schedule as zero (no-op compounding) and
781
+ * propagates any error it encounters. Text values produce #VALUE!.
782
+ */
783
+ const fnFVSCHEDULE = args => {
784
+ const principal = (0, values_1.toNumberRV)(args[0]);
785
+ if ((0, values_1.isError)(principal)) {
786
+ return principal;
787
+ }
788
+ const scheduleArg = args[1];
789
+ let fv = principal.value;
790
+ const visit = (v) => {
791
+ if (v.kind === 4 /* RVKind.Error */) {
792
+ return v;
793
+ }
794
+ if (v.kind === 0 /* RVKind.Blank */) {
795
+ return null;
796
+ } // treat blanks as 0%
797
+ if (v.kind === 1 /* RVKind.Number */) {
798
+ fv *= 1 + v.value;
799
+ return null;
800
+ }
801
+ if (v.kind === 3 /* RVKind.Boolean */) {
802
+ fv *= 1 + (v.value ? 1 : 0);
803
+ return null;
804
+ }
805
+ // Strings / other: Excel surfaces #VALUE!
806
+ return values_1.ERRORS.VALUE;
807
+ };
808
+ if (scheduleArg.kind === 5 /* RVKind.Array */) {
809
+ for (const row of scheduleArg.rows) {
810
+ for (const cell of row) {
811
+ const err = visit(cell);
812
+ if (err) {
813
+ return err;
814
+ }
815
+ }
816
+ }
817
+ }
818
+ else {
819
+ const err = visit(scheduleArg);
820
+ if (err) {
821
+ return err;
822
+ }
823
+ }
824
+ return (0, values_1.rvNumber)(fv);
825
+ };
826
+ exports.fnFVSCHEDULE = fnFVSCHEDULE;
827
+ /**
828
+ * PDURATION — number of periods required for an investment to reach a
829
+ * specified value.
830
+ *
831
+ * PDURATION(rate, pv, fv) = (log fv − log pv) / log(1 + rate)
832
+ *
833
+ * Excel requires `rate > 0` and `pv, fv > 0`.
834
+ */
835
+ const fnPDURATION = args => {
836
+ const rate = (0, values_1.toNumberRV)(args[0]);
837
+ if ((0, values_1.isError)(rate)) {
838
+ return rate;
839
+ }
840
+ const pv = (0, values_1.toNumberRV)(args[1]);
841
+ if ((0, values_1.isError)(pv)) {
842
+ return pv;
843
+ }
844
+ const fv = (0, values_1.toNumberRV)(args[2]);
845
+ if ((0, values_1.isError)(fv)) {
846
+ return fv;
847
+ }
848
+ if (rate.value <= 0 || pv.value <= 0 || fv.value <= 0) {
849
+ return values_1.ERRORS.NUM;
850
+ }
851
+ return (0, values_1.rvNumber)((Math.log(fv.value) - Math.log(pv.value)) / Math.log(1 + rate.value));
852
+ };
853
+ exports.fnPDURATION = fnPDURATION;
854
+ /**
855
+ * RRI — equivalent interest rate for the growth of an investment.
856
+ *
857
+ * RRI(nper, pv, fv) = (fv / pv)^(1/nper) − 1
858
+ *
859
+ * Excel requires `nper > 0`, `pv > 0`, `fv >= 0`.
860
+ */
861
+ const fnRRI = args => {
862
+ const nper = (0, values_1.toNumberRV)(args[0]);
863
+ if ((0, values_1.isError)(nper)) {
864
+ return nper;
865
+ }
866
+ const pv = (0, values_1.toNumberRV)(args[1]);
867
+ if ((0, values_1.isError)(pv)) {
868
+ return pv;
869
+ }
870
+ const fv = (0, values_1.toNumberRV)(args[2]);
871
+ if ((0, values_1.isError)(fv)) {
872
+ return fv;
873
+ }
874
+ if (nper.value <= 0 || pv.value <= 0 || fv.value < 0) {
875
+ return values_1.ERRORS.NUM;
876
+ }
877
+ return (0, values_1.rvNumber)(Math.pow(fv.value / pv.value, 1 / nper.value) - 1);
878
+ };
879
+ exports.fnRRI = fnRRI;
880
+ const fnEFFECT = args => {
881
+ const nomRate = (0, values_1.toNumberRV)(args[0]);
882
+ if ((0, values_1.isError)(nomRate)) {
883
+ return nomRate;
884
+ }
885
+ const npery = (0, values_1.toNumberRV)(args[1]);
886
+ if ((0, values_1.isError)(npery)) {
887
+ return npery;
888
+ }
889
+ if (nomRate.value <= 0 || npery.value < 1) {
890
+ return values_1.ERRORS.NUM;
891
+ }
892
+ return (0, values_1.rvNumber)(Math.pow(1 + nomRate.value / Math.floor(npery.value), Math.floor(npery.value)) - 1);
893
+ };
894
+ exports.fnEFFECT = fnEFFECT;
895
+ const fnNOMINAL = args => {
896
+ const effRate = (0, values_1.toNumberRV)(args[0]);
897
+ if ((0, values_1.isError)(effRate)) {
898
+ return effRate;
899
+ }
900
+ const npery = (0, values_1.toNumberRV)(args[1]);
901
+ if ((0, values_1.isError)(npery)) {
902
+ return npery;
903
+ }
904
+ if (effRate.value <= 0 || npery.value < 1) {
905
+ return values_1.ERRORS.NUM;
906
+ }
907
+ const np = Math.floor(npery.value);
908
+ return (0, values_1.rvNumber)(np * (Math.pow(effRate.value + 1, 1 / np) - 1));
909
+ };
910
+ exports.fnNOMINAL = fnNOMINAL;
911
+ const fnXNPV = args => {
912
+ const rate = (0, values_1.toNumberRV)(args[0]);
913
+ if ((0, values_1.isError)(rate)) {
914
+ return rate;
915
+ }
916
+ if (!(0, values_1.isArray)(args[1]) || !(0, values_1.isArray)(args[2])) {
917
+ return values_1.ERRORS.VALUE;
918
+ }
919
+ // Excel requires rate > -1. At rate = -1 the discount factor is
920
+ // singular; below -1 `Math.pow(negative, non-integer)` yields NaN
921
+ // for most date offsets.
922
+ if (rate.value <= -1) {
923
+ return values_1.ERRORS.NUM;
924
+ }
925
+ const rawValues = (0, _shared_1.flattenNumbers)([args[1]]);
926
+ const valuesErr = (0, _shared_1.firstError)(rawValues);
927
+ if (valuesErr) {
928
+ return valuesErr;
929
+ }
930
+ const rawDates = (0, _shared_1.flattenNumbers)([args[2]]);
931
+ const datesErr = (0, _shared_1.firstError)(rawDates);
932
+ if (datesErr) {
933
+ return datesErr;
934
+ }
935
+ const values = rawValues.map(n => n.value);
936
+ const dates = rawDates.map(n => n.value);
937
+ if (values.length === 0 || values.length !== dates.length) {
938
+ return values_1.ERRORS.NUM;
939
+ }
940
+ const d0 = dates[0];
941
+ let npv = 0;
942
+ for (let i = 0; i < values.length; i++) {
943
+ npv += values[i] / Math.pow(1 + rate.value, (dates[i] - d0) / 365);
944
+ }
945
+ return isFinite(npv) ? (0, values_1.rvNumber)(npv) : values_1.ERRORS.NUM;
946
+ };
947
+ exports.fnXNPV = fnXNPV;
948
+ const fnXIRR = args => {
949
+ if (!(0, values_1.isArray)(args[0]) || !(0, values_1.isArray)(args[1])) {
950
+ return values_1.ERRORS.VALUE;
951
+ }
952
+ const rawValues = (0, _shared_1.flattenNumbers)([args[0]]);
953
+ const valuesErr = (0, _shared_1.firstError)(rawValues);
954
+ if (valuesErr) {
955
+ return valuesErr;
956
+ }
957
+ const rawDates = (0, _shared_1.flattenNumbers)([args[1]]);
958
+ const datesErr = (0, _shared_1.firstError)(rawDates);
959
+ if (datesErr) {
960
+ return datesErr;
961
+ }
962
+ const values = rawValues.map(n => n.value);
963
+ const dates = rawDates.map(n => n.value);
964
+ if (values.length < 2 || values.length !== dates.length) {
965
+ return values_1.ERRORS.NUM;
966
+ }
967
+ // Excel's XIRR requires both at least one positive and at least one
968
+ // negative cash flow, same as IRR. Without this guard Newton would drift
969
+ // toward the singularity at rate = -1 with no valid root.
970
+ let xHasPos = false;
971
+ let xHasNeg = false;
972
+ for (const v of values) {
973
+ if (v > 0) {
974
+ xHasPos = true;
975
+ }
976
+ if (v < 0) {
977
+ xHasNeg = true;
978
+ }
979
+ }
980
+ if (!xHasPos || !xHasNeg) {
981
+ return values_1.ERRORS.NUM;
982
+ }
983
+ const guessRV = args.length > 2 ? (0, values_1.toNumberRV)(args[2]) : (0, values_1.rvNumber)(0.1);
984
+ if ((0, values_1.isError)(guessRV)) {
985
+ return guessRV;
986
+ }
987
+ const d0 = dates[0];
988
+ const xnpvAt = (g) => {
989
+ if (g <= -1) {
990
+ return Number.NaN;
991
+ }
992
+ let s = 0;
993
+ for (let i = 0; i < values.length; i++) {
994
+ const t = (dates[i] - d0) / 365;
995
+ const p = Math.pow(1 + g, t);
996
+ if (!isFinite(p)) {
997
+ return Number.NaN;
998
+ }
999
+ s += values[i] / p;
1000
+ }
1001
+ return s;
1002
+ };
1003
+ let g = guessRV.value;
1004
+ for (let iter = 0; iter < 100; iter++) {
1005
+ let npv = 0;
1006
+ let dnpv = 0;
1007
+ for (let i = 0; i < values.length; i++) {
1008
+ const t = (dates[i] - d0) / 365;
1009
+ npv += values[i] / Math.pow(1 + g, t);
1010
+ dnpv -= (t * values[i]) / Math.pow(1 + g, t + 1);
1011
+ }
1012
+ if (!isFinite(npv) || !isFinite(dnpv) || Math.abs(dnpv) < 1e-15) {
1013
+ break;
1014
+ }
1015
+ const newG = g - npv / dnpv;
1016
+ if (!isFinite(newG) || newG <= -1) {
1017
+ break;
1018
+ }
1019
+ if (Math.abs(newG - g) < 1e-10) {
1020
+ return (0, values_1.rvNumber)(newG);
1021
+ }
1022
+ g = newG;
1023
+ }
1024
+ // Newton failed — fall back to bisection.
1025
+ const xSampleRates = [-0.99, -0.5, -0.1, 0, 0.1, 0.25, 0.5, 1, 2, 5, 10];
1026
+ let xPrev = xSampleRates[0];
1027
+ let xPrevV = xnpvAt(xPrev);
1028
+ for (let i = 1; i < xSampleRates.length; i++) {
1029
+ const curr = xSampleRates[i];
1030
+ const currV = xnpvAt(curr);
1031
+ if (isFinite(xPrevV) && isFinite(currV) && xPrevV * currV < 0) {
1032
+ let lo = xPrev;
1033
+ let hi = curr;
1034
+ let loV = xPrevV;
1035
+ for (let b = 0; b < 100; b++) {
1036
+ const mid = (lo + hi) / 2;
1037
+ const midV = xnpvAt(mid);
1038
+ if (!isFinite(midV)) {
1039
+ break;
1040
+ }
1041
+ if (Math.abs(midV) < 1e-10 || hi - lo < 1e-12) {
1042
+ return (0, values_1.rvNumber)(mid);
1043
+ }
1044
+ if (loV * midV < 0) {
1045
+ hi = mid;
1046
+ }
1047
+ else {
1048
+ lo = mid;
1049
+ loV = midV;
1050
+ }
1051
+ }
1052
+ return (0, values_1.rvNumber)((lo + hi) / 2);
1053
+ }
1054
+ xPrev = curr;
1055
+ xPrevV = currV;
1056
+ }
1057
+ return values_1.ERRORS.NUM;
1058
+ };
1059
+ exports.fnXIRR = fnXIRR;
1060
+ const fnMIRR = args => {
1061
+ if (!(0, values_1.isArray)(args[0])) {
1062
+ return values_1.ERRORS.VALUE;
1063
+ }
1064
+ const rawValues = (0, _shared_1.flattenNumbers)([args[0]]);
1065
+ const valuesErr = (0, _shared_1.firstError)(rawValues);
1066
+ if (valuesErr) {
1067
+ return valuesErr;
1068
+ }
1069
+ const values = rawValues.map(n => n.value);
1070
+ const financeRate = (0, values_1.toNumberRV)(args[1]);
1071
+ if ((0, values_1.isError)(financeRate)) {
1072
+ return financeRate;
1073
+ }
1074
+ const reinvestRate = (0, values_1.toNumberRV)(args[2]);
1075
+ if ((0, values_1.isError)(reinvestRate)) {
1076
+ return reinvestRate;
1077
+ }
1078
+ const n = values.length;
1079
+ if (n < 2) {
1080
+ return values_1.ERRORS.NUM;
1081
+ }
1082
+ // Guard the singularities at rate = -1. Without this the pow()s below
1083
+ // become division by zero and produce Infinity/NaN.
1084
+ if (financeRate.value === -1 || reinvestRate.value === -1) {
1085
+ return values_1.ERRORS.DIV0;
1086
+ }
1087
+ let npvPos = 0;
1088
+ let npvNeg = 0;
1089
+ for (let i = 0; i < n; i++) {
1090
+ if (values[i] >= 0) {
1091
+ npvPos += values[i] * Math.pow(1 + reinvestRate.value, n - 1 - i);
1092
+ }
1093
+ else {
1094
+ npvNeg += values[i] / Math.pow(1 + financeRate.value, i);
1095
+ }
1096
+ }
1097
+ if (npvNeg === 0) {
1098
+ return values_1.ERRORS.DIV0;
1099
+ }
1100
+ return (0, values_1.rvNumber)(Math.pow(-npvPos / npvNeg, 1 / (n - 1)) - 1);
1101
+ };
1102
+ exports.fnMIRR = fnMIRR;
1103
+ const fnISPMT = args => {
1104
+ const rate = (0, values_1.toNumberRV)(args[0]);
1105
+ if ((0, values_1.isError)(rate)) {
1106
+ return rate;
1107
+ }
1108
+ const per = (0, values_1.toNumberRV)(args[1]);
1109
+ if ((0, values_1.isError)(per)) {
1110
+ return per;
1111
+ }
1112
+ const nper = (0, values_1.toNumberRV)(args[2]);
1113
+ if ((0, values_1.isError)(nper)) {
1114
+ return nper;
1115
+ }
1116
+ const pv = (0, values_1.toNumberRV)(args[3]);
1117
+ if ((0, values_1.isError)(pv)) {
1118
+ return pv;
1119
+ }
1120
+ // ISPMT's straight-line formula divides by `nper`; zero periods leave
1121
+ // the interest undefined, matching Excel's #DIV/0! on ISPMT(…, 0, …).
1122
+ if (nper.value === 0) {
1123
+ return values_1.ERRORS.DIV0;
1124
+ }
1125
+ return (0, values_1.rvNumber)(pv.value * rate.value * (per.value / nper.value - 1));
1126
+ };
1127
+ exports.fnISPMT = fnISPMT;
1128
+ const fnCUMPRINC = args => {
1129
+ const rate = (0, values_1.toNumberRV)(args[0]);
1130
+ if ((0, values_1.isError)(rate)) {
1131
+ return rate;
1132
+ }
1133
+ const nper = (0, values_1.toNumberRV)(args[1]);
1134
+ if ((0, values_1.isError)(nper)) {
1135
+ return nper;
1136
+ }
1137
+ const pv = (0, values_1.toNumberRV)(args[2]);
1138
+ if ((0, values_1.isError)(pv)) {
1139
+ return pv;
1140
+ }
1141
+ const startPeriod = (0, values_1.toNumberRV)(args[3]);
1142
+ if ((0, values_1.isError)(startPeriod)) {
1143
+ return startPeriod;
1144
+ }
1145
+ const endPeriod = (0, values_1.toNumberRV)(args[4]);
1146
+ if ((0, values_1.isError)(endPeriod)) {
1147
+ return endPeriod;
1148
+ }
1149
+ const type = (0, values_1.toNumberRV)(args[5]);
1150
+ if ((0, values_1.isError)(type)) {
1151
+ return type;
1152
+ }
1153
+ if (rate.value <= 0 || nper.value <= 0 || pv.value <= 0) {
1154
+ return values_1.ERRORS.NUM;
1155
+ }
1156
+ // Excel requires 1 ≤ start ≤ end ≤ nper and type ∈ {0, 1}.
1157
+ const s = Math.floor(startPeriod.value);
1158
+ const e = Math.floor(endPeriod.value);
1159
+ const n = Math.floor(nper.value);
1160
+ if (s < 1 || e < s || e > n) {
1161
+ return values_1.ERRORS.NUM;
1162
+ }
1163
+ if (type.value !== 0 && type.value !== 1) {
1164
+ return values_1.ERRORS.NUM;
1165
+ }
1166
+ let cumPrinc = 0;
1167
+ for (let p = s; p <= e; p++) {
1168
+ const pmtVal = pmtRaw(rate.value, nper.value, pv.value, 0, type.value);
1169
+ const ipmtVal = ipmtRaw(rate.value, p, nper.value, pv.value, 0, type.value);
1170
+ cumPrinc += pmtVal - ipmtVal;
1171
+ }
1172
+ return (0, values_1.rvNumber)(cumPrinc);
1173
+ };
1174
+ exports.fnCUMPRINC = fnCUMPRINC;
1175
+ const fnCUMIPMT = args => {
1176
+ const rate = (0, values_1.toNumberRV)(args[0]);
1177
+ if ((0, values_1.isError)(rate)) {
1178
+ return rate;
1179
+ }
1180
+ const nper = (0, values_1.toNumberRV)(args[1]);
1181
+ if ((0, values_1.isError)(nper)) {
1182
+ return nper;
1183
+ }
1184
+ const pv = (0, values_1.toNumberRV)(args[2]);
1185
+ if ((0, values_1.isError)(pv)) {
1186
+ return pv;
1187
+ }
1188
+ const startPeriod = (0, values_1.toNumberRV)(args[3]);
1189
+ if ((0, values_1.isError)(startPeriod)) {
1190
+ return startPeriod;
1191
+ }
1192
+ const endPeriod = (0, values_1.toNumberRV)(args[4]);
1193
+ if ((0, values_1.isError)(endPeriod)) {
1194
+ return endPeriod;
1195
+ }
1196
+ const type = (0, values_1.toNumberRV)(args[5]);
1197
+ if ((0, values_1.isError)(type)) {
1198
+ return type;
1199
+ }
1200
+ if (rate.value <= 0 || nper.value <= 0 || pv.value <= 0) {
1201
+ return values_1.ERRORS.NUM;
1202
+ }
1203
+ const sI = Math.floor(startPeriod.value);
1204
+ const eI = Math.floor(endPeriod.value);
1205
+ const nI = Math.floor(nper.value);
1206
+ if (sI < 1 || eI < sI || eI > nI) {
1207
+ return values_1.ERRORS.NUM;
1208
+ }
1209
+ if (type.value !== 0 && type.value !== 1) {
1210
+ return values_1.ERRORS.NUM;
1211
+ }
1212
+ let cumIpmt = 0;
1213
+ for (let p = sI; p <= eI; p++) {
1214
+ cumIpmt += ipmtRaw(rate.value, p, nper.value, pv.value, 0, type.value);
1215
+ }
1216
+ return (0, values_1.rvNumber)(cumIpmt);
1217
+ };
1218
+ exports.fnCUMIPMT = fnCUMIPMT;
1219
+ const fnDOLLARDE = args => {
1220
+ const fractionalDollar = (0, values_1.toNumberRV)(args[0]);
1221
+ if ((0, values_1.isError)(fractionalDollar)) {
1222
+ return fractionalDollar;
1223
+ }
1224
+ const fraction = (0, values_1.toNumberRV)(args[1]);
1225
+ if ((0, values_1.isError)(fraction)) {
1226
+ return fraction;
1227
+ }
1228
+ if (fraction.value < 1) {
1229
+ return values_1.ERRORS.NUM;
1230
+ }
1231
+ const f = Math.floor(fraction.value);
1232
+ const intPart = Math.trunc(fractionalDollar.value);
1233
+ const fracPart = Math.abs(fractionalDollar.value) - Math.abs(intPart);
1234
+ // The fractional portion of a "fractional dollar" encodes a numerator
1235
+ // with as many digits as the denominator. For fraction = 16, `0.02`
1236
+ // means 2/16 (= 0.125), not 0.02/16 (= 0.00125). Multiplying by
1237
+ // `10^ceil(log10(f))` promotes `0.02` → 2 so the subsequent divide by
1238
+ // `f` recovers the correct rational value. The old code divided
1239
+ // *before* the scale-up, which silently shrank the result by the
1240
+ // power of ten.
1241
+ const scale = f === 1 ? 1 : Math.pow(10, Math.ceil(Math.log10(f)));
1242
+ const numerator = fracPart * scale;
1243
+ const sign = fractionalDollar.value < 0 ? -1 : 1;
1244
+ return (0, values_1.rvNumber)(sign * (Math.abs(intPart) + numerator / f));
1245
+ };
1246
+ exports.fnDOLLARDE = fnDOLLARDE;
1247
+ const fnDOLLARFR = args => {
1248
+ const decimalDollar = (0, values_1.toNumberRV)(args[0]);
1249
+ if ((0, values_1.isError)(decimalDollar)) {
1250
+ return decimalDollar;
1251
+ }
1252
+ const fraction = (0, values_1.toNumberRV)(args[1]);
1253
+ if ((0, values_1.isError)(fraction)) {
1254
+ return fraction;
1255
+ }
1256
+ if (fraction.value < 1) {
1257
+ return values_1.ERRORS.NUM;
1258
+ }
1259
+ const f = Math.floor(fraction.value);
1260
+ const intPart = Math.trunc(decimalDollar.value);
1261
+ const fracPart = Math.abs(decimalDollar.value) - Math.abs(intPart);
1262
+ // DOLLARFR is the inverse of DOLLARDE. The fractional output slot must
1263
+ // encode a numerator in a fixed-width decimal column so that, say,
1264
+ // `2/16 = 0.125` round-trips to `0.02` (two digits after the point
1265
+ // because 16 needs two decimal digits to represent numerators up to
1266
+ // 15). The previous code divided by the scale *after* multiplying,
1267
+ // which moved the numerator into the wrong decimal column and made
1268
+ // DOLLARFR(1.125, 16) return 1.2 instead of 1.02.
1269
+ const scale = f === 1 ? 1 : Math.pow(10, Math.ceil(Math.log10(f)));
1270
+ const numerator = fracPart * f;
1271
+ const sign = decimalDollar.value < 0 ? -1 : 1;
1272
+ return (0, values_1.rvNumber)(sign * (Math.abs(intPart) + numerator / scale));
1273
+ };
1274
+ exports.fnDOLLARFR = fnDOLLARFR;
1275
+ /**
1276
+ * Common sanity check for the `basis` argument shared across the
1277
+ * discount/rate family of bond helpers. Excel rejects anything outside
1278
+ * `{0, 1, 2, 3, 4}` with `#NUM!`; previously these functions silently
1279
+ * fell back to basis 0 which produced subtly wrong year-fractions.
1280
+ */
1281
+ function validateBasis(basis) {
1282
+ const b = Math.floor(basis);
1283
+ if (b < 0 || b > 4) {
1284
+ return values_1.ERRORS.NUM;
1285
+ }
1286
+ return null;
1287
+ }
1288
+ const fnDISC = args => {
1289
+ const settlement = (0, values_1.toNumberRV)(args[0]);
1290
+ if ((0, values_1.isError)(settlement)) {
1291
+ return settlement;
1292
+ }
1293
+ const maturity = (0, values_1.toNumberRV)(args[1]);
1294
+ if ((0, values_1.isError)(maturity)) {
1295
+ return maturity;
1296
+ }
1297
+ const pr = (0, values_1.toNumberRV)(args[2]);
1298
+ if ((0, values_1.isError)(pr)) {
1299
+ return pr;
1300
+ }
1301
+ const redemption = (0, values_1.toNumberRV)(args[3]);
1302
+ if ((0, values_1.isError)(redemption)) {
1303
+ return redemption;
1304
+ }
1305
+ const basis = args.length > 4 ? (0, values_1.toNumberRV)(args[4]) : (0, values_1.rvNumber)(0);
1306
+ if ((0, values_1.isError)(basis)) {
1307
+ return basis;
1308
+ }
1309
+ const basisErr = validateBasis(basis.value);
1310
+ if (basisErr) {
1311
+ return basisErr;
1312
+ }
1313
+ if (maturity.value <= settlement.value || redemption.value <= 0 || pr.value <= 0) {
1314
+ return values_1.ERRORS.NUM;
1315
+ }
1316
+ // Use the same day-count fraction engine the other bond functions rely
1317
+ // on so DISC with basis 0 (30/360) and basis 4 (European 30/360)
1318
+ // actually differ instead of both collapsing to Actual/360.
1319
+ const dcf = dayCountFraction(settlement.value, maturity.value, Math.floor(basis.value));
1320
+ if (dcf <= 0) {
1321
+ return values_1.ERRORS.NUM;
1322
+ }
1323
+ return (0, values_1.rvNumber)((redemption.value - pr.value) / redemption.value / dcf);
1324
+ };
1325
+ exports.fnDISC = fnDISC;
1326
+ const fnPRICEDISC = args => {
1327
+ const settlement = (0, values_1.toNumberRV)(args[0]);
1328
+ if ((0, values_1.isError)(settlement)) {
1329
+ return settlement;
1330
+ }
1331
+ const maturity = (0, values_1.toNumberRV)(args[1]);
1332
+ if ((0, values_1.isError)(maturity)) {
1333
+ return maturity;
1334
+ }
1335
+ const disc = (0, values_1.toNumberRV)(args[2]);
1336
+ if ((0, values_1.isError)(disc)) {
1337
+ return disc;
1338
+ }
1339
+ const redemption = (0, values_1.toNumberRV)(args[3]);
1340
+ if ((0, values_1.isError)(redemption)) {
1341
+ return redemption;
1342
+ }
1343
+ const basis = args.length > 4 ? (0, values_1.toNumberRV)(args[4]) : (0, values_1.rvNumber)(0);
1344
+ if ((0, values_1.isError)(basis)) {
1345
+ return basis;
1346
+ }
1347
+ const basisErr = validateBasis(basis.value);
1348
+ if (basisErr) {
1349
+ return basisErr;
1350
+ }
1351
+ if (maturity.value <= settlement.value || disc.value <= 0 || redemption.value <= 0) {
1352
+ return values_1.ERRORS.NUM;
1353
+ }
1354
+ const dcf = dayCountFraction(settlement.value, maturity.value, Math.floor(basis.value));
1355
+ return (0, values_1.rvNumber)(redemption.value - disc.value * redemption.value * dcf);
1356
+ };
1357
+ exports.fnPRICEDISC = fnPRICEDISC;
1358
+ const fnYIELDDISC = args => {
1359
+ const settlement = (0, values_1.toNumberRV)(args[0]);
1360
+ if ((0, values_1.isError)(settlement)) {
1361
+ return settlement;
1362
+ }
1363
+ const maturity = (0, values_1.toNumberRV)(args[1]);
1364
+ if ((0, values_1.isError)(maturity)) {
1365
+ return maturity;
1366
+ }
1367
+ const pr = (0, values_1.toNumberRV)(args[2]);
1368
+ if ((0, values_1.isError)(pr)) {
1369
+ return pr;
1370
+ }
1371
+ const redemption = (0, values_1.toNumberRV)(args[3]);
1372
+ if ((0, values_1.isError)(redemption)) {
1373
+ return redemption;
1374
+ }
1375
+ const basis = args.length > 4 ? (0, values_1.toNumberRV)(args[4]) : (0, values_1.rvNumber)(0);
1376
+ if ((0, values_1.isError)(basis)) {
1377
+ return basis;
1378
+ }
1379
+ const basisErr = validateBasis(basis.value);
1380
+ if (basisErr) {
1381
+ return basisErr;
1382
+ }
1383
+ if (maturity.value <= settlement.value || pr.value <= 0 || redemption.value <= 0) {
1384
+ return values_1.ERRORS.NUM;
1385
+ }
1386
+ const dcf = dayCountFraction(settlement.value, maturity.value, Math.floor(basis.value));
1387
+ if (dcf <= 0) {
1388
+ return values_1.ERRORS.NUM;
1389
+ }
1390
+ return (0, values_1.rvNumber)((redemption.value - pr.value) / pr.value / dcf);
1391
+ };
1392
+ exports.fnYIELDDISC = fnYIELDDISC;
1393
+ const fnRECEIVED = args => {
1394
+ const settlement = (0, values_1.toNumberRV)(args[0]);
1395
+ if ((0, values_1.isError)(settlement)) {
1396
+ return settlement;
1397
+ }
1398
+ const maturity = (0, values_1.toNumberRV)(args[1]);
1399
+ if ((0, values_1.isError)(maturity)) {
1400
+ return maturity;
1401
+ }
1402
+ const investment = (0, values_1.toNumberRV)(args[2]);
1403
+ if ((0, values_1.isError)(investment)) {
1404
+ return investment;
1405
+ }
1406
+ const disc = (0, values_1.toNumberRV)(args[3]);
1407
+ if ((0, values_1.isError)(disc)) {
1408
+ return disc;
1409
+ }
1410
+ const basis = args.length > 4 ? (0, values_1.toNumberRV)(args[4]) : (0, values_1.rvNumber)(0);
1411
+ if ((0, values_1.isError)(basis)) {
1412
+ return basis;
1413
+ }
1414
+ const basisErr = validateBasis(basis.value);
1415
+ if (basisErr) {
1416
+ return basisErr;
1417
+ }
1418
+ if (maturity.value <= settlement.value || investment.value <= 0 || disc.value <= 0) {
1419
+ return values_1.ERRORS.NUM;
1420
+ }
1421
+ const dcf = dayCountFraction(settlement.value, maturity.value, Math.floor(basis.value));
1422
+ const denom = 1 - disc.value * dcf;
1423
+ if (denom === 0) {
1424
+ return values_1.ERRORS.NUM;
1425
+ }
1426
+ return (0, values_1.rvNumber)(investment.value / denom);
1427
+ };
1428
+ exports.fnRECEIVED = fnRECEIVED;
1429
+ const fnINTRATE = args => {
1430
+ const settlement = (0, values_1.toNumberRV)(args[0]);
1431
+ if ((0, values_1.isError)(settlement)) {
1432
+ return settlement;
1433
+ }
1434
+ const maturity = (0, values_1.toNumberRV)(args[1]);
1435
+ if ((0, values_1.isError)(maturity)) {
1436
+ return maturity;
1437
+ }
1438
+ const investment = (0, values_1.toNumberRV)(args[2]);
1439
+ if ((0, values_1.isError)(investment)) {
1440
+ return investment;
1441
+ }
1442
+ const redemption = (0, values_1.toNumberRV)(args[3]);
1443
+ if ((0, values_1.isError)(redemption)) {
1444
+ return redemption;
1445
+ }
1446
+ const basis = args.length > 4 ? (0, values_1.toNumberRV)(args[4]) : (0, values_1.rvNumber)(0);
1447
+ if ((0, values_1.isError)(basis)) {
1448
+ return basis;
1449
+ }
1450
+ const basisErr = validateBasis(basis.value);
1451
+ if (basisErr) {
1452
+ return basisErr;
1453
+ }
1454
+ if (maturity.value <= settlement.value || investment.value <= 0 || redemption.value <= 0) {
1455
+ return values_1.ERRORS.NUM;
1456
+ }
1457
+ const dcf = dayCountFraction(settlement.value, maturity.value, Math.floor(basis.value));
1458
+ if (dcf <= 0) {
1459
+ return values_1.ERRORS.NUM;
1460
+ }
1461
+ return (0, values_1.rvNumber)((redemption.value - investment.value) / investment.value / dcf);
1462
+ };
1463
+ exports.fnINTRATE = fnINTRATE;
1464
+ // ============================================================================
1465
+ // Bond Math — day-count conventions and coupon helpers
1466
+ // ============================================================================
1467
+ /**
1468
+ * Day-count fraction between two Excel date serials under a given basis.
1469
+ *
1470
+ * Implements:
1471
+ * basis 0 — US (NASD) 30/360 with end-of-month adjustments.
1472
+ * basis 1 — Actual/Actual (year length from the spanning year).
1473
+ * basis 2 — Actual/360.
1474
+ * basis 3 — Actual/365.
1475
+ * basis 4 — European 30/360.
1476
+ *
1477
+ * Unknown basis values fall back to basis 0 for safety.
1478
+ */
1479
+ function yearBasisDays(basis) {
1480
+ // Approximate denominator used when converting a day-count fraction
1481
+ // back to raw days (for PRICE's A / E accrual ratio). The Actual/
1482
+ // Actual bucket uses 365.25 as an average-year constant; since PRICE
1483
+ // only needs the ratio A/E the small inaccuracy cancels out.
1484
+ switch (basis) {
1485
+ case 0:
1486
+ case 2:
1487
+ case 4:
1488
+ return 360;
1489
+ case 3:
1490
+ return 365;
1491
+ case 1:
1492
+ default:
1493
+ return 365.25;
1494
+ }
1495
+ }
1496
+ function dayCountFraction(startSerial, endSerial, basis) {
1497
+ const startD = toDate(startSerial);
1498
+ const endD = toDate(endSerial);
1499
+ const diffDays = Math.floor(endSerial) - Math.floor(startSerial);
1500
+ switch (basis) {
1501
+ case 1: {
1502
+ // Actual/Actual (ISDA convention). See YEARFRAC in date.ts for the
1503
+ // rationale — the previous simple averaging produced visibly wrong
1504
+ // results like YEARFRAC(2020-01-01, 2021-01-01) ≈ 1.001 instead of 1.
1505
+ const y1 = startD.getUTCFullYear();
1506
+ const y2 = endD.getUTCFullYear();
1507
+ if (y1 === y2) {
1508
+ const yearDays = (Date.UTC(y1 + 1, 0, 1) - Date.UTC(y1, 0, 1)) / 86400000;
1509
+ return diffDays / yearDays;
1510
+ }
1511
+ let leapDays = 0;
1512
+ let nonLeapDays = 0;
1513
+ const sdMs = startD.getTime();
1514
+ const edMs = endD.getTime();
1515
+ for (let y = y1; y <= y2; y++) {
1516
+ const yStart = Math.max(sdMs, Date.UTC(y, 0, 1));
1517
+ const yEnd = Math.min(edMs, Date.UTC(y + 1, 0, 1));
1518
+ if (yEnd <= yStart) {
1519
+ continue;
1520
+ }
1521
+ const d = (yEnd - yStart) / 86400000;
1522
+ const isLeap = (y % 4 === 0 && y % 100 !== 0) || y % 400 === 0;
1523
+ if (isLeap) {
1524
+ leapDays += d;
1525
+ }
1526
+ else {
1527
+ nonLeapDays += d;
1528
+ }
1529
+ }
1530
+ return leapDays / 366 + nonLeapDays / 365;
1531
+ }
1532
+ case 2:
1533
+ return diffDays / 360;
1534
+ case 3:
1535
+ return diffDays / 365;
1536
+ case 4: {
1537
+ const d1 = Math.min(startD.getUTCDate(), 30);
1538
+ const d2 = Math.min(endD.getUTCDate(), 30);
1539
+ const m1 = startD.getUTCMonth() + 1;
1540
+ const m2 = endD.getUTCMonth() + 1;
1541
+ const y1 = startD.getUTCFullYear();
1542
+ const y2 = endD.getUTCFullYear();
1543
+ return ((y2 - y1) * 360 + (m2 - m1) * 30 + (d2 - d1)) / 360;
1544
+ }
1545
+ case 0:
1546
+ default: {
1547
+ let d1 = startD.getUTCDate();
1548
+ const m1 = startD.getUTCMonth() + 1;
1549
+ const y1 = startD.getUTCFullYear();
1550
+ let d2 = endD.getUTCDate();
1551
+ const m2 = endD.getUTCMonth() + 1;
1552
+ const y2 = endD.getUTCFullYear();
1553
+ if (d1 === 31) {
1554
+ d1 = 30;
1555
+ }
1556
+ if (d2 === 31 && d1 >= 30) {
1557
+ d2 = 30;
1558
+ }
1559
+ return ((y2 - y1) * 360 + (m2 - m1) * 30 + (d2 - d1)) / 360;
1560
+ }
1561
+ }
1562
+ }
1563
+ /**
1564
+ * Subtract (or add) `months` from an Excel date serial, clamping to month-end
1565
+ * as needed.
1566
+ *
1567
+ * The round-trip is performed entirely on the UTC timeline: we read UTC
1568
+ * fields from the source date, construct the target date with `Date.UTC`,
1569
+ * and compute the serial as the whole-day difference between the target and
1570
+ * the Excel epoch at UTC midnight (1899-12-30 when date1904 is false,
1571
+ * 1904-01-01 otherwise). Doing this in local time would produce off-by-one
1572
+ * errors in any timezone offset from UTC.
1573
+ */
1574
+ function addMonthsToSerial(serial, months) {
1575
+ const d = toDate(serial);
1576
+ const y = d.getUTCFullYear();
1577
+ const m = d.getUTCMonth();
1578
+ const day = d.getUTCDate();
1579
+ // Determine the calendar day-of-month, clamping to the last day of the
1580
+ // target month so e.g. Jan-31 + 1 month → Feb-28/29 (not Mar-3).
1581
+ const targetYear = y + Math.floor((m + months) / 12);
1582
+ const targetMonth = (((m + months) % 12) + 12) % 12;
1583
+ const lastDay = new Date(Date.UTC(targetYear, targetMonth + 1, 0)).getUTCDate();
1584
+ const targetMs = Date.UTC(targetYear, targetMonth, Math.min(day, lastDay));
1585
+ // Excel serial 0 = 1899-12-30 (1900 epoch) or 1904-01-01 when date1904.
1586
+ const epochMs = (0, _date_context_1.isDate1904)() ? Date.UTC(1904, 0, 1) : Date.UTC(1899, 11, 30);
1587
+ return Math.round((targetMs - epochMs) / 86400000);
1588
+ }
1589
+ /**
1590
+ * Find the next coupon date on or after settlement, given a maturity date
1591
+ * and coupon frequency (1=annual, 2=semi-annual, 4=quarterly).
1592
+ *
1593
+ * Computed by stepping backward from maturity by (12/frequency) months
1594
+ * until the resulting date is ≤ settlement, then stepping one coupon forward.
1595
+ */
1596
+ function nextCouponAfter(settlement, maturity, frequency) {
1597
+ const stepMonths = Math.round(12 / frequency);
1598
+ let cur = maturity;
1599
+ // Step backward until just before or at settlement.
1600
+ while (cur > settlement) {
1601
+ const prev = addMonthsToSerial(cur, -stepMonths);
1602
+ if (prev <= settlement) {
1603
+ return cur;
1604
+ }
1605
+ cur = prev;
1606
+ }
1607
+ return cur;
1608
+ }
1609
+ /**
1610
+ * Find the previous coupon date (on or before settlement).
1611
+ */
1612
+ function prevCouponOnOrBefore(settlement, maturity, frequency) {
1613
+ const stepMonths = Math.round(12 / frequency);
1614
+ let cur = maturity;
1615
+ while (cur > settlement) {
1616
+ cur = addMonthsToSerial(cur, -stepMonths);
1617
+ }
1618
+ return cur;
1619
+ }
1620
+ /**
1621
+ * Count coupon periods between settlement and maturity (rounded up).
1622
+ */
1623
+ function couponsBetween(settlement, maturity, frequency) {
1624
+ const stepMonths = Math.round(12 / frequency);
1625
+ let count = 0;
1626
+ let cur = maturity;
1627
+ while (cur > settlement) {
1628
+ cur = addMonthsToSerial(cur, -stepMonths);
1629
+ count++;
1630
+ }
1631
+ return count;
1632
+ }
1633
+ /**
1634
+ * Validate the basic bond arguments (frequency and basis). Returns null if
1635
+ * valid, or the appropriate #NUM! error.
1636
+ */
1637
+ function validateBondBasis(frequency, basis) {
1638
+ if (frequency !== 1 && frequency !== 2 && frequency !== 4) {
1639
+ return values_1.ERRORS.NUM;
1640
+ }
1641
+ if (basis < 0 || basis > 4) {
1642
+ return values_1.ERRORS.NUM;
1643
+ }
1644
+ return null;
1645
+ }
1646
+ /**
1647
+ * PRICE(settlement, maturity, rate, yield, redemption, frequency, [basis])
1648
+ *
1649
+ * Price per $100 face value of a security that pays periodic interest.
1650
+ * Excel formula (standard case, more than one coupon period remaining):
1651
+ *
1652
+ * P = [redemption / (1 + y/f)^(N - 1 + DSC/E)]
1653
+ * + Σ_{k=1..N} [100 * r / f / (1 + y/f)^(k - 1 + DSC/E)]
1654
+ * - 100 * r / f * A/E
1655
+ *
1656
+ * Where:
1657
+ * f = frequency
1658
+ * N = number of coupons from settlement to maturity
1659
+ * DSC = days from settlement to next coupon
1660
+ * E = days in coupon period containing settlement
1661
+ * A = days from beginning of coupon period to settlement
1662
+ */
1663
+ const fnPRICE = args => {
1664
+ const settlementRV = (0, values_1.toNumberRV)(args[0]);
1665
+ if ((0, values_1.isError)(settlementRV)) {
1666
+ return settlementRV;
1667
+ }
1668
+ const maturityRV = (0, values_1.toNumberRV)(args[1]);
1669
+ if ((0, values_1.isError)(maturityRV)) {
1670
+ return maturityRV;
1671
+ }
1672
+ const rateRV = (0, values_1.toNumberRV)(args[2]);
1673
+ if ((0, values_1.isError)(rateRV)) {
1674
+ return rateRV;
1675
+ }
1676
+ const yieldRV = (0, values_1.toNumberRV)(args[3]);
1677
+ if ((0, values_1.isError)(yieldRV)) {
1678
+ return yieldRV;
1679
+ }
1680
+ const redemptionRV = (0, values_1.toNumberRV)(args[4]);
1681
+ if ((0, values_1.isError)(redemptionRV)) {
1682
+ return redemptionRV;
1683
+ }
1684
+ const frequencyRV = (0, values_1.toNumberRV)(args[5]);
1685
+ if ((0, values_1.isError)(frequencyRV)) {
1686
+ return frequencyRV;
1687
+ }
1688
+ const basisRV = args.length > 6 ? (0, values_1.toNumberRV)(args[6]) : (0, values_1.rvNumber)(0);
1689
+ if ((0, values_1.isError)(basisRV)) {
1690
+ return basisRV;
1691
+ }
1692
+ const settlement = Math.floor(settlementRV.value);
1693
+ const maturity = Math.floor(maturityRV.value);
1694
+ const rate = rateRV.value;
1695
+ const yld = yieldRV.value;
1696
+ const redemption = redemptionRV.value;
1697
+ const frequency = Math.floor(frequencyRV.value);
1698
+ const basis = Math.floor(basisRV.value);
1699
+ if (settlement >= maturity || rate < 0 || yld < 0 || redemption <= 0) {
1700
+ return values_1.ERRORS.NUM;
1701
+ }
1702
+ const basisErr = validateBondBasis(frequency, basis);
1703
+ if (basisErr) {
1704
+ return basisErr;
1705
+ }
1706
+ const nextCoupon = nextCouponAfter(settlement, maturity, frequency);
1707
+ const prevCoupon = prevCouponOnOrBefore(settlement, maturity, frequency);
1708
+ // Period length E, accrued A, and days-to-next-coupon DSC must all
1709
+ // honour the selected basis. The previous implementation used raw
1710
+ // `floor(next) - floor(prev)` day counts, which is correct for basis
1711
+ // 1/2/3 (Actual/something) but off by up to ~5 days for basis 0 and 4
1712
+ // (30/360). Using `dayCountFraction` — which already implements every
1713
+ // basis — keeps PRICE consistent with YIELD and the rest of the bond
1714
+ // family.
1715
+ const e = dayCountFraction(prevCoupon, nextCoupon, basis) * yearBasisDays(basis);
1716
+ const a = dayCountFraction(prevCoupon, settlement, basis) * yearBasisDays(basis);
1717
+ const dscDays = e - a;
1718
+ // Fractional position in period (DSC/E).
1719
+ const dscE = e === 0 ? 0 : dscDays / e;
1720
+ const N = couponsBetween(settlement, maturity, frequency);
1721
+ const couponAmt = (100 * rate) / frequency;
1722
+ const discountBase = 1 + yld / frequency;
1723
+ let price = redemption / Math.pow(discountBase, N - 1 + dscE);
1724
+ for (let k = 1; k <= N; k++) {
1725
+ price += couponAmt / Math.pow(discountBase, k - 1 + dscE);
1726
+ }
1727
+ price -= couponAmt * (a / e);
1728
+ return (0, values_1.rvNumber)(price);
1729
+ };
1730
+ exports.fnPRICE = fnPRICE;
1731
+ /**
1732
+ * YIELD(settlement, maturity, rate, pr, redemption, frequency, [basis])
1733
+ *
1734
+ * Inverse of PRICE: solve numerically for yield such that PRICE(...y) = pr.
1735
+ * Uses bracketed bisection in [0, 1] (100% yield upper bound covers all
1736
+ * realistic bond scenarios) followed by a light Newton polish.
1737
+ */
1738
+ const fnYIELD = args => {
1739
+ const settlementRV = (0, values_1.toNumberRV)(args[0]);
1740
+ if ((0, values_1.isError)(settlementRV)) {
1741
+ return settlementRV;
1742
+ }
1743
+ const maturityRV = (0, values_1.toNumberRV)(args[1]);
1744
+ if ((0, values_1.isError)(maturityRV)) {
1745
+ return maturityRV;
1746
+ }
1747
+ const rateRV = (0, values_1.toNumberRV)(args[2]);
1748
+ if ((0, values_1.isError)(rateRV)) {
1749
+ return rateRV;
1750
+ }
1751
+ const prRV = (0, values_1.toNumberRV)(args[3]);
1752
+ if ((0, values_1.isError)(prRV)) {
1753
+ return prRV;
1754
+ }
1755
+ const redemptionRV = (0, values_1.toNumberRV)(args[4]);
1756
+ if ((0, values_1.isError)(redemptionRV)) {
1757
+ return redemptionRV;
1758
+ }
1759
+ const frequencyRV = (0, values_1.toNumberRV)(args[5]);
1760
+ if ((0, values_1.isError)(frequencyRV)) {
1761
+ return frequencyRV;
1762
+ }
1763
+ const basisRV = args.length > 6 ? (0, values_1.toNumberRV)(args[6]) : (0, values_1.rvNumber)(0);
1764
+ if ((0, values_1.isError)(basisRV)) {
1765
+ return basisRV;
1766
+ }
1767
+ if (prRV.value <= 0 || redemptionRV.value <= 0) {
1768
+ return values_1.ERRORS.NUM;
1769
+ }
1770
+ const priceAt = (y) => {
1771
+ const result = (0, exports.fnPRICE)([
1772
+ settlementRV,
1773
+ maturityRV,
1774
+ rateRV,
1775
+ (0, values_1.rvNumber)(y),
1776
+ redemptionRV,
1777
+ frequencyRV,
1778
+ basisRV
1779
+ ]);
1780
+ if (result.kind !== 1 /* RVKind.Number */) {
1781
+ return NaN;
1782
+ }
1783
+ return result.value;
1784
+ };
1785
+ // Bisection in [0, 1].
1786
+ let lo = 0;
1787
+ let hi = 1;
1788
+ const fLo = priceAt(lo) - prRV.value;
1789
+ const fHi = priceAt(hi) - prRV.value;
1790
+ if (isNaN(fLo) || isNaN(fHi)) {
1791
+ return values_1.ERRORS.NUM;
1792
+ }
1793
+ if (fLo * fHi > 0) {
1794
+ // Try extending upper bound.
1795
+ hi = 10;
1796
+ if ((priceAt(hi) - prRV.value) * fLo > 0) {
1797
+ return values_1.ERRORS.NUM;
1798
+ }
1799
+ }
1800
+ for (let i = 0; i < 200; i++) {
1801
+ const mid = (lo + hi) / 2;
1802
+ const f = priceAt(mid) - prRV.value;
1803
+ if (isNaN(f)) {
1804
+ return values_1.ERRORS.NUM;
1805
+ }
1806
+ // PRICE decreases as yield increases, so f = priceAtY - pr decreases.
1807
+ if (f > 0) {
1808
+ lo = mid;
1809
+ }
1810
+ else {
1811
+ hi = mid;
1812
+ }
1813
+ if (hi - lo < 1e-12) {
1814
+ break;
1815
+ }
1816
+ }
1817
+ return (0, values_1.rvNumber)((lo + hi) / 2);
1818
+ };
1819
+ exports.fnYIELD = fnYIELD;
1820
+ /**
1821
+ * DURATION(settlement, maturity, coupon, yield, frequency, [basis])
1822
+ *
1823
+ * Macaulay duration of a bond: the weighted average time to cash flows,
1824
+ * weighted by present value. Expressed in years.
1825
+ */
1826
+ const fnDURATION = args => {
1827
+ const settlementRV = (0, values_1.toNumberRV)(args[0]);
1828
+ if ((0, values_1.isError)(settlementRV)) {
1829
+ return settlementRV;
1830
+ }
1831
+ const maturityRV = (0, values_1.toNumberRV)(args[1]);
1832
+ if ((0, values_1.isError)(maturityRV)) {
1833
+ return maturityRV;
1834
+ }
1835
+ const couponRV = (0, values_1.toNumberRV)(args[2]);
1836
+ if ((0, values_1.isError)(couponRV)) {
1837
+ return couponRV;
1838
+ }
1839
+ const yieldRV = (0, values_1.toNumberRV)(args[3]);
1840
+ if ((0, values_1.isError)(yieldRV)) {
1841
+ return yieldRV;
1842
+ }
1843
+ const frequencyRV = (0, values_1.toNumberRV)(args[4]);
1844
+ if ((0, values_1.isError)(frequencyRV)) {
1845
+ return frequencyRV;
1846
+ }
1847
+ const basisRV = args.length > 5 ? (0, values_1.toNumberRV)(args[5]) : (0, values_1.rvNumber)(0);
1848
+ if ((0, values_1.isError)(basisRV)) {
1849
+ return basisRV;
1850
+ }
1851
+ const settlement = Math.floor(settlementRV.value);
1852
+ const maturity = Math.floor(maturityRV.value);
1853
+ const coupon = couponRV.value;
1854
+ const yld = yieldRV.value;
1855
+ const frequency = Math.floor(frequencyRV.value);
1856
+ const basis = Math.floor(basisRV.value);
1857
+ if (settlement >= maturity || coupon < 0 || yld < 0) {
1858
+ return values_1.ERRORS.NUM;
1859
+ }
1860
+ const basisErr = validateBondBasis(frequency, basis);
1861
+ if (basisErr) {
1862
+ return basisErr;
1863
+ }
1864
+ const nextCoupon = nextCouponAfter(settlement, maturity, frequency);
1865
+ const prevCoupon = prevCouponOnOrBefore(settlement, maturity, frequency);
1866
+ const periodDays = Math.floor(nextCoupon) - Math.floor(prevCoupon);
1867
+ const dscDays = Math.floor(nextCoupon) - settlement;
1868
+ const dscE = periodDays === 0 ? 0 : dscDays / periodDays;
1869
+ const N = couponsBetween(settlement, maturity, frequency);
1870
+ const couponPerPeriod = (100 * coupon) / frequency;
1871
+ const discountBase = 1 + yld / frequency;
1872
+ let pv = 0;
1873
+ let weighted = 0;
1874
+ for (let k = 1; k <= N; k++) {
1875
+ const t = (k - 1 + dscE) / frequency; // time in years
1876
+ const cf = k === N ? couponPerPeriod + 100 : couponPerPeriod;
1877
+ const df = Math.pow(discountBase, k - 1 + dscE);
1878
+ pv += cf / df;
1879
+ weighted += (t * cf) / df;
1880
+ }
1881
+ if (pv === 0) {
1882
+ return values_1.ERRORS.NUM;
1883
+ }
1884
+ return (0, values_1.rvNumber)(weighted / pv);
1885
+ };
1886
+ exports.fnDURATION = fnDURATION;
1887
+ /**
1888
+ * MDURATION — modified duration = DURATION / (1 + yield/frequency).
1889
+ */
1890
+ const fnMDURATION = args => {
1891
+ const dur = (0, exports.fnDURATION)(args);
1892
+ if (dur.kind !== 1 /* RVKind.Number */) {
1893
+ return dur;
1894
+ }
1895
+ const yieldRV = (0, values_1.toNumberRV)(args[3]);
1896
+ if ((0, values_1.isError)(yieldRV)) {
1897
+ return yieldRV;
1898
+ }
1899
+ const frequencyRV = (0, values_1.toNumberRV)(args[4]);
1900
+ if ((0, values_1.isError)(frequencyRV)) {
1901
+ return frequencyRV;
1902
+ }
1903
+ return (0, values_1.rvNumber)(dur.value / (1 + yieldRV.value / frequencyRV.value));
1904
+ };
1905
+ exports.fnMDURATION = fnMDURATION;
1906
+ /**
1907
+ * ACCRINT(issue, first_interest, settlement, rate, par, frequency, [basis], [calc_method])
1908
+ *
1909
+ * Accrued interest for a security that pays periodic interest.
1910
+ * The simplified implementation (calc_method TRUE, the default) treats
1911
+ * accrued interest from issue to settlement as par * rate * dcf(issue, settlement, basis).
1912
+ */
1913
+ const fnACCRINT = args => {
1914
+ const issueRV = (0, values_1.toNumberRV)(args[0]);
1915
+ if ((0, values_1.isError)(issueRV)) {
1916
+ return issueRV;
1917
+ }
1918
+ // first_interest (args[1]) is unused in the simplified implementation.
1919
+ const settlementRV = (0, values_1.toNumberRV)(args[2]);
1920
+ if ((0, values_1.isError)(settlementRV)) {
1921
+ return settlementRV;
1922
+ }
1923
+ const rateRV = (0, values_1.toNumberRV)(args[3]);
1924
+ if ((0, values_1.isError)(rateRV)) {
1925
+ return rateRV;
1926
+ }
1927
+ const parRV = (0, values_1.toNumberRV)(args[4]);
1928
+ if ((0, values_1.isError)(parRV)) {
1929
+ return parRV;
1930
+ }
1931
+ const frequencyRV = (0, values_1.toNumberRV)(args[5]);
1932
+ if ((0, values_1.isError)(frequencyRV)) {
1933
+ return frequencyRV;
1934
+ }
1935
+ const basisRV = args.length > 6 ? (0, values_1.toNumberRV)(args[6]) : (0, values_1.rvNumber)(0);
1936
+ if ((0, values_1.isError)(basisRV)) {
1937
+ return basisRV;
1938
+ }
1939
+ // calc_method (args[7]) — we ignore the distinction between TRUE/FALSE
1940
+ // because the simplified semantics always accrue from issue date.
1941
+ const issue = Math.floor(issueRV.value);
1942
+ const settlement = Math.floor(settlementRV.value);
1943
+ const frequency = Math.floor(frequencyRV.value);
1944
+ const basis = Math.floor(basisRV.value);
1945
+ if (issue >= settlement || rateRV.value <= 0 || parRV.value <= 0) {
1946
+ return values_1.ERRORS.NUM;
1947
+ }
1948
+ const basisErr = validateBondBasis(frequency, basis);
1949
+ if (basisErr) {
1950
+ return basisErr;
1951
+ }
1952
+ const dcf = dayCountFraction(issue, settlement, basis);
1953
+ return (0, values_1.rvNumber)(parRV.value * rateRV.value * dcf);
1954
+ };
1955
+ exports.fnACCRINT = fnACCRINT;
1956
+ /**
1957
+ * ACCRINTM(issue, settlement, rate, par, [basis]) — accrued interest
1958
+ * for a security that pays interest at maturity.
1959
+ * result = par × rate × dayCountFraction(issue, settlement, basis)
1960
+ */
1961
+ const fnACCRINTM = args => {
1962
+ const issueRV = (0, values_1.toNumberRV)(args[0]);
1963
+ if ((0, values_1.isError)(issueRV)) {
1964
+ return issueRV;
1965
+ }
1966
+ const settlementRV = (0, values_1.toNumberRV)(args[1]);
1967
+ if ((0, values_1.isError)(settlementRV)) {
1968
+ return settlementRV;
1969
+ }
1970
+ const rateRV = (0, values_1.toNumberRV)(args[2]);
1971
+ if ((0, values_1.isError)(rateRV)) {
1972
+ return rateRV;
1973
+ }
1974
+ const parRV = (0, values_1.toNumberRV)(args[3]);
1975
+ if ((0, values_1.isError)(parRV)) {
1976
+ return parRV;
1977
+ }
1978
+ const basisRV = args.length > 4 ? (0, values_1.toNumberRV)(args[4]) : (0, values_1.rvNumber)(0);
1979
+ if ((0, values_1.isError)(basisRV)) {
1980
+ return basisRV;
1981
+ }
1982
+ const issue = Math.floor(issueRV.value);
1983
+ const settlement = Math.floor(settlementRV.value);
1984
+ const basis = Math.floor(basisRV.value);
1985
+ if (issue >= settlement || rateRV.value <= 0 || parRV.value <= 0) {
1986
+ return values_1.ERRORS.NUM;
1987
+ }
1988
+ if (basis < 0 || basis > 4) {
1989
+ return values_1.ERRORS.NUM;
1990
+ }
1991
+ const dcf = dayCountFraction(issue, settlement, basis);
1992
+ return (0, values_1.rvNumber)(parRV.value * rateRV.value * dcf);
1993
+ };
1994
+ exports.fnACCRINTM = fnACCRINTM;
1995
+ /**
1996
+ * TBILLPRICE(settlement, maturity, discount) — price per $100 face value.
1997
+ * price = 100 × (1 - discount × DSM / 360)
1998
+ * where DSM is days from settlement to maturity.
1999
+ */
2000
+ const fnTBILLPRICE = args => {
2001
+ const settlementRV = (0, values_1.toNumberRV)(args[0]);
2002
+ if ((0, values_1.isError)(settlementRV)) {
2003
+ return settlementRV;
2004
+ }
2005
+ const maturityRV = (0, values_1.toNumberRV)(args[1]);
2006
+ if ((0, values_1.isError)(maturityRV)) {
2007
+ return maturityRV;
2008
+ }
2009
+ const discountRV = (0, values_1.toNumberRV)(args[2]);
2010
+ if ((0, values_1.isError)(discountRV)) {
2011
+ return discountRV;
2012
+ }
2013
+ const settlement = Math.floor(settlementRV.value);
2014
+ const maturity = Math.floor(maturityRV.value);
2015
+ const discount = discountRV.value;
2016
+ if (settlement >= maturity || discount <= 0) {
2017
+ return values_1.ERRORS.NUM;
2018
+ }
2019
+ // T-bills have a max maturity of 1 year (~365 days).
2020
+ const dsm = maturity - settlement;
2021
+ if (dsm > 365) {
2022
+ return values_1.ERRORS.NUM;
2023
+ }
2024
+ const price = 100 * (1 - (discount * dsm) / 360);
2025
+ if (price <= 0) {
2026
+ return values_1.ERRORS.NUM;
2027
+ }
2028
+ return (0, values_1.rvNumber)(price);
2029
+ };
2030
+ exports.fnTBILLPRICE = fnTBILLPRICE;
2031
+ /**
2032
+ * TBILLYIELD(settlement, maturity, pr) — bond-equivalent yield.
2033
+ * yield = (100 - pr) / pr × (360 / DSM)
2034
+ */
2035
+ const fnTBILLYIELD = args => {
2036
+ const settlementRV = (0, values_1.toNumberRV)(args[0]);
2037
+ if ((0, values_1.isError)(settlementRV)) {
2038
+ return settlementRV;
2039
+ }
2040
+ const maturityRV = (0, values_1.toNumberRV)(args[1]);
2041
+ if ((0, values_1.isError)(maturityRV)) {
2042
+ return maturityRV;
2043
+ }
2044
+ const prRV = (0, values_1.toNumberRV)(args[2]);
2045
+ if ((0, values_1.isError)(prRV)) {
2046
+ return prRV;
2047
+ }
2048
+ const settlement = Math.floor(settlementRV.value);
2049
+ const maturity = Math.floor(maturityRV.value);
2050
+ const pr = prRV.value;
2051
+ if (settlement >= maturity || pr <= 0) {
2052
+ return values_1.ERRORS.NUM;
2053
+ }
2054
+ const dsm = maturity - settlement;
2055
+ if (dsm > 365) {
2056
+ return values_1.ERRORS.NUM;
2057
+ }
2058
+ return (0, values_1.rvNumber)(((100 - pr) / pr) * (360 / dsm));
2059
+ };
2060
+ exports.fnTBILLYIELD = fnTBILLYIELD;
2061
+ /**
2062
+ * TBILLEQ(settlement, maturity, discount) — bond equivalent yield.
2063
+ * TBILLEQ = (365 × discount) / (360 - discount × DSM)
2064
+ */
2065
+ const fnTBILLEQ = args => {
2066
+ const settlementRV = (0, values_1.toNumberRV)(args[0]);
2067
+ if ((0, values_1.isError)(settlementRV)) {
2068
+ return settlementRV;
2069
+ }
2070
+ const maturityRV = (0, values_1.toNumberRV)(args[1]);
2071
+ if ((0, values_1.isError)(maturityRV)) {
2072
+ return maturityRV;
2073
+ }
2074
+ const discountRV = (0, values_1.toNumberRV)(args[2]);
2075
+ if ((0, values_1.isError)(discountRV)) {
2076
+ return discountRV;
2077
+ }
2078
+ const settlement = Math.floor(settlementRV.value);
2079
+ const maturity = Math.floor(maturityRV.value);
2080
+ const discount = discountRV.value;
2081
+ if (settlement >= maturity || discount <= 0) {
2082
+ return values_1.ERRORS.NUM;
2083
+ }
2084
+ const dsm = maturity - settlement;
2085
+ if (dsm > 365) {
2086
+ return values_1.ERRORS.NUM;
2087
+ }
2088
+ const denom = 360 - discount * dsm;
2089
+ if (denom <= 0) {
2090
+ return values_1.ERRORS.NUM;
2091
+ }
2092
+ return (0, values_1.rvNumber)((365 * discount) / denom);
2093
+ };
2094
+ exports.fnTBILLEQ = fnTBILLEQ;
2095
+ /**
2096
+ * PRICEMAT(settlement, maturity, issue, rate, yld, [basis]) —
2097
+ * price per $100 face value for a security that pays interest at maturity.
2098
+ *
2099
+ * A = DCF(issue, settlement, basis)
2100
+ * DSM = DCF(settlement, maturity, basis)
2101
+ * DIM = DCF(issue, maturity, basis)
2102
+ * price = (100 + DIM × rate × 100) / (1 + DSM × yld) - A × rate × 100
2103
+ */
2104
+ const fnPRICEMAT = args => {
2105
+ const settlementRV = (0, values_1.toNumberRV)(args[0]);
2106
+ if ((0, values_1.isError)(settlementRV)) {
2107
+ return settlementRV;
2108
+ }
2109
+ const maturityRV = (0, values_1.toNumberRV)(args[1]);
2110
+ if ((0, values_1.isError)(maturityRV)) {
2111
+ return maturityRV;
2112
+ }
2113
+ const issueRV = (0, values_1.toNumberRV)(args[2]);
2114
+ if ((0, values_1.isError)(issueRV)) {
2115
+ return issueRV;
2116
+ }
2117
+ const rateRV = (0, values_1.toNumberRV)(args[3]);
2118
+ if ((0, values_1.isError)(rateRV)) {
2119
+ return rateRV;
2120
+ }
2121
+ const yldRV = (0, values_1.toNumberRV)(args[4]);
2122
+ if ((0, values_1.isError)(yldRV)) {
2123
+ return yldRV;
2124
+ }
2125
+ const basisRV = args.length > 5 ? (0, values_1.toNumberRV)(args[5]) : (0, values_1.rvNumber)(0);
2126
+ if ((0, values_1.isError)(basisRV)) {
2127
+ return basisRV;
2128
+ }
2129
+ const settlement = Math.floor(settlementRV.value);
2130
+ const maturity = Math.floor(maturityRV.value);
2131
+ const issue = Math.floor(issueRV.value);
2132
+ const rate = rateRV.value;
2133
+ const yld = yldRV.value;
2134
+ const basis = Math.floor(basisRV.value);
2135
+ if (settlement >= maturity || issue >= settlement) {
2136
+ return values_1.ERRORS.NUM;
2137
+ }
2138
+ if (rate < 0 || yld < 0) {
2139
+ return values_1.ERRORS.NUM;
2140
+ }
2141
+ if (basis < 0 || basis > 4) {
2142
+ return values_1.ERRORS.NUM;
2143
+ }
2144
+ const a = dayCountFraction(issue, settlement, basis);
2145
+ const dsm = dayCountFraction(settlement, maturity, basis);
2146
+ const dim = dayCountFraction(issue, maturity, basis);
2147
+ const numerator = 100 + dim * rate * 100;
2148
+ const denominator = 1 + dsm * yld;
2149
+ return (0, values_1.rvNumber)(numerator / denominator - a * rate * 100);
2150
+ };
2151
+ exports.fnPRICEMAT = fnPRICEMAT;
2152
+ /**
2153
+ * YIELDMAT(settlement, maturity, issue, rate, pr, [basis]) —
2154
+ * annual yield for a security that pays interest at maturity.
2155
+ *
2156
+ * A = DCF(issue, settlement, basis)
2157
+ * DSM = DCF(settlement, maturity, basis)
2158
+ * DIM = DCF(issue, maturity, basis)
2159
+ * yield = ((1 + DIM × rate) / (pr/100 + A × rate) - 1) / DSM
2160
+ */
2161
+ const fnYIELDMAT = args => {
2162
+ const settlementRV = (0, values_1.toNumberRV)(args[0]);
2163
+ if ((0, values_1.isError)(settlementRV)) {
2164
+ return settlementRV;
2165
+ }
2166
+ const maturityRV = (0, values_1.toNumberRV)(args[1]);
2167
+ if ((0, values_1.isError)(maturityRV)) {
2168
+ return maturityRV;
2169
+ }
2170
+ const issueRV = (0, values_1.toNumberRV)(args[2]);
2171
+ if ((0, values_1.isError)(issueRV)) {
2172
+ return issueRV;
2173
+ }
2174
+ const rateRV = (0, values_1.toNumberRV)(args[3]);
2175
+ if ((0, values_1.isError)(rateRV)) {
2176
+ return rateRV;
2177
+ }
2178
+ const prRV = (0, values_1.toNumberRV)(args[4]);
2179
+ if ((0, values_1.isError)(prRV)) {
2180
+ return prRV;
2181
+ }
2182
+ const basisRV = args.length > 5 ? (0, values_1.toNumberRV)(args[5]) : (0, values_1.rvNumber)(0);
2183
+ if ((0, values_1.isError)(basisRV)) {
2184
+ return basisRV;
2185
+ }
2186
+ const settlement = Math.floor(settlementRV.value);
2187
+ const maturity = Math.floor(maturityRV.value);
2188
+ const issue = Math.floor(issueRV.value);
2189
+ const rate = rateRV.value;
2190
+ const pr = prRV.value;
2191
+ const basis = Math.floor(basisRV.value);
2192
+ if (settlement >= maturity || issue >= settlement) {
2193
+ return values_1.ERRORS.NUM;
2194
+ }
2195
+ if (rate < 0 || pr <= 0) {
2196
+ return values_1.ERRORS.NUM;
2197
+ }
2198
+ if (basis < 0 || basis > 4) {
2199
+ return values_1.ERRORS.NUM;
2200
+ }
2201
+ const a = dayCountFraction(issue, settlement, basis);
2202
+ const dsm = dayCountFraction(settlement, maturity, basis);
2203
+ const dim = dayCountFraction(issue, maturity, basis);
2204
+ if (dsm <= 0) {
2205
+ return values_1.ERRORS.NUM;
2206
+ }
2207
+ const numer = 1 + dim * rate;
2208
+ const denom = pr / 100 + a * rate;
2209
+ if (denom <= 0) {
2210
+ return values_1.ERRORS.NUM;
2211
+ }
2212
+ return (0, values_1.rvNumber)((numer / denom - 1) / dsm);
2213
+ };
2214
+ exports.fnYIELDMAT = fnYIELDMAT;
2215
+ // ============================================================================
2216
+ // COUP family — coupon-period queries
2217
+ // ============================================================================
2218
+ /**
2219
+ * Shared validation + parsing for the COUP family. Returns the parsed
2220
+ * settlement/maturity/frequency/basis values, or an error.
2221
+ */
2222
+ function parseCoupArgs(args) {
2223
+ const settlementRV = (0, values_1.toNumberRV)(args[0]);
2224
+ if ((0, values_1.isError)(settlementRV)) {
2225
+ return settlementRV;
2226
+ }
2227
+ const maturityRV = (0, values_1.toNumberRV)(args[1]);
2228
+ if ((0, values_1.isError)(maturityRV)) {
2229
+ return maturityRV;
2230
+ }
2231
+ const frequencyRV = (0, values_1.toNumberRV)(args[2]);
2232
+ if ((0, values_1.isError)(frequencyRV)) {
2233
+ return frequencyRV;
2234
+ }
2235
+ const basisRV = args.length > 3 ? (0, values_1.toNumberRV)(args[3]) : (0, values_1.rvNumber)(0);
2236
+ if ((0, values_1.isError)(basisRV)) {
2237
+ return basisRV;
2238
+ }
2239
+ const settlement = Math.floor(settlementRV.value);
2240
+ const maturity = Math.floor(maturityRV.value);
2241
+ const frequency = Math.floor(frequencyRV.value);
2242
+ const basis = Math.floor(basisRV.value);
2243
+ if (settlement >= maturity) {
2244
+ return values_1.ERRORS.NUM;
2245
+ }
2246
+ const err = validateBondBasis(frequency, basis);
2247
+ if (err) {
2248
+ return err;
2249
+ }
2250
+ return { settlement, maturity, frequency, basis };
2251
+ }
2252
+ /**
2253
+ * COUPNCD(settlement, maturity, frequency, [basis]) — next coupon date
2254
+ * after settlement, as an Excel serial.
2255
+ */
2256
+ const fnCOUPNCD = args => {
2257
+ const p = parseCoupArgs(args);
2258
+ if ("kind" in p) {
2259
+ return p;
2260
+ }
2261
+ return (0, values_1.rvNumber)(nextCouponAfter(p.settlement, p.maturity, p.frequency));
2262
+ };
2263
+ exports.fnCOUPNCD = fnCOUPNCD;
2264
+ /**
2265
+ * COUPPCD(settlement, maturity, frequency, [basis]) — previous coupon
2266
+ * date on or before settlement, as an Excel serial.
2267
+ */
2268
+ const fnCOUPPCD = args => {
2269
+ const p = parseCoupArgs(args);
2270
+ if ("kind" in p) {
2271
+ return p;
2272
+ }
2273
+ return (0, values_1.rvNumber)(prevCouponOnOrBefore(p.settlement, p.maturity, p.frequency));
2274
+ };
2275
+ exports.fnCOUPPCD = fnCOUPPCD;
2276
+ /**
2277
+ * COUPNUM(settlement, maturity, frequency, [basis]) — number of
2278
+ * coupons payable between settlement and maturity, rounded up.
2279
+ */
2280
+ const fnCOUPNUM = args => {
2281
+ const p = parseCoupArgs(args);
2282
+ if ("kind" in p) {
2283
+ return p;
2284
+ }
2285
+ return (0, values_1.rvNumber)(couponsBetween(p.settlement, p.maturity, p.frequency));
2286
+ };
2287
+ exports.fnCOUPNUM = fnCOUPNUM;
2288
+ /**
2289
+ * COUPDAYSNC(settlement, maturity, frequency, [basis]) — days from
2290
+ * settlement to the next coupon date.
2291
+ */
2292
+ const fnCOUPDAYSNC = args => {
2293
+ const p = parseCoupArgs(args);
2294
+ if ("kind" in p) {
2295
+ return p;
2296
+ }
2297
+ const next = nextCouponAfter(p.settlement, p.maturity, p.frequency);
2298
+ if (p.basis === 0 || p.basis === 4) {
2299
+ // 30/360 methods — Excel treats the period day-count via NASD-style
2300
+ // adjustment; dayCountFraction already handles this. Multiply by
2301
+ // 360 (days in 30/360 "year") to get days.
2302
+ return (0, values_1.rvNumber)(dayCountFraction(p.settlement, next, p.basis) * 360);
2303
+ }
2304
+ // Actual day count: plain serial difference works for basis 1/2/3.
2305
+ return (0, values_1.rvNumber)(next - p.settlement);
2306
+ };
2307
+ exports.fnCOUPDAYSNC = fnCOUPDAYSNC;
2308
+ /**
2309
+ * COUPDAYBS(settlement, maturity, frequency, [basis]) — days from the
2310
+ * beginning of the coupon period to settlement.
2311
+ */
2312
+ const fnCOUPDAYBS = args => {
2313
+ const p = parseCoupArgs(args);
2314
+ if ("kind" in p) {
2315
+ return p;
2316
+ }
2317
+ const prev = prevCouponOnOrBefore(p.settlement, p.maturity, p.frequency);
2318
+ if (p.basis === 0 || p.basis === 4) {
2319
+ return (0, values_1.rvNumber)(dayCountFraction(prev, p.settlement, p.basis) * 360);
2320
+ }
2321
+ return (0, values_1.rvNumber)(p.settlement - prev);
2322
+ };
2323
+ exports.fnCOUPDAYBS = fnCOUPDAYBS;
2324
+ /**
2325
+ * COUPDAYS(settlement, maturity, frequency, [basis]) — days in the
2326
+ * coupon period that contains settlement.
2327
+ */
2328
+ const fnCOUPDAYS = args => {
2329
+ const p = parseCoupArgs(args);
2330
+ if ("kind" in p) {
2331
+ return p;
2332
+ }
2333
+ const prev = prevCouponOnOrBefore(p.settlement, p.maturity, p.frequency);
2334
+ const next = nextCouponAfter(p.settlement, p.maturity, p.frequency);
2335
+ if (p.basis === 1) {
2336
+ // Actual/actual — actual days between the two coupon dates.
2337
+ return (0, values_1.rvNumber)(next - prev);
2338
+ }
2339
+ if (p.basis === 2 || p.basis === 3) {
2340
+ // Actual/360 or actual/365 — the period length is conventionally the
2341
+ // period basis divided by frequency (e.g. 360/freq, 365/freq).
2342
+ const yearDays = p.basis === 2 ? 360 : 365;
2343
+ return (0, values_1.rvNumber)(yearDays / p.frequency);
2344
+ }
2345
+ // 30/360 NASD or 30/360 European: 360/freq conventional.
2346
+ return (0, values_1.rvNumber)(360 / p.frequency);
2347
+ };
2348
+ exports.fnCOUPDAYS = fnCOUPDAYS;