Skip to content

Use face-scaled accrued interest in bond cash values - #256

Open
kadyrbekovhamit-cyber wants to merge 4 commits into
domokane:masterfrom
kadyrbekovhamit-cyber:codex/fix-bond-principal-face-scaling
Open

kadyrbekovhamit-cyber wants to merge 4 commits into
domokane:masterfrom
kadyrbekovhamit-cyber:codex/fix-bond-principal-face-scaling

Conversation

@kadyrbekovhamit-cyber

@kadyrbekovhamit-cyber kadyrbekovhamit-cyber commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Description

Use consistently scaled accrued interest in four bond cash-value paths:

  • Bond.principal()
  • BondFRN.principal()
  • InflationBond.inflation_principal()
  • Bond.clean_price_from_survival_curve()

The three principal methods scale dirty price by face / par, but the released implementation subtracts accrued interest calculated for face 1.0. The unscaled subtraction violates the basic identity

principal(face) = clean_price_per_100 * face / 100

The released BondZero.principal() already follows this pattern by calculating accrued interest with the supplied face.

BondFRN.principal() and BondFRN.clean_price_from_dm() also relied on prior calls to initialize their previous/next coupon dates. Both now calculate accrued interest before dirty price, so direct calls on a newly constructed bond work without hidden state.

Separately, Bond.clean_price_from_survival_curve() returns a price per 100 of par but subtracts accrued interest for face 1.0. In the synthetic released-version reproduction it returns 103.9834175889; the dirty-minus-accrued-per-100 identity is 103.0152960420, a difference of 0.9681215470.

Released-version reproductions

Using synthetic inputs with FinancePy 1.1.2:

Bond.principal(), face 1,000,000
actual:   1,027,043.3256
expected: 1,017,264.3298
difference: 9,778.9957

BondFRN.principal(), face 1,000,000
actual:   970,266.2031
expected: 967,929.9898
difference: 2,336.2132

InflationBond.inflation_principal(), face 1,000,000
actual:   1,167,344.6587
expected: 1,167,116.0212
difference: 228.6375

These are method-level synthetic reproductions only; no production system or customer data was tested.

Correction

Each principal method now recalculates accrued interest for the supplied face amount before subtraction. The survival-curve clean-price path recalculates it for self.par. This also leaves the object's accrued-interest state consistent with the cash value being returned.

Testing

  • Added regressions at face 1, 100 and 1,000,000.
  • Added a fresh-object direct-call regression for BondFRN.principal().
  • The same regression calls BondFRN.clean_price_from_dm() on a separate fresh object before any other method.
  • Updated the existing face-100 expectations to the clean-value identities.
  • Focused bond tests: 17 passed.
  • Full unit suite: 962 passed, 4 pre-existing numerical-fit warnings.
  • Mutation checks: restoring each old unscaled subtraction makes its new regression fail; restoring the corrections returns the focused tests to green.

Signed-off-by: kadyrbekovhamit-cyber <288885044+kadyrbekovhamit-cyber@users.noreply.github.com>
Signed-off-by: kadyrbekovhamit-cyber <288885044+kadyrbekovhamit-cyber@users.noreply.github.com>
@kadyrbekovhamit-cyber kadyrbekovhamit-cyber changed the title Scale Bond.principal accrued interest by face Scale bond principal values by face Sep 13, 2026
Signed-off-by: kadyrbekovhamit-cyber <288885044+kadyrbekovhamit-cyber@users.noreply.github.com>
@kadyrbekovhamit-cyber kadyrbekovhamit-cyber changed the title Scale bond principal values by face Use face-scaled accrued interest in bond cash values Sep 13, 2026
Signed-off-by: kadyrbekovhamit-cyber <288885044+kadyrbekovhamit-cyber@users.noreply.github.com>
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