fintech-algorithms
Using a coding agent? Give it the skill: npx skills add IslamBaraka90/Fintech-Algorithms-Library What it does →

Residual-Income Model

Install and import#

bash
npm install fintech-algorithms
ts
import { residualIncomeModel } from "fintech-algorithms/fundamental-analysis-and-valuation/intrinsic-valuation/residual-income-model";

Signature#

residualIncomeModel(data)

Rolls per-share book value forward through a forecast, charges each period's opening book value at the cost of equity, discounts the residual income that remains, and adds opening book value plus a growing-perpetuity continuing value to reach intrinsic value per share.

Parameters#

NameTypeNotes
data{ opening_book_value_per_share: number; earnings_per_share: number[]; dividends_per_share: number[]; nonowner_equity_adjustments_per_share: number[]; cost_of_equity: number; terminal_residual_income_growth: number }opening_book_value_per_share starts the roll-forward, which each period adds earnings_per_share, subtracts dividends_per_share and adds nonowner_equity_adjustments_per_share. Those three arrays must be the same length. cost_of_equity sets both the equity charge on opening book value and the discount rate, and terminal_residual_income_growth grows the last period's residual income into the continuing value. Other keys in the record are not read.

Returns#

{ state: string; method: string; schedule: { period: number; opening_book_value_per_share: number; earnings_per_share: number; dividends_per_share: number; nonowner_equity_adjustment_per_share: number; equity_charge_per_share: number; residual_income_per_share: number; discount_factor: number; present_value: number; closing_book_value_per_share: number }[]; pv_explicit_residual_income: number; terminal_residual_income_per_share: number; continuing_value: number; terminal_discount_factor: number; pv_continuing_value: number; continuing_value_share: number; closing_book_value_per_share: number; intrinsic_value_per_share: number }

A schedule row per period showing the book-value roll-forward, its equity_charge_per_share, residual_income_per_share, discount_factor and present_value. Then pv_explicit_residual_income, the continuing block (terminal_residual_income_per_share, continuing_value, terminal_discount_factor, pv_continuing_value), the final closing_book_value_per_share, and intrinsic_value_per_share — opening book value plus both present values. continuing_value_share is the continuing share of that total, emitted as 0 when the total is 0.

Errors#

  • When data is absent, an array, or not an object — throws Error
  • When opening_book_value_per_share is not a positive finite number — throws Error
  • When any of the three per-share arrays is not an array holding at least one finite number — throws Error
  • When dividends_per_share or nonowner_equity_adjustments_per_share differs in length from earnings_per_share — throws Error
  • When cost_of_equity is not strictly between 0 and 1 — throws Error
  • When terminal_residual_income_growth is at or below -1, or at or above cost_of_equity — throws Error
  • When a period's dividends_per_share is negative — throws Error
  • When the book-value roll-forward reaches a non-positive closing balance — throws Error

Complexity: time O(n), space O(n).

Worked example#

executed Captured by running this function on the input its own test provides. Real output of real code — but not asserted against a published figure.

Input#

data
{
  "valuation_date": "2026-08-04",
  "currency": "USD",
  "opening_book_value_per_share": 30,
  "earnings_per_share": [3.6, 4, 4.35, 4.65, 4.9],
  "dividends_per_share": [1.2, 1.35, 1.5, 1.65, 1.8],
  "nonowner_equity_adjustments_per_share": [0, 0, 0, 0, 0],
  "cost_of_equity": 0.1,
  "terminal_residual_income_growth": 0.03
}

Call#

residualIncomeModel(data)

Returns#

object with 11 fields: state, method, schedule, pv_explicit_residual_income, terminal_residual_income_per_share, continuing_value, terminal_discount_factor, pv_continuing_value, …

{
  "state": "valued",
  "method": "residual-income",
  "schedule": [
    {
      "period": 1,
      "opening_book_value_per_share": 30,
      "earnings_per_share": 3.6,
      "dividends_per_share": 1.2,
      "nonowner_equity_adjustment_per_share": 0,
      "equity_charge_per_share": 3,
      "residual_income_per_share": 0.6000000000000001,
      "discount_factor": 0.9090909090909091,
      "present_value": 0.5454545454545455,
      "closing_book_value_per_share": 32.4
    },
    {
      "period": 2,
      "opening_book_value_per_share": 32.4,
      "earnings_per_share": 4,
      "dividends_per_share": 1.35,
      "nonowner_equity_adjustment_per_share": 0,
      "equity_charge_per_share": 3.24,
      "residual_income_per_share": 0.7599999999999998,
      "discount_factor": 0.8264462809917354,
      "present_value": 0.6280991735537188,
      "closing_book_value_per_share": 35.05
    },
    {
      "period": 3,
      "opening_book_value_per_share": 35.05,
      "earnings_per_share": 4.35,
      "dividends_per_share": 1.5,
      "nonowner_equity_adjustment_per_share": 0,
      "equity_charge_per_share": 3.505,
      "residual_income_per_share": 0.8449999999999998,
      "discount_factor": 0.7513148009015775,
      "present_value": 0.6348610067618329,
      "closing_book_value_per_share": 37.9
    }
  ],
  "pv_explicit_residual_income": 2.898752569061974,
  "terminal_residual_income_per_share": 0.8343000000000005,
  "continuing_value": 11.918571428571434,
  "terminal_discount_factor": 0.6209213230591549,
  "pv_continuing_value": 7.400495140403617,
  "continuing_value_share": 0.18363854317472458,
  "closing_book_value_per_share": 44,
  "intrinsic_value_per_share": 40.29924770946559
}

Other exports#

This module also exports calculate, freeCashFlowDcf, dividendDiscountModel, gordonGrowthModel, economicValueAdded. Every module additionally exports run as an alias of its primary function, and a meta object carrying its catalog id, domain, family, shape and article URL.

Diagrams#

Residual-Income Model — article hero
Residual-Income Model — model boundaries
Residual-Income Model — reconciliation bridge
Residual-Income Model — sensitivity map
Residual-Income Model — system map
Residual-Income Model — valuation timeline

How it works#

This page states the contract — how to call it correctly. The article explains the concept: why it works, and where it breaks.

Read the article →

References#

The rest of the Intrinsic Valuation family#