Generated file. Source:
docs/result_model_contract.mdEdit the source document and runnpm run docs:syncto refresh this published copy.
Result Model Contract¶
This document defines the canonical public result contract produced by the bond engine and consumed by UI, exports, saved simulations, and future data-layer features.
Goal¶
The result model is not just a UI payload.
It is a domain contract that must:
- provide a stable cross-strategy output shape
- preserve enough resolution for future portfolio and persistence features
- support charts, summaries, yearly tables, CSV export, and future assistant flows
- remain independent from any single presentation component
Architectural Position¶
The result model sits between:
- bond engine internals and strategy-specific simulation details
- presentation adapters such as charts, cards, tables, and exports
- future persistence contracts for saved simulations and portfolio history
Simplified flow:
flowchart LR
A[Selection and normalized inputs] --> B[Bond engine]
B --> C[Strategy internals]
C --> D[Canonical result model]
D --> E[Analysis models]
E --> F[UI views]
D --> G[CSV export]
D --> H[Saved simulations]
D --> I[Future portfolio and analytics]
Current Contract Shape¶
The current canonical result contract is represented in code by:
YearlyResultBondCalculationResultBondSimulationDetails
Source of truth in code:
src/app/domain/result-model/types.ts
Related modules:
src/app/domain/result-model/view-adapters.tssrc/app/domain/persistence/types.tsdocs/analysis_models_architecture.md
Level 1. Yearly Result Contract¶
Each supported bond strategy must produce a normalized yearly series.
YearlyResult¶
Fields:
year- 1-based investment year index used for yearly chart and yearly table alignment
grossValue- year-end gross value before all taxes and costs realized so far are subtracted from the current snapshot semantics
taxPaid- tax realized in the given year only
earlyRedemptionCost- early-redemption cost realized in the given year only
netValue- year-end net value according to the semantics of the strategy family
nominalProfit- nominal profit versus initial invested amount
realValuenetValueadjusted by inflation assumed up to that yearrealProfit- real profit versus initial invested amount
isRedemption- whether the year contains a redemption event visible in the public yearly semantics
isEarlyRedemption- whether the redemption represented in that yearly row is early rather than natural maturity
Level 2. Final Bond Result Contract¶
Each selected bond must produce one normalized final result.
BondCalculationResult¶
Fields:
bondId- stable product identifier
bondName- presentation-ready public name
yearlyResults- normalized yearly series described above
finalGrossValue- final gross value derived from the last public yearly result
totalTaxPaid- sum of all realized tax postings during the whole simulation
totalEarlyRedemptionCosts- sum of all early-redemption costs realized during the whole simulation
finalNetValue- final net value for the investment horizon
totalNominalProfit- final nominal profit for the full investment horizon
totalRealProfit- final real profit for the full investment horizon
irr- effective rate based on current contract assumptions
cagr- compound annual growth rate based on final net value
simulationDetails- optional event ledger for richer inspection and future persistence
Level 3. Optional Simulation Event Ledger¶
The canonical result may include an auxiliary event ledger via BondSimulationDetails.
This part is optional because not every strategy exposes the same event families.
Possible event groups:
purchaseEventsreinvestmentDecisionsredemptionEventspayoutEvents
This event ledger is currently a bridge between:
- strategy internals
- richer UI diagnostics
- future persistence and portfolio/history features
It should be treated as an extension of the result contract, not as a replacement for the canonical yearly/final result.
Contract Semantics¶
The most important rule:
- the result model is strategy-normalized, but not strategy-erasing
That means:
- all strategies must return the same public shape
- but field semantics may still depend on the strategy family, especially for intermediate yearly rows
Examples:
- intermediate
netValuein accumulation families is a carried portfolio value - final
netValueis the liquidation value for the investment horizon grossValuemay include already realized taxes and costs in order to preserve a comparable public meaning across yearly snapshots
In practice, the calculator should be read as simulating the real progression of the investment. Earlier yearly rows usually show the portfolio during the investment rather than a fully comparable liquidation answer to the question: "how much would I receive if I closed the investment exactly in that year?"
The alternative scenario, where the simulation explicitly compares profitability under the assumption that the investment is ended in each successive year, should be treated as a separate product interpretation layer. In the current product this exists as an explicit global analysis mode exposed through shared UI controls in the chart and yearly-table sections, rather than as an implicit promise of the canonical yearly-result semantics.
This means the canonical result contract remains the source of truth for the real progression of the investment, while any alternative interpretation should be built in the dedicated analysis-model layer described in analysis_models_architecture.md.
Boundary table:
| Layer | What it owns | What it must not own |
|---|---|---|
Canonical result |
real progression semantics, stable engine output, cross-product source of truth | UI-local labels, alternative interpretation-specific fields |
Analysis model |
explicit interpretation or recomputation, e.g. investment_progression, year_by_year_liquidation |
layout concerns, section-specific presentation details |
View adapter |
mapping analysis output to chart, summary, or table view models | engine semantics, hidden business reinterpretation |
These semantics must stay aligned with:
- family strategy docs
- product-specific strategy docs
- product specification section on interpreting results
What This Contract Must Support¶
The canonical result model should be judged against future product requirements from the roadmap.
It must be rich enough to support:
- current chart and summary views
- current yearly table and CSV export
- saved simulations
- comparison of historical predictions vs actual values
- future portfolio-oriented reconstruction
- future assistant flow that still lands on the same result semantics
- recurring investing and account-type expansion
What This Contract Should Not Become¶
The canonical result model should not become:
- a bag of UI-only convenience fields
- a direct dump of raw strategy internals
- a storage model tightly coupled to one persistence engine
- a substitute for family-specific semantic docs
Current Architectural Assessment¶
Current state:
- the project already has a shared canonical result shape in code
- strategies already converge to one public contract
- canonical result types live in
src/app/domain/result-model/types.ts - thin presentation adapters live in
src/app/domain/result-model/view-adapters.ts - UI consumes the result semantics through the canonical result, the analysis-model layer, and dedicated adapters across chart, summary, and yearly table
Current architectural caution:
- the canonical result must continue to be treated as a stable architectural boundary rather than just a convenient collection of shared types
- adapters between canonical result, analysis models, and future view-specific models should remain explicit as the product grows
- interpretation-specific fields must continue to stay out of the canonical result contract
Recommended Evolution Path¶
Short term¶
- keep
BondCalculationResultas the current public contract - document its semantics centrally
- align all strategy docs with this contract
Medium term¶
- keep result types in the dedicated
result-modelmodule - separate:
- canonical result contract
- strategy event details
- view adapters
- explicitly define which fields are persistence-ready and which are presentation-derived
- keep persistence contracts distinct from canonical runtime results
Longer term¶
- add persistence-oriented and analytics-oriented contracts on top of the canonical result model
- make historical reconstruction and portfolio aggregation rely on explicit domain contracts instead of UI structures
Maintenance Rules¶
When changing the result model:
- update the canonical contract documentation first
- update the code types
- update all affected strategy families
- update UI consumers and export adapters
- update tests covering result semantics
- update product docs if user-facing interpretation changes
Documents That Must Stay Aligned¶
docs/bond_engine_architecture.mddocs/analysis_models_architecture.mddocs/data_persistence_contracts.mddocs/annual_accumulation_logic.mddocs/annual_payout_logic.md- per-bond strategy docs
docs/product_spec.md- UI components consuming results