Skip to content

Generated file. Source: docs/result_model_contract.md Edit the source document and run npm run docs:sync to 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:

  • YearlyResult
  • BondCalculationResult
  • BondSimulationDetails

Source of truth in code:

  • src/app/domain/result-model/types.ts

Related modules:

  • src/app/domain/result-model/view-adapters.ts
  • src/app/domain/persistence/types.ts
  • docs/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
  • realValue
  • netValue adjusted by inflation assumed up to that year
  • realProfit
  • 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:

  • purchaseEvents
  • reinvestmentDecisions
  • redemptionEvents
  • payoutEvents

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 netValue in accumulation families is a carried portfolio value
  • final netValue is the liquidation value for the investment horizon
  • grossValue may 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

Short term

  • keep BondCalculationResult as the current public contract
  • document its semantics centrally
  • align all strategy docs with this contract

Medium term

  • keep result types in the dedicated result-model module
  • 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:

  1. update the canonical contract documentation first
  2. update the code types
  3. update all affected strategy families
  4. update UI consumers and export adapters
  5. update tests covering result semantics
  6. update product docs if user-facing interpretation changes

Documents That Must Stay Aligned

  • docs/bond_engine_architecture.md
  • docs/analysis_models_architecture.md
  • docs/data_persistence_contracts.md
  • docs/annual_accumulation_logic.md
  • docs/annual_payout_logic.md
  • per-bond strategy docs
  • docs/product_spec.md
  • UI components consuming results