Skip to content

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

lbo-model

tests

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.

Features

  • Sources and uses with sponsor equity as the plug, so sources - uses == 0 is 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 .xlsx workbook behind the [excel] extra.
  • 133 tests, offline, about one second. numpy and pyyaml only in the core.

5-minute quickstart

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.py

The 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.

Example output

$ 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

Architecture

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_naive is run_deal with 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.

Honest findings

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.

Demo scope vs custom build

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.

Disclaimer

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

About

A full LBO model with the interest/cash-flow circularity solved to convergence — multi-tranche debt with a cash sweep, covenants, sponsor IRR and MoIC, an equity waterfall, a value-creation bridge that ties exactly, and accretion/dilution.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages