Skip to content

Add ACCRINTM, DISC, INTRATE, PRICEDISC, RECEIVED and YIELDDISC functions - #1799

Open
Tobiadefami wants to merge 6 commits into
developfrom
feature/HF-454
Open

Tobiadefami wants to merge 6 commits into
developfrom
feature/HF-454

Conversation

@Tobiadefami

@Tobiadefami Tobiadefami commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Context

Adds six Excel financial functions for discounted and interest-at-maturity securities: ACCRINTM, DISC,
INTRATE, PRICEDISC, RECEIVED and YIELDDISC.

Shared day-count helper. DateTimeHelper.dayCountByBasis(startDate, endDate, basis) returns
{dayCount, yearDays} for day-count bases 0-4. Basis 0 applies toBasisUS to copies of the dates (YEARFRAC's US
30/360 rule), basis 1 uses yearLengthForBasis, basis 4 uses toBasisEU. YEARFRAC now uses the helper (its own
commit). YEARFRAC's results are unchanged; DAYS360 is not touched.

Argument handling. Every parameter is SCALAR and is converted in the method, to match Excel:

  • booleans and the empty text give #VALUE!; a blank cell is 0; numeric, percent, currency and date text are
    accepted (date text per the dateFormats config);
  • dates are truncated, and dates outside the supported range give #NUM!, a negative date such as −0.5 included;
  • basis is truncated (4.9 → 4), but a negative basis such as −0.5 is #NUM!, as in Excel;
  • invalid arguments are reported in Excel's order: settlement, maturity, basis, then the two amounts for the
    DISC-like functions (so DISC(s, m, "abc", r, 9) is #NUM!, not #VALUE!); left to right for ACCRINTM.

Arithmetic. Each formula uses the operation order that reproduces Excel's results to the 15th significant digit,
for example DISC = (1 - pr / redemption) * (yearDays / dayCount). Dividing by the year fraction instead differs
from Excel in the last digit on dozens of cases.

strictNumber. The argument conversion is a private strictNumber, copied unchanged (name, signature, JSDoc,
body) from DeltaPlugin in #1788; the same helper is in StatisticalPlugin in #1789. Follow-up: once these PRs are
merged, extract it into one shared helper.

ACCRINTM par. An empty or omitted par is 1000. Excel rejects the 3-argument form
(=ACCRINTM(issue, settlement, rate)); Google Sheets accepts it and returns the same 20.5556 as HyperFormula. Added
to docs/guide/list-of-differences.md. HyperFormula's argument metadata cannot make par required and still map an
empty argument to 1000 while a blank cell stays 0.

How did you test your changes?

  • Expected values come from Microsoft Excel Online (en-US). Each test is one measured case: the formula in the
    test is the formula measured in Excel, on the same cell layout as the test fixture (the Microsoft docs example as
    the arguments, plus cells for coercion), so any case can be re-checked by entering it in Excel. Cases cover the
    docs examples, every basis (29 February, month ends, multi-year spans, zero-day 30/360 spans), coercion of each
    argument, truncation, out-of-range dates, the order in which invalid arguments are reported, array arguments and
    argument counts.
  • 691 new tests in hyperformula-tests: function-disc 117, function-intrate 114, function-pricedisc 114,
    function-received 114, function-yielddisc 114, function-accrintm 118. They run with smartRounding: false
    and compare at Excel's 15th significant digit (three rounding ties compare at 14). Config-dependent cases pass their
    config: dateFormats: ['MM/DD/YYYY', 'YYYY-MM-DD'] for text dates, useArrayArithmetic: true for array constants.
    All failed with #NAME? before the engine change.
  • Full Jest suite with the private tests: 526 suites, 7,424 passed, 3 skipped (pre-existing). The YEARFRAC,
    DAYS360, EDATE, EOMONTH, DATEDIF, TBILLEQ, TBILLPRICE, TBILLYIELD and date specs pass unchanged.
  • YEARFRAC unchanged: the helper was compared with the previous YEARFRAC code on 26,400 values (random date
    pairs and February/month-end pairs, both date orders, bases 0-4): 0 differences.
  • Mutation check: breaking one thing per function fails tests (DISC operation order: 49; INTRATE operation
    order: 4; PRICEDISC without * redemption: 66; RECEIVED <= 0 → < 0: 1; YIELDDISC dividing by redemption: 65;
    ACCRINTM > → >=: 2; basis 0 without toBasisUS: 13, YEARFRAC included).
  • npm run lint (0 errors), npm run compile, npm run docs:generate-function-docs, git diff --check.

Types of changes

  • Breaking change (a fix or a feature because of which an existing functionality doesn't work as expected anymore)
  • New feature or improvement (a non-breaking change that adds functionality)
  • Bug fix (a non-breaking change that fixes an issue)
  • Additional language file, or a change to an existing language file (translations)
  • Change to the documentation

Related issues:

  1. Tests: handsontable/hyperformula-tests#75
  2. Follow-up: extract strictNumber into one shared helper once Add GESTEP function (HF-481) #1788 and Add ERF.PRECISE function (HF-482) #1789 are merged.

Checklist:

  • I have reviewed the guidelines about Contributing to HyperFormula and I confirm that my code follows the code style of this project.
  • I have signed the Contributor License Agreement.
  • My change is compliant with the OpenDocument standard.
  • My change is compatible with Microsoft Excel.
  • My change is compatible with Google Sheets.
  • I described my changes in the CHANGELOG.md file.
  • My changes require a documentation update.
  • My changes require a migration guide.

Translations: names read from Microsoft Excel Online in each language. csCZ keeps the English names, as Excel
does. idID uses the English names: Google Sheets' Indonesian list keeps INTRATE, RECEIVED and YIELDDISC in
English and does not list the other three (DEV_DOCS fallback).

🤖 Generated with Claude Code


Note

Medium Risk
New financial calculations and a shared day-count helper also used by YEARFRAC; incorrect basis or operation order would affect spreadsheet results, though scope is limited to date/financial plugins.

Overview
Adds six Excel securities functions — ACCRINTM, DISC, INTRATE, PRICEDISC, RECEIVED, and YIELDDISC — with metadata, i18n names, license gating, and changelog/docs updates.

Day-count plumbing: DateTimeHelper.dayCountByBasis() centralizes bases 0–4 (dayCount / yearDays); YEARFRAC is refactored to call it (behavior intended unchanged). The five settlement/maturity functions share a private discountedSecurity() path with Excel-style validation order and formulas; ACCRINTM uses the same date/basis coercion plus default par of 1000 when omitted/empty.

Argument handling: New functions use SCALAR args and local strictNumber / coerceToSecurityDate / coerceToDayCountBasis helpers (booleans and "" → #VALUE!, etc.). list-of-differences.md notes Excel rejects the 3-argument ACCRINTM form while HyperFormula matches Google Sheets.

Reviewed by Cursor Bugbot for commit 3ec6b6e. Bugbot is set up for automated code reviews on this repo. Configure here.

Tobiadefami and others added 2 commits October 7, 2026 22:12
DateTimeHelper.dayCountByBasis(startDate, endDate, basis) returns the
day count and the days in the year for day-count bases 0-4. Basis 0
applies toBasisUS to copies of the dates, basis 1 uses
yearLengthForBasis, and basis 4 uses toBasisEU.

YEARFRAC now returns dayCount / yearDays from the helper. Its results
are unchanged. DAYS360 keeps its own days360Core and is not touched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nctions

Six financial functions for discounted and interest-at-maturity
securities, built on DateTimeHelper.dayCountByBasis.

- Every argument is a SCALAR and is converted in the method, so booleans
  and the empty text give #VALUE! and invalid arguments are reported in
  Excel's order (dates, basis, then the amounts for the DISC-like
  functions; left to right for ACCRINTM).
- Basis is truncated, but a negative basis is #NUM!, as in Excel.
- Dates are truncated; dates outside the supported range give #NUM!.
- The arithmetic follows the operation order that reproduces Excel's
  results to the 15th significant digit.
- An empty or omitted ACCRINTM par is 1000. Excel rejects the
  3-argument form; this is added to the list of differences.

Adds catalogue entries, names in all language packs and a changelog
entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cla-external-contractor-signup

Copy link
Copy Markdown

@Tobiadefami thanks for the pull request. No CLA step needed here — our records show you signed the Contributor License Agreement on 2026-07-31. That signature came from our previous signing form and has been carried over, so there is nothing for you to re-sign.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
hyperformula-docs 3ec6b6e Commit Preview URL

Branch Preview URL
Oct 09 2026, 08:49 PM

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit b3989ed. Configure here.

{argumentType: FunctionArgumentType.SCALAR, defaultValue: 1000, emptyAsDefault: true},
{argumentType: FunctionArgumentType.SCALAR, defaultValue: 0},
],
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New functions omit result number types

Low Severity

DISC, INTRATE, and YIELDDISC omit returnNumberType NUMBER_PERCENT, and PRICEDISC, RECEIVED, and ACCRINTM omit NUMBER_CURRENCY. Sibling functions such as TBILLYIELD and TBILLPRICE set these, so getCellValueDetailedType() and type-based formatting treat the new results as raw numbers.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b3989ed. Configure here.

Resolves the CHANGELOG conflict and lists ACCRINTM, DISC, INTRATE, PRICEDISC,
RECEIVED and YIELDDISC in the ungrouped function capabilities, as the license
capability completeness invariant requires.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Performance comparison of head (3ec6b6e) vs base (eff7928)

                                     testName |    base |    head | change
--------------------------------------------------------------------------
                                      Sheet A |  393.48 |  388.35 | -1.30%
                                      Sheet B |  129.06 |  127.61 | -1.12%
                                      Sheet T |  111.21 |  111.67 | +0.41%
                                Column ranges |   417.9 |  414.26 | -0.87%
                                Sorted lookup | 12072.8 | 12224.3 | +1.25%
Sheet A:  change value, add/remove row/column |   13.29 |   13.73 | +3.31%
 Sheet B: change value, add/remove row/column |  113.15 |  113.81 | +0.58%
                   Column ranges - add column |  133.99 |  130.63 | -2.51%
                Column ranges - without batch |  415.24 |  402.78 | -3.00%
                        Column ranges - batch |  104.45 |  102.68 | -1.69%

coerceToSecurityDate truncated before checking the range, so a date
such as -0.5 became serial 0, a valid date. Excel returns #NUM! for any
negative date, before looking at the later arguments. Reject a negative
value before truncating, as coerceToDayCountBasis already does for the
basis.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.11504% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 97.29%. Comparing base (eff7928) to head (3ec6b6e).
⚠️ Report is 6 commits behind head on develop.

Files with missing lines Patch % Lines
src/DateTimeHelper.ts 94.44% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop    #1799      +/-   ##
===========================================
+ Coverage    97.28%   97.29%   +0.01%     
===========================================
  Files          207      207              
  Lines        16218    16319     +101     
  Branches      3588     3612      +24     
===========================================
+ Hits         15777    15878     +101     
  Misses         433      433              
  Partials         8        8              
Files with missing lines Coverage Δ
src/i18n/languages/csCZ.ts 100.00% <ø> (ø)
src/i18n/languages/daDK.ts 100.00% <ø> (ø)
src/i18n/languages/deDE.ts 100.00% <ø> (ø)
src/i18n/languages/enGB.ts 100.00% <ø> (ø)
src/i18n/languages/esES.ts 100.00% <ø> (ø)
src/i18n/languages/fiFI.ts 100.00% <ø> (ø)
src/i18n/languages/frFR.ts 100.00% <ø> (ø)
src/i18n/languages/huHU.ts 100.00% <ø> (ø)
src/i18n/languages/idID.ts 100.00% <ø> (ø)
src/i18n/languages/itIT.ts 100.00% <ø> (ø)
... and 12 more

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant