A full leveraged buyout model with the interest/cash-flow circularity solved to convergence — sources and uses, multi-tranche debt with a cash sweep, covenant tests, sponsor IRR and MoIC, an equity waterfall, a value-creation bridge that ties exactly, and accretion/dilution.
An LBO's headline IRR is mostly a number somebody typed into the exit-multiple cell. This model prices the deal properly — interest on average balances, mandatory amortisation before an optional sweep, a revolver that actually funds a cash shortfall, loss carryforwards with a usage cap — and then does the two things a returns table never does: it decomposes the return into EBITDA growth, multiple expansion and debt paydown so you can see how much of the "value creation" was an operating thesis and how much was an assumption, and it measures what all that machinery is actually worth by running the shortcut model every spreadsheet tutorial builds and reporting the difference honestly. That difference is not where you would expect it. See Honest findings.
- Sources and uses with sponsor equity as the plug, so
sources - uses == 0is a real test rather than a tautology — and enterprise value is not double-counted against the debt being refinanced, which is the single most common error in a hand-built funding table (test_existing_debt_is_not_double_counted). - A circular debt schedule solved as a fixed point. Interest depends on average balances, which depend on the sweep, which depends on the cash left after interest and tax. The solver iterates to a stated tolerance and reports the iteration count — six on the bundled deal. No "enable iterative calculation" toggle, no interest-on-opening-balance shortcut.
- A capital structure with the parts that matter: revolver (commitment, draws, undrawn commitment fee), amortising term loans, fixed-rate notes, floating tranches priced off a base rate with a floor, PIK that accrues into its own balance, original issue discount, and a cash sweep applied by tranche priority after mandatory amortisation.
- Cash taxes with a net operating loss carryforward and a configurable usage cap (the US 80% limitation is one line of config).
- Covenant tests per year — net leverage, interest coverage, springing revolver covenants — with headroom reported and breaches flagged rather than fatal.
- Returns: MoIC and an XIRR over dated cash flows including dividend recapitalisations, an equity waterfall (8% preferred, GP catch-up, 20% carry, LP/GP split), and equity value floored at zero because limited liability is not optional — a model reporting a negative MoIC has forgotten that equity is an option.
- A value-creation bridge that ties out exactly. EBITDA growth, multiple expansion, debt paydown and fees sum to exit equity value less total equity invested, to floating-point precision, with the decomposition convention stated openly because there is more than one.
- Accretion/dilution for cash/stock/mixed consideration with synergies, financing interest, foregone interest on cash and intangible amortisation, plus breakeven synergy and breakeven premium solvers. The classic all-stock relative-P/E result is asserted in both directions.
- Sensitivity grids (entry × exit multiple, leverage × exit multiple), IRR elasticities per unit and per standard deviation, and a seeded Monte Carlo over the drivers.
- Outputs: console tables always, CSV always, and an
.xlsxworkbook behind the[excel]extra. - 133 tests, offline, about one second.
numpyandpyyamlonly in the core.
git clone https://github.com/kayasolomon/lbo-model.git
cd lbo-model
python -m venv .venv && source .venv/bin/activate
pip install -e .[dev]
pytest -q # 133 passed
# the whole model on the bundled fictional deal
lbo-model run
# just the debt schedule and credit stats
lbo-model schedule
# returns, the value bridge and the waterfall
lbo-model returns
# sensitivity grids and which assumption is doing the work
lbo-model sensitivity
# the honest benchmark: this model against the spreadsheet shortcut
lbo-model compare-naive
# where the base case sits in a distribution
lbo-model montecarlo --trials 400
# accretion/dilution on an illustrative strategic deal
lbo-model accretion --premium 0.28 --cash-pct 0.6
# exports
lbo-model run --csv-dir output/ # always available
lbo-model run --xlsx output/deal.xlsx # needs pip install '.[excel]'
# the whole tour
python examples/run_all.pyThe deal lives in examples/deal.yml — a fictional company with fictional financials. Edit the
assumptions and re-run; every number in this README comes from that file.
$ lbo-model run
SOURCES AND USES — Meridian Components — sponsor acquisition (illustrative)
================================================================================================
Sources amount % of total
revolver 0.0 0.0%
term_loan_a 93.2 12.0%
term_loan_b 199.0 25.6%
senior_notes 85.5 11.0%
pik_note 31.1 4.0%
management rollover 29.6 3.8%
sponsor equity 339.9 43.7%
TOTAL SOURCES 778.2 100.0%
Uses amount % of total
equity purchase price (EV 9.50x = 738.1) 643.1 82.6%
refinance existing debt 95.0 12.2%
transaction fees 14.8 1.9%
financing fees 10.3 1.3%
cash to balance sheet 15.0 1.9%
TOTAL USES 778.2 100.0%
balance check (sources - uses): +0.0000000000
entry EBITDA 77.7 at 9.50x = EV 738.1 opening leverage 5.30x debt 52.7% of capitalisation
The schedule, with the circularity converging and the cash flows tying out:
TOTALS
year debt open repaid debt close cash net debt interest iters
1 411.8 23.2 392.4 18.7 373.7 33.9 6
2 392.4 28.7 368.0 20.5 347.5 32.5 6
3 368.0 34.9 337.9 22.6 315.3 30.7 6
4 337.9 40.9 302.4 24.6 277.8 28.4 6
5 302.4 44.4 264.0 28.9 235.1 25.7 6
circularity solved to convergence in every year: True (max 6 iterations)
cash-flow identity error, worst year: 7.11e-15
CREDIT STATISTICS
================================================================================================
year EBITDA cash int FCF net debt net lev gross lev int cover
1 85.4 30.1 26.9 373.7 4.37x 4.59x 2.83x
2 92.5 28.3 30.5 347.5 3.76x 3.98x 3.27x
3 98.6 26.0 36.9 315.3 3.20x 3.43x 3.80x
4 103.9 23.1 42.8 277.8 2.67x 2.91x 4.50x
5 108.3 19.6 48.7 235.1 2.17x 2.44x 5.51x
breaches: none — covenants hold in every year
Returns, the bridge and the waterfall:
RETURNS
================================================================================================
exit in year 5 at 9.50x EBITDA of 108.3 = EV 1,028.7
less net debt 235.1 = equity value 793.6
sponsor share 92.0% = 730.1
MoIC 2.15x over 5 years IRR 16.52%
VALUE CREATION BRIDGE
ebitda growth 290.6 68.5%
multiple expansion 0.0 0.0%
debt paydown / cash generation 161.7 38.1%
fees and other -28.1 -6.6%
TOTAL VALUE CREATED 424.2 100.0%
bridge residual: +0.0000000000
EQUITY WATERFALL (8% preferred, 20% carry, with catch-up)
return of capital 339.9
preferred return to LPs 156.3
GP catch-up 39.1
residual to LPs 155.9
residual to GP 39.0
LP proceeds 645.3 (1.94x) GP proceeds 84.8 carry 78.0
distribution check: +0.0000000000
And the benchmark that gives this repository its point:
$ lbo-model compare-naive
FULL MODEL vs THE SPREADSHEET SHORTCUT
================================================================================================
shortcut = interest on opening balances, no cash sweep, no revolver, no loss carryforward
scenario model MoIC IRR exit ND worst cash shortfall breaches iters
base full 2.15 16.52% 235.1 18.7 0.0 0 6
base naive 2.10 16.00% 252.8 26.9 0.0 0 1
-> shortcut IRR is -52bp against the full model in the base case
stress full 0.00 -100.00% 414.2 14.8 0.0 13 8
stress naive 0.00 -100.00% 415.8 -27.6 75.7 9 1
-> shortcut IRR is +0bp against the full model in the stress case
src/lbo_model/
config.py validated dataclasses + YAML loading (unknown keys are errors)
operating.py revenue -> EBITDA -> capex and working capital; cash taxes with NOLs
capital.py sources and uses, tranche sizing, sponsor equity as the plug
schedule.py the fixed-point debt schedule, sweep waterfall, covenant tests
returns.py IRR/XIRR by bisection, equity waterfall, value-creation bridge
model.py run_deal() and run_naive() — the same engine, three flags apart
analysis.py scenarios, sensitivity grids, elasticities, Monte Carlo
accretion.py accretion/dilution and the two breakeven solvers
report.py text tables and CSV export
excel.py optional .xlsx workbook (behind the [excel] extra)
cli.py argparse CLI
Design decisions worth defending:
- The shortcut model reuses the full engine.
run_naiveisrun_dealwith three flags — interest on opening balances, no revolver, no loss carryforward — plus a zeroed sweep. Writing a second model to lose to the first would prove nothing; sharing the code path means any difference is attributable to the simplification and nothing else. - The fixed point iterates on the sweep and the draw, not on a scalar interest guess. If the interest basis does not respond to the sweep, the "circularity" is decorative.
- Invariants are tests, not comments. Sources equal uses; the cash-flow identity reconstructs closing cash to 7e-15; the value bridge residual is exactly zero; the waterfall allocates every pound. Each one is asserted on three different deals.
- IRR refuses to guess. Cash flows with two sign changes have no unique IRR, so the solver raises with an explanation instead of returning whichever root it found first.
- Every convention that changes the answer is stated in the docstring where it is applied — average-balance interest, mandatory-before-optional repayment, pref on LP capital only, EBITDA growth valued at the entry multiple.
Everything below is produced by the code in this repository, on the deal in examples/deal.yml.
The first finding is the one the repository was built to test, and it did not come out the way
the pitch would prefer.
1. The spreadsheet shortcut is nearly right about the IRR. In the base case it is 52 basis points out. Not percentage points — basis points, and understating rather than flattering: without a cash sweep it carries 252.8 of net debt to exit against 235.1, and charging interest on opening balances overstates the interest bill. If you only care about the headline return in the base case, the tutorial model is fine, and anyone selling the circularity as worth points of IRR is overselling it.
2. What the shortcut gets catastrophically wrong is solvency. In the stress case (growth down 9 points, margins down 180bp, base rates up 300bp) it reports a company running a negative cash balance of 27.6 — which is not a thing that happens; the company would have drawn its revolver or defaulted. It also sees 9 covenant breaches where the real structure has 13, because its debt balances are wrong. The full model draws the revolver, stays solvent, and flags every breach. So the honest conclusion: the circularity and the revolver buy you liquidity and covenant visibility, not decimal places on the return. That is a better reason to build them than the one usually given, and it only shows up when you look.
3. The exit multiple is worth about as much as the entire operating case, and requires no execution. Per unit, margin dominates: 925bp of IRR per 100bp a year of margin expansion, against 157bp per 100bp of revenue growth and 318bp per full turn of exit multiple. But per unit is the wrong comparison, because those units are not equally likely. Scaled to one standard deviation of each assumption, the three levers are the same order of magnitude — 239bp for the exit multiple, 313bp for growth, 278bp for margin. The exit multiple is 0.76x the largest operating lever. The point is not that the multiple dominates (on this deal it does not); it is that a valuation assumption nobody has to deliver is worth roughly as much as an operating plan somebody does.
4. The base case is a fair middle outcome — and still hides a real chance of losing everything. Across 400 seeded trials shocking growth, margin and the exit multiple, the base case sits at the 55th percentile with a median of 16.1% against a base of 16.5%: honest, not sandbagged. Widen the shocks to a genuinely uncertain deal (±5% growth, ±80bp margin, ±2.0x exit) and the distribution's left skew appears: mean 14.5% against a median 16.7%, a 5th percentile of −11%, and 13.5% of trials losing the entire equity cheque. Leverage truncates the downside at total loss, so the mean is always worse than the middle. A single-point IRR cannot show you that.
5. Two modelling errors this build caught, both of which would have looked fine. The first: adding refinanced existing debt to uses on top of enterprise value, which double-counts (EV already includes it), inflated the sponsor cheque by 95 and cut the IRR by roughly 2 percentage points — the model still balanced and still produced a plausible table. The second: the revolver's undrawn commitment fee was charged to cash but left out of the funding requirement, so the closing cash balance drifted negative by a few tenths a year even with headroom available. Both are now tests. Neither would have thrown an error.
6. What no LBO model prices. Every number here is conditional on being able to exit at all. The model will happily tell you an IRR for a year-5 exit at 9.5x; it has nothing to say about the window being shut, the lender syndicate not re-cutting the covenant, or the management team leaving in year two. Those are the things that actually determine outcomes, and no amount of schedule precision touches them.
| This demo | Custom build |
|---|---|
| One fictional target, annual periods, five-year forecast | Your target's real financials, quarterly or monthly periods, stub periods and mid-year conventions |
| Revolver, TLA/TLB, fixed notes, PIK, OID | Full term-sheet mechanics: unitranche, delayed draw, ticking fees, MFN, ratio and incremental debt, intercreditor waterfalls |
| Net leverage and interest-coverage covenants | Your credit agreement's definitions — EBITDA add-backs, cure rights, equity cures, baskets, RP capacity |
| Sponsor-level waterfall with a standard 8/20 | Fund-level economics, management incentive plans and option pools, ratchets, co-invest, tax structuring |
| Single exit at a chosen multiple | Exit-route analysis (sponsor-to-sponsor, IPO, strategic), dividend recaps, add-on acquisitions and roll-up modelling |
| Console tables, CSV, a simple .xlsx | Lender-ready and IC-ready deliverables, a formula-driven Excel model your team can audit and edit, IC memo generation (pairs with auto-report) |
| Deterministic case plus a seeded Monte Carlo | Scenario libraries agreed with the deal team, downside cases tied to real covenant definitions, sensitivity packs |
Related repositories: valuation-toolkit does DCF and trading comps — the intrinsic-value side of the same question, with no leverage in it; portfolio-optimizer and options-pricer cover the markets side.
Illustrative model on fictional synthetic financials for a company that does not exist. It is not investment, legal, tax or accounting advice, not a solicitation, and not a representation that any deal or return shown here is achievable. Real transactions turn on diligence, documentation and negotiation that no model captures — and, as the Honest findings section says explicitly, on an exit window that no model prices. No vendor or client data is included.
Want this adapted and deployed for your business? I build custom versions — kayasolomon.tech