This explains, in plain language, how this tool turns the numbers you enter into the results on the dashboard. It's the content behind every "Show calculation background" panel (SPEC.md §22) — written so a hospital administrator or a CFO's finance team can follow the logic without reading code. For the exact formula behind each step, see report-templates/formula-appendix.md; for term definitions, see content/glossary.md.
All figures below are illustrative only — a hypothetical MRI scenario used to show how the pieces connect, not a real benchmark or recommendation.
1. From usage to revenue
Everything starts with how much the equipment is used and what it's billed at.
Billed monthly revenue = Usage per day × Billed revenue per use × Working days per month
= 25 × ₹7,500 × 25
= ₹46,87,500That's the sticker-price view — what you'd bill if every patient paid the full rate in full, on time. It's what Basic Mode shows by default, because it's the number you can estimate before you know your exact payer mix.
Realized revenue brings in the fact that different payer types (private cash, insurance/TPA, corporate, government scheme) pay different effective rates, once scheme discounts and claim disallowances are accounted for:
Realized revenue per use = weighted average, across payer types, of:
(that payer's share of your volume) × (their billed tariff) × (their realization %)
Monthly realized revenue = Usage per day × Realized revenue per use × Working days per month
= ₹42,18,750 [illustrative, at 90% blended realization]Realized revenue is always less than or equal to billed revenue. The gap between them is real money the hospital never collects — scheme discounts, disallowed claims, write-offs — not a rounding difference.
2. From realized revenue to cash in hand
Realized revenue isn't cash the moment it's earned — each payer type takes a different number of days to actually pay (DSO). A private cash patient might pay same-day; a government scheme might take 60-90 days.
Cash received in a given month = realized revenue from earlier months, shifted forward
in time according to each payer type's own DSOThe gap between cumulative realized revenue and cumulative cash actually received is the working capital gap — the cash a hospital needs to have on hand to bridge the wait, separate from whether the equipment is profitable on paper.
3. Costs
Costs split into two kinds, because they behave differently as volume changes:
Variable cost per use = Consumables + Professional/reporting fee + Other per-use cost
Monthly variable cost = Usage per day × Variable cost per use × Working days per month
Fixed operating cost = Staff cost + Utilities + Fixed maintenance allocation + OtherMaintenance is not flat over the equipment's life. It follows a schedule:
Years 1 to (warranty years) → covered by warranty, cost ₹0
Years (warranty+1) to (warranty + CMC years) → CMC cost applies
Years after that → AMC cost appliesThe jump from ₹0 (warranty) to a real annual cost (CMC, then AMC) is the maintenance cliff — a common source of over-optimistic multi-year projections if a model just uses Year 1's cost for every year.
4. Surplus, EMI, and cash flow after financing
Monthly operating surplus = Monthly realized revenue − (Monthly variable cost + Monthly fixed cost)If the equipment is financed (loan or lease), the EMI is subtracted next:
EMI = Principal × monthlyRate × (1 + monthlyRate)^tenure ÷ ((1 + monthlyRate)^tenure − 1)
Cash flow after EMI = Cash received − Operating cash expenses − EMIIf there's a launch delay before revenue starts (civil work, installation, licensing, training) and the equipment is financed, interest still accrues during that window with nothing yet to offset it:
Pre-operative interest = Principal × (Annual interest rate ÷ 12 ÷ 100) × Launch delay in months5. The core outputs: payback, ROI, NPV, IRR
Once a full year-by-year (or month-by-month) net cash-flow series exists, the headline numbers all derive from it:
Payback period = Initial investment ÷ Annual net cash flow
(or, from a multi-year series: the point cumulative cash flow first
reaches the initial investment, interpolated within that year)
ROI = (Annual net return ÷ Initial investment) × 100
NPV = (Σ each year's cash flow, discounted back to today at the discount rate)
− Initial investment
IRR = the discount rate at which NPV would equal exactly zeroDiscounted payback applies the same discounting NPV uses before finding the payback point — always a longer period than simple payback, since money in the future is worth less than money today.
Break-even usage per day answers a different question: not "is this profitable at my expected volume," but "what's the minimum volume before it's profitable at all":
Contribution per use = Realized revenue per use − Variable cost per use
Break-even usage per day = Fixed monthly cost ÷ Contribution per use ÷ Working days per month6. Comparing options: EAC
Equivalent Annual Cost (EAC) answers a different kind of question than payback/ROI — not "is this a good investment" but "if I strip out revenue, what does owning and running this equipment cost me per year, on a comparable basis":
EAC = (discounted total lifetime cost, purchase + operating + maintenance + financing)
÷ annuity factor (a function of discount rate and useful life)This is what makes a 10-year MRI and a 5-year ultrasound machine, or a cash purchase and a lease, comparable side by side — each collapses to one number per year, on the same footing regardless of how long the equipment lasts or how it's financed.
7. The Investment Outlook score
The dashboard's single 0-100 score is a weighted lens on the numbers already shown above — not a separate, hidden calculation. It exists so a busy administrator gets one number to anchor on, while the finance team can still see exactly what it's built from.
Four components, each scored 0-100 independently, then combined by weight:
Return Strength (35%) — how far IRR sits above the discount rate
Speed to Payback (25%) — how fast discounted payback happens, relative
to the equipment's own useful life
Financing Resilience (20%) — DSCR: does operating cash flow comfortably
cover the EMI? (skipped for cash purchases,
weight redistributed to the other three)
Operational Margin of Safety (20%) — how much cushion exists between expected
usage and break-even usageComposite score = 0.35 × Return Strength + 0.25 × Speed to Payback
+ 0.20 × Financing Resilience + 0.20 × Operational Margin of Safety
Bands: Strong 75-100 · Moderate 55-74 · Caution 35-54 · Weak 0-34Illustrative worked example (continuing the numbers above, plus some additional assumptions not shown earlier — not a real scenario):
IRR 18.2%, discount rate 12.5% → Return Strength ≈ 78.5
Discounted payback 3.8 years, useful life 10 years → Speed to Payback = 70
Monthly cash flow before EMI ₹4,50,000, EMI ₹3,20,000 (DSCR 1.41×) → Financing Resilience = 41
Usage 25/day, break-even 20.5/day (18% cushion) → Operational Margin of Safety = 45
Composite = 0.35×78.5 + 0.25×70 + 0.20×41 + 0.20×45 ≈ 62 → "Moderate"
Driver: Financing Resilience is the lowest sub-score, so the dashboard shows:
"Main risk: EMI is consuming a large share of monthly operating cash flow
relative to lenders' typical comfort margin (DSCR 1.41×)."The driver line is always generated from the actual lowest-scoring component and its real underlying number — never separately written copy that could drift from what the score is actually measuring. Full methodology and every anchor-point's rationale live in financial-model-spec.md §1.
8. Sensitivity analysis and the automatic actionable insight
Sensitivity analysis re-runs the entire waterfall above under a different set of assumptions (lower utilization, lower realization %, a different financing structure) so you can see how exposed the result is to any one assumption being wrong, rather than trusting a single point estimate.
A special case of this — the automatic actionable insight — runs silently in the background on every scenario: it tests a grid of modest tariff increases (2% to 15%) starting at different future years, and surfaces the single smallest price change that would improve payback by at least 6 months. If no realistic price change clears that bar, nothing is shown — silence is the normal, expected result, not a fallback state. Full grid, gate, and selection logic in financial-model-spec.md §4 and report-templates/formula-appendix.md §6.2.
A note on what this methodology does and doesn't guarantee
Every calculation above is deterministic and auditable — the same inputs always produce the same outputs, and every number can be traced back to the formula that produced it. What it can't do is know whether your inputs are accurate. See content/benchmark-notes.md for how to read the confidence level behind any default value, and report-templates/disclaimer.md before using any output to support an actual capital decision.
Formula Appendix
This is the authoritative, plain-language reference for every formula in /formulas. Every entry below matches the actual implementation exactly (file, function, and logic) — this document is what the code is tested against, not the other way around. Terms in bold are defined in content/glossary.md; this file doesn't redefine them.
Ships as an appendix on the Word report and as the "Formulas" sheet in the Excel export, so a CFO or auditor can trace any headline number back to its exact calculation without reading TypeScript.
1. Revenue
1.1 Billed monthly revenue
formulas/revenue.ts — billedMonthlyRevenue()
Billed monthly revenue = Usage per day × Average billed revenue per use × Working days per month1.2 Realized revenue per use
formulas/realization.ts — realizedRevenuePerUse()
Realized revenue per use = Σ over payer types of:
(payer's share of volume ÷ 100) × payer's billed tariff × (payer's realization % ÷ 100)A volume-weighted average across every payer type in the mix (private cash, insurance/TPA, corporate credit, PM-JAY/government scheme, other), each with its own tariff and realization %.
1.3 Monthly realized revenue
formulas/revenue.ts — monthlyRealizedRevenue()
Monthly realized revenue = Usage per day × Realized revenue per use × Working days per month1.4 Cash received by month
formulas/dso.ts — cashReceivedByMonth()
For each payer type, that payer's share of a month's realized revenue is added to the
cash-received total in the month it's actually collected:
collection month = revenue month + ceil(payer's days-to-collect ÷ 30)Each payer type can have a different DSO, so a single month's realized revenue splits across several future collection months rather than landing all at once. The output series extends past the input series by the largest payer's collection delay, in months.
Consuming this series (added 2026-07-13, PBA-3): always sum/discount the full extended series when computing NPV, IRR, the annual cash-flow summary, or the working-capital gap — never truncate it to the original projection-horizon length first. Cash is conserved over the full series (total received = total realized revenue); truncating drops the tail and turns a temporary collection delay into what looks like a permanent loss. See SPEC.md §14.4 for the full contract.
2. Costs
2.1 Contribution per use
formulas/breakEven.ts — contributionPerUse()
Contribution per use = Realized revenue per use − Variable cost per use2.2 Break-even usage per day
formulas/breakEven.ts — breakEvenUsagePerDay()
Break-even usage per day = Fixed monthly cost ÷ Contribution per use ÷ Working days per monthUndefined when contribution per use is zero or negative — the formula throws rather than returning a misleading number, since there is no usage level at which a loss-making contribution margin breaks even. The Investment Outlook score (§5) treats this case as an Operational Margin of Safety score of 0, not an error to hide.
2.3 Maintenance schedule (warranty → CMC → AMC)
formulas/maintenance.ts — maintenanceScheduleForYears()
For each year of the projection:
year ≤ warranty years → covered by warranty, cost = 0
warranty years < year ≤ → covered by CMC, cost = CMC annual cost
(warranty years + CMC years)
otherwise → covered by AMC, cost = AMC annual costProduces one schedule entry per projection year, so the maintenance cliff (the cost jump the moment warranty ends) is visible year-by-year rather than averaged away.
2.4 Pre-operative interest
formulas/launchDelay.ts — preOperativeInterest()
Pre-operative interest = Principal × (Annual interest rate ÷ 100 ÷ 12) × Launch delay in monthsSimple (non-compounding) monthly interest accrued during the launch delay window, before any revenue exists to offset it.
3. Financing and depreciation
3.1 EMI (Equated Monthly Installment)
formulas/emi.ts — monthlyEmi()
If annual interest rate = 0:
EMI = Principal ÷ Tenure in months
Otherwise:
monthlyRate = Annual interest rate ÷ 12 ÷ 100
EMI = Principal × monthlyRate × (1 + monthlyRate)^tenureMonths
÷ ((1 + monthlyRate)^tenureMonths − 1)Standard amortizing-loan formula, with the zero-interest case handled as a straight division rather than a division by zero.
3.2 Straight-line depreciation
formulas/depreciation.ts — annualStraightLineDepreciation()
Annual depreciation = (Purchase cost − Salvage value) ÷ Useful lifeStraight-line only for v1, per SPEC.md §17 — an equal amount expensed every year of useful life, no accelerated-depreciation option yet.
4. Core financial outputs
4.1 NPV (Net Present Value)
formulas/npv.ts — npv()
NPV = (Σ over each period t of: cash flow[t] ÷ (1 + discount rate ÷ 100)^t) − Initial investment4.2 IRR (Internal Rate of Return)
formulas/irr.ts — irr()
IRR = the discount rate at which NPV = 0Solved numerically by bisection search between −99% and 1,000%, narrowing until NPV is within 0.000001 of zero (or 100 iterations elapse). Undefined and throws when the cash-flow series (initial investment plus all period cash flows) doesn't contain both a positive and a negative value, or when no sign change in NPV exists across the search range — there is no rate that makes an all-positive or all-negative series break even. The Investment Outlook score (§5.1) falls back to a net-return-ratio calculation in this case rather than propagating the error to the dashboard.
4.3 ROI (Return on Investment)
formulas/roi.ts — roi()
ROI = (Annual net return ÷ Initial investment) × 100Takes a view parameter (billed / realized / cash-flow) purely as a label — the caller is responsible for passing in the correctly-computed annual net return for that view; the formula itself doesn't change based on which view is selected.
4.4 Payback period (simple)
formulas/roi.ts — paybackPeriod()
Payback period = Initial investment ÷ Annual net cash flowReturns Infinity if annual net cash flow is zero or negative — the investment never pays back under a flat-annual-cash-flow assumption.
4.5 Payback period (from a multi-year cash-flow series)
formulas/roi.ts — paybackPeriodFromCashFlows()
Find the smallest year y such that:
cumulative cash flow through year (y − 1) + cash flow[y] ≥ Initial investment
Payback period = (y − 1) + (Initial investment − cumulative cash flow through year (y − 1)) ÷ cash flow[y]Linear interpolation within the year the cumulative cash flow crosses the initial investment. Returns Infinity if cumulative cash flow never reaches the initial investment across the whole series.
4.6 Discounted payback period
formulas/discountedPayback.ts — discountedPaybackPeriod()
Same as §4.5, but each year's cash flow is first discounted:
discounted cash flow[t] = cash flow[t] ÷ (1 + discount rate ÷ 100)^tAlways longer than (or equal to) simple payback, since discounting reduces the value of every future cash flow. Returns null (not Infinity) if cumulative discounted cash flow never reaches the initial investment — the Investment Outlook score (§5.1.2) treats null the same way it treats a ratio of 1.0 or more: a Speed to Payback score of 0.
On the `Infinity`/`null` split (added 2026-07-13, capexiq-prebuild-assurance PBA-7): §4.4/§4.5's Infinity and §4.6's null are two *different, deliberate* sentinels for "never pays back," not an inconsistency to unify. Infinity is required by formulas/actionableInsight.ts's subtraction-based comparison (baselinePaybackYears − scenarioPaybackYears) — Infinity propagates correctly through that arithmetic (a scenario that still never pays back correctly fails the materiality gate); if this were null instead, null coerces to 0 in JavaScript arithmetic and would silently produce a wrong, false-positive "improvement." null is required by investmentOutlookScore.ts's explicit === null branch. Do not unify these into one sentinel — that was considered and rejected specifically because of the actionableInsight.ts dependency above.
What *is* a real hazard: JSON.stringify(Infinity) silently produces the string "null" — indistinguishable from discountedPaybackPeriod's genuine null, or any other explicit "unavailable" marker, if either payback value is ever serialized (a scenario fixture, a future export intermediate format, anything touching localStorage — though per this project's browser-storage rules, calculated results should never be persisted there in the first place). Any future serialization boundary must encode Infinity as an explicit, distinct marker (e.g., a string "never" or a neverPaysBack: true flag) before calling JSON.stringify — never rely on JSON.stringify's default behavior for a value that can be Infinity. See agent-build-plan.md Phase 6/8 for the corresponding checklist item.
4.7 EAC (Equivalent Annual Cost)
formulas/eac.ts — equivalentAnnualCost()
annuityFactor = (1 − (1 + discount rate ÷ 100)^(−useful life)) ÷ (discount rate ÷ 100)
= useful life [if discount rate = 0]
EAC = (Initial investment + Σ over each year of: cost[year] ÷ (1 + discount rate ÷ 100)^year) ÷ annuityFactorConverts the full discounted lifetime cost of owning and running the equipment (purchase, installation, operating, maintenance, financing — costs only, no revenue) into a single comparable annual figure. Used for comparing financing structures or equipment options with different useful lives against each other, not as an input to the Investment Outlook score.
5. Investment Outlook score
formulas/investmentOutlookScore.ts — investmentOutlookScore(). Full methodology, worked example, and design rationale in financial-model-spec.md §1 — this section is the condensed formula reference; that document is the one to read for *why* each anchor point was chosen.
5.1 Four sub-scores, each normalized to 0–100
Return Strength (weight 35%)
If IRR is defined:
spread = IRR − Discount rate
score = 0 if spread ≤ −5
score = (spread + 5) ÷ 5 × 50 if −5 < spread ≤ 0
score = 50 + (spread ÷ 10) × 50 if 0 < spread ≤ 10
score = 100 if spread > 10
If IRR is undefined (formulas/irr.ts throws), fall back to:
netReturnRatio = NPV ÷ Initial investment
score = 0 if netReturnRatio ≤ −0.2
score = (netReturnRatio + 0.2) ÷ 0.2 × 50 if −0.2 < netReturnRatio ≤ 0
score = 50 + (netReturnRatio ÷ 0.5) × 50 if 0 < netReturnRatio ≤ 0.5
score = 100 if netReturnRatio > 0.5Speed to Payback (weight 25%)
ratio = Discounted payback years ÷ Useful life years (or treat as ≥ 1 if payback is null)
score = 0 if ratio ≥ 1.0
score = (1.0 − ratio) ÷ 0.5 × 50 if 0.5 ≤ ratio < 1.0
score = 50 + ((0.5 − ratio) ÷ 0.3) × 50 if 0.2 ≤ ratio < 0.5
score = 100 if ratio < 0.2Financing Resilience (weight 20% — 0% for cash purchases, see §5.3)
DSCR = Monthly operating cash flow before EMI ÷ Monthly EMI
score = 0 if DSCR ≤ 1.0
score = (DSCR − 1.0) ÷ 0.5 × 50 if 1.0 < DSCR ≤ 1.5
score = 50 + ((DSCR − 1.5) ÷ 0.5) × 50 if 1.5 < DSCR ≤ 2.0
score = 100 if DSCR > 2.0Operational Margin of Safety (weight 20%)
cushion = (Usage per day − Break-even usage per day) ÷ Usage per day
(score = 0 directly if usage per day = 0, or if break-even usage is undefined)
score = 0 if cushion ≤ 0
score = (cushion ÷ 0.20) × 50 if 0 < cushion ≤ 0.20
score = 50 + ((cushion − 0.20) ÷ 0.30) × 50 if 0.20 < cushion ≤ 0.50
score = 100 if cushion > 0.505.2 Composite score
Composite = 0.35 × Return Strength + 0.25 × Speed to Payback
+ 0.20 × Financing Resilience + 0.20 × Operational Margin of Safety
Rounded to the nearest integer for display.5.3 Cash-purchase weight redistribution
If financing type is cash (no EMI, no DSCR), Financing Resilience is dropped and its 20% weight redistributes proportionally across the other three (each original weight ÷ 0.8):
Return Strength 43.75%, Speed to Payback 31.25%, Operational Margin of Safety 25%Never scored as a silent 100 — that would misrepresent "not applicable" as "excellent."
5.4 Bands
Strong 75–100
Moderate 55–74
Caution 35–54
Weak 0–345.5 Driver (explainability)
The sub-score with the lowest value is the mechanically-derived driver — never hand-authored. Ties broken in fixed order: Return Strength → Speed to Payback → Financing Resilience → Operational Margin of Safety. If the driver's own score is ≥ 55, it's framed as a strength ("Main strength: ...") rather than a risk.
6. Scenario analysis and the automatic actionable insight
6.1 Scenario run
formulas/sensitivity.ts — runScenario()
For each projection year, computes annual net cash flow (applying a tariff increase from a given start year onward, if one is specified), then derives ROI, payback years, NPV, and IRR (null if undefined) from that cash-flow series — the same underlying formulas as §1-§4, run against a hypothetical set of assumptions instead of the baseline.
6.2 Automatic actionable price-increase insight
formulas/actionableInsight.ts — actionablePriceIncreaseInsight(). Full rationale and worked example in financial-model-spec.md §4.
Test grid: price increases of [2%, 5%, 8%, 10%, 15%] of current billed tariff,
starting in year [1, 2, 3], each capped at floor(useful life years ÷ 2)
For each (delta, start year) combination:
paybackImprovementMonths = (baseline payback years − scenario payback years) × 12
qualifies if paybackImprovementMonths ≥ 6
Among qualifying combinations, select:
1. smallest price-increase delta
2. tie-break: earliest start year
3. tie-break: largest payback improvement
Returns null if no combination qualifies — the expected, common result.Rupee amounts round to the nearest ₹5 for display.