Skip to content

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

ROS Logic

This file documents the current ROS calculation engine implementation.

Status

  • Scope: current ROS 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
  • ros.ts: src/app/domain/bond-engine/strategies/ros.ts
  • Test path:
  • annual-accumulation.strategy.test.ts: tests/annual-accumulation.strategy.test.ts
  • ros.strategy.test.ts: tests/ros.strategy.test.ts
  • Last reviewed against code: 2026-03-23

Product Rules

  • Bond: ROS
  • Duration: 6 years
  • Year 1 rate: 5.20%
  • Years 2-6 rate: inflation + 2.00%
  • 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

ROS 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

and is intended to be extended further with:

  • ROD

ROS Specialization

ROS configures the shared engine with:

  • strategyId = annual-accumulation-ros
  • cycleLengthYears = 6
  • rate resolver:
  • cycleYear === 1 -> 5.20%
  • cycleYear >= 2 -> inflation + 2.00%

So the ROS 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 (6 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.
flowchart TD
    A[Initial cash] --> B[Buy initial full bonds]
    B --> C[Year loop]
    C --> D{Any batch reached 6 years?}
    D -->|Yes| E[Natural redemption + tax]
    D -->|No| F[Keep active batches]
    E --> G[Add net cash]
    F --> G
    G --> H{More years left?}
    H -->|Yes| I[Buy new full bonds]
    H -->|No| J[Final settlement]
    I --> C
    J --> K[Early redeem unfinished batches]
    K --> L[Build final result]

Natural Redemption

For a naturally redeemed batch:

  1. Start with nominal principal.
  2. Apply yearly compounding for 6 years:
V1 = P * (1 + 0.052)
V2 = V1 * (1 + inflation + 0.02)
...
V6 = V5 * (1 + inflation + 0.02)
  1. Compute gross profit:
grossProfit = V6 - P
  1. Compute tax:
tax = grossProfit * 0.19
  1. Add net redemption value to cash:
netRedemption = V6 - tax

Final Early Redemption

If the investment horizon ends before a batch reaches 6 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

ROS 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 ROS public result includes:

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

Covered Tests

Current automated ROS tests cover:

  • first-year fixed 5.20% rate
  • switch to inflation + 2.00%
  • natural 6-year redemption
  • early redemption after 1, 2, and 5 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:
  • ros.strategy.test.ts: tests/ros.strategy.test.ts
  • shared carried-value vs liquidation semantics:
  • annual-accumulation.strategy.test.ts: tests/annual-accumulation.strategy.test.ts
  • purchase and redemption history retention:
  • ros.strategy.test.ts: tests/ros.strategy.test.ts
  • annual-accumulation.strategy.test.ts: tests/annual-accumulation.strategy.test.ts

Change Rules

If ROS logic changes, update together:

  • annual-accumulation.ts: src/app/domain/bond-engine/strategies/annual-accumulation.ts
  • annual_accumulation_logic.md
  • ros.ts: src/app/domain/bond-engine/strategies/ros.ts
  • ros.strategy.test.ts: tests/ros.strategy.test.ts
  • ROS_logic.md
  • init_prompt.md: ai/init_prompt.md