Calculation catalog › Accounts and growth

Income taxable yield annual

Taxable account income recognized in a projection year.

Field: Taxable investment yield Unit: US dollars Basis: nominal dollars Computed by: Engine Status: Complete chain

Appears in: chart (taxableYield); csv (taxableYield); scenarios-page (Lifetime income "Taxable yield"); cash-flow-drilldown (taxableAccountYield source line)

Calculation records

  • Annual taxable yield distributed by a taxable account Formula record, justified by derivation

    Purpose: Interest plus all dividends, the single taxable-yield figure the income total counts.

    Statement: projection/internal/distributedTaxableYieldRows.ts#distributedTaxableYieldRows computes, for each taxable account whose start-of-year balance is strictly positive, taxableGross = interest + dividends, and projection/internal/annualIncomeSetup.ts#annualIncomeSetup folds those rows into the published projection/internal/types/result.ts#YearResult.incomes.taxableYield. Qualified and ordinary dividends partition dividends and are not added again; federally tax-exempt interest is computed on the same balance from taxExemptInterestYieldPct but is a separate published member and never joins taxable yield. Units: nominal USD per year. Rounding: none.

    Formula: taxableYield = startBalance * interestYieldPct / 100 + startBalance * dividendYieldPct / 100

    • startBalance: Start-of-year balance, the prior year's close (unit: usd) (domain: strictly positive, else no row)
    • interestYieldPct, dividendYieldPct: Account yields in percent of the start balance (unit: percent/year) (domain: nonnegative)
    • taxExemptInterestYieldPct: Municipal sleeve yield, published separately (unit: percent/year) (domain: nonnegative; never in taxable yield)

    Timing: once per taxable account per projection year, before the wage pass

    Rounding: none

    Limits:

    • Asserted on two real simulatePlan runs of the projection's first year, where the start-of-year balance is the plan balance. Plan assumptions beyond the worksheet's inputs: a single 51-year-old filing single in KY with a zero state rate, zero inflation, zero account return and cost basis equal to balance; the account carries no allocation, so the defaults case exercises the no-blend fallback
    • The dividend partition is asserted alongside the total, because the worksheet's first wrong reading is adding the partition back on top of its own parent
    • Taxable yield joins the income total whether or not it is reinvested; reinvestment only decides whether the same dollars reach the household cash hub

    Provenance: derived by codex, implemented by claude.

    Independent review: cursor

    Gates (6 of 6 pass): fixture registers a test: pass; pins resolve: pass; families exist: pass; worksheet exists: pass; mutation receipt exists: pass; provenance independent: pass

    Worksheet: income-taxable-yield-annual.md Mutation receipt: income-taxable-yield-annual.mutation.md

    Evidence tests: distributedTaxableYieldRows.evidence.test.ts

    Implemented in: distributedTaxableYieldRows.ts; annualIncomeSetup.ts; result.ts

    Is the number of: income-taxable-yield-annual

    Feeds: income-total-annual

