Skip to content

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

ROD Logic

This file documents the current ROD calculation engine implementation.

Status

  • Scope: current ROD engine and the shared annual-accumulation family
  • Implementation status: active in production code
  • Code path:
  • annual-accumulation.ts: src/app/domain/bond-engine/strategies/annual-accumulation.ts
  • rod.ts: src/app/domain/bond-engine/strategies/rod.ts
  • Test path:
  • rod.strategy.test.ts: tests/rod.strategy.test.ts
  • annual-accumulation.strategy.test.ts: tests/annual-accumulation.strategy.test.ts
  • excel-accumulation-alignment.test.ts: tests/excel-accumulation-alignment.test.ts
  • Visual diagrams:
  • ROD_interest_flow_diagram.md - Flow diagrams and algorithm visualization

Product Rules

  • Bond: ROD
  • Duration: 12 years
  • Year 1 rate: 5.60%
  • Years 2-12 rate: inflation + 2.50%
  • Capitalization: yearly
  • Interest payout: only on redemption
  • Early redemption cost: 3 zł per bond
  • Exchange price used on rollover: catalog-driven; both the production catalog and the workbook-aligned regression scenario currently use 100
  • Bond unit price: 100 zł
  • Tax rate: 19%
  • Availability: only for investors receiving 800+

Strategy Family

ROD is a specialization of the shared annual-accumulation family.

It configures:

  • strategyId = annual-accumulation-rod
  • cycleLengthYears = 12
  • rate resolver:
  • cycleYear === 1 -> 5.60%
  • cycleYear >= 2 -> inflation + 2.50%

Runtime Model

The engine models:

  • yearly capitalization inside the active ROD batch
  • tax only on redemption
  • rollover after natural maturity
  • a residual cash account that does not earn interest (unlike non-capitalizing bonds)

For ROD, workbook alignment depends on both the rollover and the residual-account behavior. Since ROD is capitalizing, the residual account does not earn monthly interest to prevent incorrect OKO balance reporting, even when exchangePrice is effectively 100.

Natural Redemption And Rollover

At natural maturity:

tax = (grossValue - principal) * 0.19
netRedemption = grossValue - tax
nextBondCount = floor(netRedemption / exchangePrice)
residue = netRedemption - nextBondCount * exchangePrice

residue is transferred to the residual cash account. Since ROD is capitalizing, this residue does not earn monthly interest.

Final Early Redemption

If the simulation ends before year 12:

earlyCost = bondCount * 3 zł
taxableProfit = max(0, grossValue - principal - earlyCost)
tax = taxableProfit * 0.19
netLiquidation = grossValue - earlyCost - tax

Public Result Semantics

The ROD public result includes:

  • bondValue
  • cashAccountBalance
  • yearly tax and early-redemption postings
  • purchase and redemption histories

Covered Tests

Current automated ROD tests cover:

  • first-year fixed 5.60%
  • switch to inflation + 2.50%
  • natural 12-year maturity
  • early redemption after 1, 2, and 6 years
  • rollover after natural maturity
  • residual cash-account balance after rollover
  • public result contract
  • Excel alignment across 12 yearly checkpoints

Eligibility Boundary

ROD remains available only to investors receiving 800+, but that rule stays outside the strategy itself.

Change Rules

If ROD logic changes, update together:

  • annual-accumulation.ts: src/app/domain/bond-engine/strategies/annual-accumulation.ts
  • annual_accumulation_logic.md
  • rod.ts: src/app/domain/bond-engine/strategies/rod.ts
  • rod.strategy.test.ts: tests/rod.strategy.test.ts
  • excel-accumulation-alignment.test.ts: tests/excel-accumulation-alignment.test.ts
  • ROD_logic.md