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 strategy family it uses
  • 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:
  • annual-accumulation.strategy.test.ts: tests/annual-accumulation.strategy.test.ts
  • rod.strategy.test.ts: tests/rod.strategy.test.ts
  • Last reviewed against code: 2026-03-23

Product Rules

  • Bond: ROD
  • Duration: 12 years
  • Year 1 rate: 5.85%
  • Years 2-12 rate: inflation + 2.50%
  • Capitalization: yearly
  • Interest payout: only on redemption
  • Early redemption cost: 3 zł per bond
  • Bond unit price: 100 zł
  • Tax rate: 19%
  • Availability: only for investors receiving 800+

Strategy Family

ROD does not use the generic yearly calculator anymore.

It is implemented as a specialization of the shared annual-accumulation family for bonds that:

  • capitalize annually
  • do not pay cash during the cycle
  • allow reinvestment only after redemption
  • may end with early redemption of unfinished batches

That same family is currently used by:

  • TOS
  • EDO
  • ROS

ROD Specialization

ROD configures the shared engine with:

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

So the ROD module contains only product-specific rate policy, while yearly batch mechanics remain shared.

State Model

The engine tracks:

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

Each active position contains:

  • bonds
  • purchaseYear

Simulation Flow

The engine works on yearly steps.

For each year:

  1. Redeem positions that reached natural maturity (12 years).
  2. Add redeemed net cash to cash.
  3. Reinvest available cash into full new bonds if the horizon continues.
  4. If this is the final year, early-redeem all still-active positions.
  5. Build yearly carried-value and liquidation snapshots.

Natural Redemption

For a naturally redeemed batch:

  1. Start with nominal principal.
  2. Apply yearly compounding for 12 years:
V1 = P * (1 + 0.0585)
V2 = V1 * (1 + inflation + 0.025)
...
V12 = V11 * (1 + inflation + 0.025)
  1. Compute gross profit:
grossProfit = V12 - P
  1. Compute tax:
tax = grossProfit * 0.19
  1. Add net redemption value to cash:
netRedemption = V12 - tax

Final Early Redemption

If the investment horizon ends before a batch reaches 12 years:

  1. Compute accumulated value for the current age in full years.
  2. Compute tax on total accrued gain.
  3. Subtract early redemption cost:
earlyCost = bondCount * 3 zł
  1. Add final net liquidation value to cash.

Reinvestment

ROD does not reinvest during a cycle.

Reinvestment may happen only after natural redemption, using:

reinvestedBondCount = floor(cash / 100)

Any remainder stays as cash.

The engine records:

  • initial allocation purchase event
  • each reinvestment purchase event
  • natural redemption events
  • final early redemption events

Snapshot Semantics

grossValue

For each year:

  • grossValue means year-end portfolio value before all taxes and costs realized so far
  • it includes:
  • carried portfolio value
  • taxes already realized in previous completed cycles
  • early-redemption costs already realized, if any

netValue

  • intermediate years:
  • netValue = carried portfolio value
  • no forced liquidation is applied
  • final year:
  • netValue = liquidationValue
  • all taxes and early redemption costs are fully settled

Rounding Policy

Current implementation follows project policy:

  • internal calculations: 0.001 zł
  • final public values: 0.01 zł

Public Contract

The ROD public result includes:

  • yearly results
  • final totals
  • simulationDetails.strategy = "annual-accumulation-rod"
  • simulationDetails.purchaseEvents
  • simulationDetails.redemptionEvents

Covered Tests

Current automated ROD tests cover:

  • first-year fixed 5.85% rate
  • switch to inflation + 2.50%
  • natural 12-year redemption
  • early redemption after 1, 2, 6, and 11 years through direct and golden references
  • no reinvestment before maturity
  • reinvestment after natural redemption
  • public result contract and strategy id
  • long-horizon large-principal stability
  • shared annual-accumulation invariant grid

Traceability

  • product duration, redemption, tax, reinvestment:
  • rod.strategy.test.ts: tests/rod.strategy.test.ts
  • first-year fixed rate:
    • ROD first year uses the dedicated 5.85% rate before the inflation-indexed years begin
  • later-year inflation + margin rule:
    • ROD switches from year-one fixed rate to inflation + 2.50% in later years
  • natural maturity:
    • ROD natural 12-year maturity capitalizes annually and taxes only on redemption
  • final early redemption:
    • ROD early redemption after eleven years remains on the shared annual-accumulation liquidation path
  • reinvestment after maturity:
    • ROD reinvests after natural maturity and stores the second-cycle purchase event
  • shared carried-value vs liquidation semantics:
  • annual-accumulation.strategy.test.ts: tests/annual-accumulation.strategy.test.ts
  • purchase and redemption history retention:
  • rod.strategy.test.ts: tests/rod.strategy.test.ts
  • annual-accumulation.strategy.test.ts: tests/annual-accumulation.strategy.test.ts

Eligibility Boundary

ROD is a special bond available only to investors receiving 800+, but this rule is intentionally not implemented inside the calculation strategy.

Eligibility is handled outside the engine in:

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

This separation is part of the architecture:

  • strategy modules model financial behavior
  • selection/eligibility modules control whether a bond may be chosen

Do not move 800+ availability checks into the annual-accumulation engine or the ROD strategy module.

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
  • ROD_logic.md
  • init_prompt.md: ai/init_prompt.md