Records that feed this number

  • Blended taxable yields and dividend-weighted qualified share Formula record, justified by derivation

    Purpose: The interest yield, dividend yield and qualified share a taxable account's allocation implies.

    Statement: For weights w and class parameters: interestYieldPct = sum of w_i x interestYieldPct_i; dividendYieldPct = sum of w_i x dividendYieldPct_i; qualified = sum of w_i x dividendYieldPct_i x qualifiedRatioPct_i/100; qualifiedRatio = qualified/dividendYieldPct when dividendYieldPct > 0, else DEFAULT_QUALIFIED_DIVIDEND_RATIO = 0.85. Units: the two yields in percent of balance per year; the ratio a fraction in [0, 1]. Rounding: none. In the ledger an account-level interestYieldPct, dividendYieldPct or qualifiedRatio overrides the blend field by field, and the published rows are start-of-year balance x yield/100.

    Formula: I = sum_i w_i I_i; D = sum_i w_i D_i; Q = (sum_i w_i D_i q_i/100) / D when D > 0, else 0.85

    • w_i: Fraction of the account in class i (unit: 1) (domain: w_i >= 0)
    • I_i, D_i: Interest and dividend yield of class i (unit: percent of balance/year) (domain: >= 0)
    • q_i: Qualified share of class i's dividends (unit: percent) (domain: 0 <= q_i <= 100)
    • I, D: Blended interest and dividend yields (unit: percent of balance/year) (domain: >= 0)
    • Q: Dividend-dollar-weighted qualified share (unit: 1) (domain: 0 <= Q <= 1)

    Timing: annual; applied to the start-of-year balance

    Rounding: none

    Limits:

    • The qualified share is dividend-dollar-weighted, so a class with no dividends contributes nothing to the ratio however large its weight
    • A blend with zero dividends returns the 0.85 fallback rather than 0 or NaN; the fallback is a convention for the unallocated case, not a sourced class parameter
    • Weights are taken as given, not normalized
    • The published income rows are balance x yield in the ledger (distributedTaxableYieldRows.ts), so this record feeds those families and is not their value

    Provenance: derived by codex, implemented by claude-subagent.

    Independent review: cursor

    Gates (6 of 6 pass): fixture registers a test: pass; pins resolve: pass; families exist: pass; worksheet exists: pass; mutation receipt exists: pass; provenance independent: pass

    Worksheet: allocation-blended-taxable-yield.md Mutation receipt: allocation-blended-taxable-yield.mutation.md

    Evidence tests: assetClasses.evidence.test.ts

    Implemented in: assetClasses.ts

    Feeds: income-taxable-yield-annual, income-taxable-interest-annual, income-qualified-dividends-annual, income-ordinary-dividends-annual

  • Glidepath compilation: linear interpolation with flat endpoints, staged as a step function Model record, justified by derivation

    Purpose: This year's target weight vector for an allocated account under any of the four policy modes.

    Statement: static: weightsToVector(weights). linear: from = weightsToVector(policy.from), to = weightsToVector(policy.to); year <= startYear or endYear <= startYear returns from; year >= endYear returns to; otherwise lerp(from, to, t) with t = (year - startYear)/(endYear - startYear), where lerp mixes componentwise a_i + (b_i - a_i) t and renormalizes the mix to sum 1 when its sum is positive. staged: stages sorted by fromYear; the target is the last stage whose fromYear <= year, or the earliest stage when none has started. custom: targets sorted by year; a year at or before the first target returns its vector and at or after the last returns the last; otherwise the first bracketing pair lo, hi with lo.year <= year <= hi.year is lerped at t = (year - lo.year)/(hi.year - lo.year). Units: fractions in ASSET_CLASS_IDS order. Rounding: none.

    Formula: w(y) = a + t (b - a), t = (y - y0)/(y1 - y0), renormalized to sum 1; w(y) = a for y <= y0; w(y) = b for y >= y1; staged: w(y) = weights of the last stage with fromYear <= y

    • y: Calendar year being compiled (unit: year) (domain: integer, schema 1900..2200)
    • y0, y1: Glidepath start and end years (or a bracketing custom pair) (unit: year) (domain: y0 < y1 for interpolation)
    • a, b: Normalized endpoint vectors (unit: 1) (domain: each sums to 1)
    • t: Interior fraction of the way from y0 to y1 (unit: 1) (domain: 0 < t < 1)
    • w(y): Target weight vector for the year (unit: 1) (domain: sums to 1)

    Timing: annual; one target per calendar year

    Rounding: none

    Limits:

    • Endpoints clamp flat; a linear or custom policy is never extrapolated
    • Linear and custom mixes are renormalized after interpolation, which changes nothing when both endpoints already sum to 1
    • A degenerate linear policy (endYear <= startYear) holds the from vector for every year
    • A staged policy before its first stage year holds the earliest stage; a custom policy with one target holds it for every year
    • lerpVectors is module-private; the evidence reaches it through targetWeightsAt
    • The worksheet names bucket-lens-allocation upstream; the bucket lens (its own record) reads the published investable total and net portfolio need, refusing a year whose need is not a finite number, and is not listed here

    Provenance: derived by codex, implemented by claude-subagent.

    Independent review: grok

    Gates (6 of 6 pass): fixture registers a test: pass; pins resolve: pass; families exist: pass; worksheet exists: pass; mutation receipt exists: pass; provenance independent: pass

    Worksheet: allocation-glidepath-interpolation.md Mutation receipt: allocation-glidepath-interpolation.mutation.md

    Evidence tests: assetClasses.evidence.test.ts

    Implemented in: assetClasses.ts

    Feeds: income-taxable-yield-annual, accounts-balance-per-account-annual

  • Asset-class parameters: assumption overrides laid over the sourced defaults Composition record, justified by derivation

    Purpose: The per-class return, volatility, yield and qualified-share figures every allocation blend reads.

    Statement: For each class id in ASSET_CLASS_IDS order (usStocks, intlStocks, bonds, cash) the resolved record is { label: the default label; returnPct, volatilityPct, interestYieldPct, dividendYieldPct, qualifiedRatioPct: the override object's field when the overrides name that class and supply that field (o?.field ?? d.field), else the sourced default }. An absent overrides object, an absent class and an absent field each fall through to the default; the label cannot be overridden. Units: returnPct, volatilityPct, interestYieldPct and dividendYieldPct in percent per year; qualifiedRatioPct in percent. Rounding: none; every value passes through unchanged.

    Formula: resolved[c].f = override[c]?.f ?? default[c].f for f in {returnPct, volatilityPct, interestYieldPct, dividendYieldPct, qualifiedRatioPct}; resolved[c].label = default[c].label

    • c: Asset class id (unit: enum) (domain: usStocks | intlStocks | bonds | cash)
    • default[c]: Sourced planning default record for the class (DEFAULT_ASSET_CLASS_PARAMS) (unit: mixed) (domain: fixed)
    • override[c]: Optional per-class assumption overrides, each field optional (unit: mixed) (domain: schema-bounded)
    • resolved[c]: The record the blends read (unit: mixed) (domain: every field present)

    Timing: resolved once per simulation and once per Monte Carlo run; time-invariant

    Rounding: none

    Limits:

    • Overrides apply field by field: an override object cannot remove a class or blank a field, and the label is never overridable
    • The sourced defaults (7/7/4/2.5 percent returns, 19.6/21/7.7/0.5 volatilities, the yields and qualified shares) are planning conventions from domain rules section 15, not forecasts; this record pins the overlay, not the defaults
    • No validation here: the plan schema bounds each override field before it reaches the function
    • The worksheet asks for exactness at one-decimal precision; the test compares with an absolute bound of 0 because the helper's exact keyword is reserved for integers, and pass-through doubles are equal bit for bit

    Provenance: derived by codex, implemented by claude-subagent.

    Independent review: cursor

    Gates (6 of 6 pass): fixture registers a test: pass; pins resolve: pass; families exist: pass; worksheet exists: pass; mutation receipt exists: pass; provenance independent: pass

    Worksheet: asset-class-parameter-overrides.md Mutation receipt: asset-class-parameter-overrides.mutation.md

    Evidence tests: assetClasses.evidence.test.ts

    Implemented in: assetClasses.ts

    Feeds: accounts-balance-per-account-annual, income-taxable-yield-annual, monte-carlo-investable-fan-percentiles

Engine source: engine/src/projection/internal/types/result.ts (YearResult.incomes.taxableYield)

The evidence links on this page (each record's worksheet, mutation receipt, evidence tests and implementation, and the source links) are pinned to engine commit 2624dbdba9fd, committed 2026-10-11. The live calculation index and output-family census links point at the repository's main branch, so you can check this snapshot against them.