Generated file. Source:
docs/EDO_logic.mdEdit the source document and runnpm run docs:syncto refresh this published copy.
EDO Logic¶
This file documents the current EDO calculation engine implementation.
Status¶
- Scope: current
EDOengine 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.tsedo.ts: src/app/domain/bond-engine/strategies/edo.ts- Test path:
annual-accumulation.strategy.test.ts: tests/annual-accumulation.strategy.test.tsedo.strategy.test.ts: tests/edo.strategy.test.ts- Last reviewed against code: 2026-03-23
Product Rules¶
- Bond:
EDO - Duration:
10 years - Year 1 rate:
5.60% - Years 2-10 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%
Strategy Family¶
EDO 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 already used by:
TOS
and is intended to be extended with:
ROSROD
EDO Specialization¶
EDO configures the shared engine with:
strategyId = annual-accumulation-edocycleLengthYears = 10- rate resolver:
cycleYear === 1->5.60%cycleYear >= 2->inflation + 2.00%
So the EDO module contains only product-specific rate policy, while yearly batch mechanics remain shared.
State Model¶
The engine tracks:
cashpositionspurchaseEventsredemptionEventsyearSnapshotsyearlyResults
Each active position contains:
bondspurchaseYear
Simulation Flow¶
The engine works on yearly steps.
For each year:
- Redeem positions that reached natural maturity (
10 years). - Add redeemed net cash to
cash. - Reinvest available
cashinto full new bonds if the horizon continues. - If this is the final year, early-redeem all still-active positions.
- 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 10 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:
- Start with nominal principal.
- Apply yearly compounding for
10years:
V1 = P * (1 + 0.056)
V2 = V1 * (1 + inflation + 0.02)
...
V10 = V9 * (1 + inflation + 0.02)
- Compute gross profit:
grossProfit = V10 - P
- Compute tax:
tax = grossProfit * 0.19
- Add net redemption value to cash:
netRedemption = V10 - tax
Final Early Redemption¶
If the investment horizon ends before a batch reaches 10 years:
- Compute accumulated value for the current age in full years.
- Compute tax on total accrued gain.
- Subtract early redemption cost:
earlyCost = bondCount * 3 zł
- Add final net liquidation value to
cash.
Reinvestment¶
EDO 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¶
This is critical for UI consistency.
grossValue¶
For each year:
grossValuemeans 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
taxPaid¶
- only tax actually realized in that year
earlyRedemptionCost¶
- only cost actually realized in that year
Rounding Policy¶
Current implementation follows project policy:
- internal calculations:
0.001 zł - final public values:
0.01 zł
Public Contract¶
The EDO public result includes:
- yearly results
- final totals
simulationDetails.strategy = "annual-accumulation-edo"simulationDetails.purchaseEventssimulationDetails.redemptionEvents
Covered Tests¶
Current automated EDO tests cover:
- first-year fixed
5.60%rate - switch to
inflation + 2.00% - natural 10-year redemption
- early redemption after 1, 2, and 5 years through golden references and dedicated tests
- 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:
edo.strategy.test.ts: tests/edo.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:
edo.strategy.test.ts: tests/edo.strategy.test.tsannual-accumulation.strategy.test.ts: tests/annual-accumulation.strategy.test.ts
Change Rules¶
If EDO logic changes, update together:
annual-accumulation.ts: src/app/domain/bond-engine/strategies/annual-accumulation.ts- annual_accumulation_logic.md
edo.ts: src/app/domain/bond-engine/strategies/edo.tsedo.strategy.test.ts: tests/edo.strategy.test.ts- EDO_logic.md
init_prompt.md: ai/init_prompt.md