ROI / NPV Sensitivity Model
Purpose
Calculates ROI, NPV, and IRR plus a sensitivity analysis across scenarios.
Anchored in research
- w95 business-case-builder
- aj-geddes — business-case-development
Method
- Establish the cash-flow baseline — incremental costs and benefits by
period, compared against a credible do-nothing baseline, not against
zero. ROI and NPV overstate the case if they're compared to nothing
happening instead of to what would happen anyway.
- Select and justify a discount rate (e.g. WACC, an internal hurdle
rate, or the organization's standard rate) — never invent this figure;
ask for it, use a marked placeholder, or use a clearly flagged
conservative default.
- Compute NPV (the sum of discounted net cash flows), IRR (the discount
rate at which NPV = 0), payback period, and ROI (net benefit ÷ cost) side
by side — a single metric can look attractive while another flags a
problem, e.g. a fast payback period paired with a negative NPV at the
real discount rate.
- Run a one-at-a-time sensitivity analysis: vary each key input
(adoption rate, unit cost, benefit-realization timing, discount rate) by a
defined range (e.g. ±20%) while holding the others constant, and rank the
inputs by how much they move NPV. This produces a tornado chart that
identifies which two or three assumptions actually drive the result.
- Build at least a downside (pessimistic) and an upside (optimistic)
scenario, not only the base case — a sensitivity analysis whose worst
case still looks comfortable isn't a real stress test.
- Report the breakeven point for the most sensitive variable (e.g.
"adoption has to exceed X% for NPV to stay positive") so the
decision-maker sees exactly which assumption they're betting on.
Available scripts
scripts/roi_npv_model.py -- calculates NPV, IRR (bisection search),
payback period, and ROI from a list of incremental net cash flows
(already vs. the do-nothing baseline, per Method step 1), then runs the
one-at-a-time sensitivity analysis from step 4, an upside/downside
scenario pair from step 5, and the breakeven estimate from step 6. Run it
once cash flows and a discount rate are known, instead of computing NPV/
IRR by hand -- it also catches the sign-flip cases (no IRR in a sane
range) that are easy to get wrong manually.
python3 scripts/roi_npv_model.py --example > input.json # see the input shape
python3 scripts/roi_npv_model.py input.json # or: ... - <<< '{...}'
Stdlib only (json, argparse, math) -- no install needed. Exit code 1 on
invalid input (missing discount_rate, fewer than 2 cash-flow periods),
with a specific error on stderr.
What this skill does NOT do
- Doesn't make the final decision for you — it produces a structured draft to
support a human decision.
- Doesn't confirm figures, market data, or competitor data from memory — it
uses the inputs you provide, or marks an assumption clearly
(
[assumption — verify]).
- Doesn't invent precise currency amounts — it calculates from the baseline
values you provide and makes every assumption visible.
Refinement notes
Areas to keep deepening with real practice:
- your own rules of thumb and heuristics for this technique
- concrete templates (into
../../references/)
- reference cases / your own examples
- what this skill deliberately does not do (guardrails, common mistakes) —
add to the list above
This is an internal working note, not a claim about the skill's current
usability. Track depth privately via the maturity field in
skills_index.json (see
../../../meta/maturity_levels.md).
Don't add new fields to the frontmatter — name and description are
the only ones allowed (see
../../../meta/frontmatter_schema.md).
Continue from here
References
1---2name: roi-npv-sensitivity-model3description: Calculates ROI, NPV, IRR, and payback period against a credible do-nothing baseline, then runs a one-at-a-time sensitivity analysis to rank which two or three assumptions actually drive the result, plus upside/downside scenarios. Use when a business case's financial numbers need to survive scrutiny, not just look attractive under the base case.4---56# ROI / NPV Sensitivity Model78## Purpose910Calculates ROI, NPV, and IRR plus a sensitivity analysis across scenarios.1112## Anchored in research1314- w95 business-case-builder15- aj-geddes — business-case-development1617## Method18191. **Establish the cash-flow baseline** — incremental costs and benefits by20 period, compared against a credible do-nothing baseline, not against21 zero. ROI and NPV overstate the case if they're compared to nothing22 happening instead of to what would happen anyway.232. **Select and justify a discount rate** (e.g. WACC, an internal hurdle24 rate, or the organization's standard rate) — never invent this figure;25 ask for it, use a marked placeholder, or use a clearly flagged26 conservative default.273. **Compute NPV (the sum of discounted net cash flows), IRR (the discount28 rate at which NPV = 0), payback period, and ROI (net benefit ÷ cost) side29 by side** — a single metric can look attractive while another flags a30 problem, e.g. a fast payback period paired with a negative NPV at the31 real discount rate.324. **Run a one-at-a-time sensitivity analysis:** vary each key input33 (adoption rate, unit cost, benefit-realization timing, discount rate) by a34 defined range (e.g. ±20%) while holding the others constant, and rank the35 inputs by how much they move NPV. This produces a tornado chart that36 identifies which two or three assumptions actually drive the result.375. **Build at least a downside (pessimistic) and an upside (optimistic)38 scenario, not only the base case** — a sensitivity analysis whose worst39 case still looks comfortable isn't a real stress test.406. **Report the breakeven point for the most sensitive variable** (e.g.41 "adoption has to exceed X% for NPV to stay positive") so the42 decision-maker sees exactly which assumption they're betting on.4344## Available scripts4546- **`scripts/roi_npv_model.py`** -- calculates NPV, IRR (bisection search),47 payback period, and ROI from a list of incremental net cash flows48 (already vs. the do-nothing baseline, per Method step 1), then runs the49 one-at-a-time sensitivity analysis from step 4, an upside/downside50 scenario pair from step 5, and the breakeven estimate from step 6. Run it51 once cash flows and a discount rate are known, instead of computing NPV/52 IRR by hand -- it also catches the sign-flip cases (no IRR in a sane53 range) that are easy to get wrong manually.5455 ```bash56 python3 scripts/roi_npv_model.py --example > input.json # see the input shape57 python3 scripts/roi_npv_model.py input.json # or: ... - <<< '{...}'58 ```5960 Stdlib only (json, argparse, math) -- no install needed. Exit code 1 on61 invalid input (missing discount_rate, fewer than 2 cash-flow periods),62 with a specific error on stderr.6364## What this skill does NOT do6566- Doesn't make the final decision for you — it produces a structured draft to67 support a human decision.68- Doesn't confirm figures, market data, or competitor data from memory — it69 uses the inputs you provide, or marks an assumption clearly70 (`[assumption — verify]`).71- Doesn't invent precise currency amounts — it calculates from the baseline72 values you provide and makes every assumption visible.7374## Refinement notes7576Areas to keep deepening with real practice:7778- your own rules of thumb and heuristics for this technique79- concrete templates (into [`../../references/`](../../references/))80- reference cases / your own examples81- what this skill deliberately does *not* do (guardrails, common mistakes) —82 add to the list above8384This is an internal working note, not a claim about the skill's current85usability. Track depth privately via the `maturity` field in86`skills_index.json` (see87[`../../../meta/maturity_levels.md`](../../../meta/maturity_levels.md)).88**Don't add new fields to the frontmatter** — `name` and `description` are89the only ones allowed (see90[`../../../meta/frontmatter_schema.md`](../../../meta/frontmatter_schema.md)).9192## Continue from here9394- Next in this pack: [`../risk-matrix-and-mitigation/SKILL.md`](../risk-matrix-and-mitigation/SKILL.md) — Identifies and scores risks (probability × impact) and designs mitigations.95- Before this (if the inputs come from a demo/PoC):96 [`../../../prototyping-and-demonstration/skills/demo-to-business-case-bridge/SKILL.md`](../../../prototyping-and-demonstration/skills/demo-to-business-case-bridge/SKILL.md)97 — run the sensitivity analysis especially on the assumptions that skill98 flagged as weakest in the assumption chain.99- A ready-made skill chain for this situation: see [`../../../playbooks/`](../../../playbooks/)100- This pack's shared guardrails: [`../../CLAUDE.md`](../../CLAUDE.md)101102## References103104- [`../../references/`](../../references/) — the pack's shared background material105- [`../../CLAUDE.md`](../../CLAUDE.md) — the pack's shared guardrails