Skip to content

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

Annual Accumulation Strategy Family

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

  • capitalize annually
  • do not pay interest during the cycle
  • reinvest only after redemption
  • may finish with final early redemption of unfinished batches

Status

  • Shared engine status: active in production code
  • Current specializations:
  • TOS
  • EDO
  • ROS
  • ROD

Code Path

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

Shared Responsibilities

The shared engine is responsible for:

  • initial allocation into full bonds
  • yearly state loop
  • natural redemption handling
  • reinvestment after redemption
  • final early redemption
  • yearly snapshots
  • purchase 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 such as 800+ access for family bonds are intentionally out of scope for this engine family.

Those rules belong to bond selection and sanitization layers, not to financial calculation strategies.

Shared State

The engine tracks:

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

Each position contains:

  • bonds
  • purchaseYear

Year Loop

For each year:

  1. Identify naturally maturing positions.
  2. Compute their gross value.
  3. Compute tax on realized profit.
  4. Add net redemption proceeds to cash.
  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:
  8. carried grossValue
  9. liquidation netValue on final year

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

Final year

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

Shared History Contracts

Purchase events

Recorded for:

  • initial allocation
  • post-redemption reinvestment

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
  • reinvestment never happens before available cash reaches one full bond unit

Mermaid Overview

flowchart TD
    A[Initial cash] --> B[Buy initial full bonds]
    B --> C[Year loop]
    C --> D[Natural redemption]
    D --> E[Add net cash]
    E --> F[Reinvest after redemption]
    F --> G{Final year?}
    G -->|No| C
    G -->|Yes| H[Final early redemption of remaining batches]
    H --> I[Build final yearly result]

Shared Tests

Shared-engine tests should validate:

  • invariant grids
  • final total consistency
  • history retention
  • golden reference scenarios for more than one specialization

Current test path:

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

Proven Abstraction Scope

The shared family is now validated against three different real product shapes:

  • TOS
  • fixed annual rate
  • short 3-year cycle
  • EDO
  • inflation-indexed annual rates
  • long 10-year cycle
  • ROS
  • inflation-indexed annual rates
  • shorter 6-year family-bond cycle
  • ROD
  • inflation-indexed annual rates
  • longer 12-year family-bond cycle
  • different family-bond margin than ROS

This means the current abstraction has now been exercised on the full planned annual-accumulation family.

Change Rules

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

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