Skip to content

Generated file. Source: docs/annual_payout_logic.md Edit the source document and run npm run docs:sync to refresh this published copy.

Annual Payout Strategy Family

This file documents the shared annual-payout engine used by bonds that:

  • pay interest out to cash every year
  • do not capitalize interest inside the same batch
  • may reinvest accumulated cash into new batches
  • redeem nominal principal at natural maturity
  • may finish with final early redemption of unfinished batches

Status

  • Shared engine status: active in production code
  • Current specializations:
  • COI

Code Path

  • annual-payout.ts: src/app/domain/bond-engine/strategies/annual-payout.ts

Shared Responsibilities

The shared engine is responsible for:

  • initial allocation into full bonds
  • yearly interest payout posting
  • yearly tax posting on paid interest
  • natural redemption handling
  • reinvestment after cash accumulation
  • final early redemption
  • yearly snapshots
  • purchase history
  • payout history
  • redemption history
  • final public totals

Specializations should define only:

  • strategyId
  • cycle length in years
  • annual rate resolution policy

The product-specific strategy factory exported from each specialization module is the single source of truth for:

  • runtime strategy configuration
  • test strategy configuration
  • public strategy identifier

This is intentional. Tests should reuse these factories instead of duplicating inline strategy definitions.

Eligibility rules are intentionally out of scope for this engine family.

Selection and availability belong to:

  • selection.ts: src/app/domain/bond-engine/selection.ts

Do not move bond-availability rules into the annual-payout engine or COI specialization.

Shared State

The engine tracks:

  • cash
  • active positions
  • purchaseEvents
  • payoutEvents
  • redemptionEvents
  • yearSnapshots
  • yearlyResults

Each position contains:

  • bonds
  • purchaseYear

Year Loop

For each year:

  1. Pay annual interest for all active positions.
  2. Tax that year’s paid interest immediately.
  3. Add net interest to cash.
  4. Redeem positions that reached natural maturity and return principal.
  5. Reinvest available cash into full new bonds if the horizon continues.
  6. If the simulation ends, early-redeem all remaining active positions.
  7. Build carried and liquidation snapshots.

Shared Snapshot Semantics

Intermediate years

  • grossValue = carried portfolio value plus all taxes and costs already realized so far
  • netValue = carried portfolio value after already realized taxes and costs
  • for COI, carried portfolio value means cash + active principal

Final year

  • grossValue = final portfolio value before all taxes and costs from the whole simulation
  • netValue = actual liquidation value after final early-redemption costs

Shared History Contracts

Purchase events

Recorded for:

  • initial allocation
  • reinvestment from accumulated cash

Payout events

Recorded for:

  • each yearly interest payout per active batch

Redemption events

Recorded for:

  • natural maturity
  • final early redemption

Invariants

The shared engine should always satisfy:

  • grossValue >= liquidationValue
  • cash >= 0
  • final public gross/net totals match the last yearly snapshot after display rounding
  • taxes are recognized only on actually paid interest
  • reinvestment never happens before available cash reaches one full bond unit

Shared Tests

Shared-engine tests should validate:

  • invariant grids
  • final total consistency
  • payout/redemption history retention
  • golden reference scenarios for at least one specialization

Current test path:

  • annual-payout.strategy.test.ts: tests/annual-payout.strategy.test.ts

Proven Abstraction Scope

The annual-payout family is currently validated on:

  • COI
  • yearly interest payout
  • no capitalization in-batch
  • reinvestment from cash pool
  • principal redemption handled separately from interest payout

If another annual-payout product is added later, it should first try to fit this family before introducing a new one.

Change Rules

If the shared annual-payout engine changes, update together:

  • annual-payout.ts: src/app/domain/bond-engine/strategies/annual-payout.ts
  • all specialization docs and tests that depend on it
  • annual_payout_logic.md