Generated file. Source:
docs/OTS_interest_flow_diagram.mdEdit the source document and runnpm run docs:syncto refresh this published copy.
OTS Interest Distribution Flow Diagram¶
This document provides detailed visual and narrative descriptions of how interest flows through the OTS engine after the 2026-04-08 refactoring.
Monthly State Evolution¶
Over a 3-month quarterly cycle:
Month 1-2: Active Holding¶
stateDiagram-v2
[*] --> Holding: New batch purchased
Holding --> Holding: Interest accrues<br/>(not realized)
note right of Holding
Amount: 10 bonds
Purchased: Month 0
Age: 1-2 months
Unrealized Gross Interest
(would be realized at month 3)
end note
Month 3: Quarterly Settlement and Reinvestment¶
flowchart TD
A["Bonds Mature<br/>(10 bonds × €100 × 2.5% × 3/12)"] --> B["Gross Interest<br/>= €6.25"]
B --> C["Apply 19% Tax<br/>€6.25 × 0.19 = €1.1875"]
C --> D["Net Interest<br/>= €4.0625"]
A --> E["Principal<br/>= €1000"]
E --> F["Add to CASH pool<br/>(for reinvestment)"]
D --> G["Add to OKO account<br/>(accumulated interest)"]
F --> H["Available for Reinvestment<br/>CASH + OKO"]
G --> H
H --> I["Total Available<br/>= €1000 + €4.0625"]
I --> J["Compute New Bonds<br/>floor(€1004.0625 / €100) = 10"]
J --> K["After Purchase"]
K --> K1["CASH = €4.0625<br/>(remainder)"]
K --> K2["OKO = €0<br/>(merged for purchase)"]
style E fill:#e1f5ff
style F fill:#e1f5ff
style D fill:#fff3e0
style G fill:#fff3e0
style B fill:#fff3e0
style C fill:#ffebee
Two-Account Model Processing¶
sequenceDiagram
participant B as Bond Position
participant C as Cash Pool
participant O as OKO Account
participant T as Tax Counter
participant N as New Bonds
B->>B: Hold 3 months<br/>with invisible interest
Note over B: Month = quarter marker
B->>B: Calculate Gross Interest<br/>(based on annual rate)
B->>T: Apply tax percentage<br/>Tax = Gross × 0.19
B->>C: Principal + Gross<br/>(initially)
Note over C,T: Interest Separation
C->>C: Keep Principal
C->>O: Transfer Interest portion
O->>O: Net Interest = Gross - Tax
Note over C,O: Reinvestment Phase
C->>C: Existing remainder
O->>C: Add OKO balance
C->>N: Buy new bonds from combined pool
O->>N: (OKO emptied after purchase)
N->>N: Create new position<br/>for next quarter
Quarterly Cycle Example: €1,000 over 4 Quarters¶
Initial Setup¶
| Metric | Value |
|---|---|
| Initial Investment | €1,000 |
| Bonds Purchased | 10 |
| Annual Rate | 2.5% |
| Tax Rate | 19% |
| Cash Remainder | €0 |
| OKO Balance | €0 |
Quarter 1 (Month 3)¶
Redemption:
Gross Interest = 10 × 100 × 0.025 × (3/12) = 6.25
Tax = 6.25 × 0.19 = 1.1875
Net Interest = 6.25 - 1.1875 = 4.0625
Principal = 1,000
Distribution:
| Account | Before | Receipt | After |
|---|---|---|---|
| Cash | €0 | +€1,000 principal | €1,000.00 |
| OKO | €0 | +€4.0625 net int. | €4.0625 |
| Tax Paid | €0 | +€1.1875 | €1.1875 |
Reinvestment:
Available = 1000 + 4.0625 = 1004.0625
New Bonds = floor(1004.0625 / 100) = 10
Cash After = 4.0625 (remainder)
OKO After = 0 (merged into purchase)
| Account | After Reinv. |
|---|---|
| Cash | €4.0625 |
| OKO | €0 |
| Bonds | 10 (active) |
Quarter 2 (Month 6)¶
Redemption of Q1 batch:
Gross Interest = 6.25
Tax = 1.1875
Net Interest = 4.0625
Principal = 1,000
Distribution:
| Account | Before | Receipt | After |
|---|---|---|---|
| Cash | €4.0625 | +€1,000 principal | €1,004.0625 |
| OKO | €0 | +€4.0625 net int. | €4.0625 |
| Tax Paid | €1.1875 | +€1.1875 | €2.375 |
Reinvestment:
Available = 1004.0625 + 4.0625 = 1008.125
New Bonds = floor(1008.125 / 100) = 10
Cash After = 8.125 (remainder, growing!)
OKO After = 0
Quarter 3 (Month 9)¶
By now, the cash remainder continues growing every quarter. Eventually it reaches €100 and can buy an extra bond.
Quarter 4 (Month 12)¶
Final settlement and consolidation:
At the final month (month 12), before computing final valuation:
Final Cash = (accumulated cash remainder)
Final OKO = (accumulated interest if not yet reinvested)
Final Consolidation:
Final Cash += Final OKO
Final OKO = 0
This ensures the final net value properly accounts for all accumulated interest.
Invariants Maintained¶
At Every Month End¶
graph LR
GV["Gross Value<br/>Cash + OKO +<br/>Bonds Value +<br/>Accrued Interest"]
LV["Liquidation Value<br/>Cash + OKO +<br/>Bonds Value +<br/>Accrued Interest -<br/>Early Cost - Tax"]
GV -->|must be ≥| LV
TB["Tax Paid<br/>must be ≥ 0"]
OB["OKO Balance<br/>must be ≥ 0"]
CB["Cash<br/>must be ≥ 0"]
TB --> TB
OB --> OB
CB --> CB
Period Totals¶
totalTaxPaid= sum of allmonthTaxPaidtotalCashAccountTaxPaid= sum of all taxes on interesttotalCashAccountInterestPaid= sum of all gross interest earned- Final values match last monthly snapshot
Tax Impact Timeline¶
xychart-beta
title "Tax Accumulation Over 12 Months (€1,000)"
x-axis ["Mo0", "Mo3", "Mo6", "Mo9", "Mo12"]
y-axis "Cumulative Tax Paid (€)" 0 --> 2.50
line [0, 1.19, 2.38, 3.57, 4.75]
note "Q1 settlement: 1.1875€ tax"
note "Q2 settlement: +1.1875€ more"
note "Running total grows linearly"
Design Rationale¶
Why Separate Accounts?¶
- Transparency - Makes interest distinct from principal in reports
- Flexibility - Future strategies can implement different interest rules
- Accuracy - Prevents interest from "hiding" in cash pool
- Tax Reporting - Tax on interest is explicitly tracked
Why Consolidate at Final Month?¶
If OKO balance persisted in separate account at final month: - ❌ Final net value would be understated - ❌ UI would need special logic for OKO presentation - ❌ Liquidation value inconsistent
By consolidating: - ✅ Final cash accurately reflects all realized value - ✅ Gross and liquidation semantics remain clean - ✅ End-of-simulation behavior is clear
Why Both Sources Feed Reinvestment?¶
Interest earned should stay invested: - Better aligns with compound growth expectations - Matches user mental model of "reinvestment" - Prevents artificial cash leakage
Related Documentation¶
- OTS_logic.md - Full engine specification
- ots.ts - Implementation
- ots.strategy.test.ts - Test coverage