capstack-lbo 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- capstack/__init__.py +243 -0
- capstack/balance_sheet.py +295 -0
- capstack/cli.py +1995 -0
- capstack/covenants.py +594 -0
- capstack/daycount.py +121 -0
- capstack/debt.py +2003 -0
- capstack/drivers.py +167 -0
- capstack/events.py +914 -0
- capstack/fees.py +484 -0
- capstack/incentive.py +517 -0
- capstack/money.py +204 -0
- capstack/operating.py +677 -0
- capstack/outcome.py +958 -0
- capstack/periods.py +431 -0
- capstack/py.typed +0 -0
- capstack/report.py +1188 -0
- capstack/returns.py +426 -0
- capstack/sensitivity.py +940 -0
- capstack/spec.py +1686 -0
- capstack/tax.py +834 -0
- capstack/transaction.py +430 -0
- capstack_lbo-0.1.0.dist-info/METADATA +1260 -0
- capstack_lbo-0.1.0.dist-info/RECORD +27 -0
- capstack_lbo-0.1.0.dist-info/WHEEL +5 -0
- capstack_lbo-0.1.0.dist-info/entry_points.txt +3 -0
- capstack_lbo-0.1.0.dist-info/licenses/LICENSE +21 -0
- capstack_lbo-0.1.0.dist-info/top_level.txt +1 -0
capstack/__init__.py
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"""capstack — a leveraged buyout engine."""
|
|
2
|
+
|
|
3
|
+
from .balance_sheet import (
|
|
4
|
+
BalanceSheetError,
|
|
5
|
+
OpeningBalanceSheet,
|
|
6
|
+
PurchaseAccounting,
|
|
7
|
+
TargetBookBalanceSheet,
|
|
8
|
+
UnbalancedBalanceSheetError,
|
|
9
|
+
)
|
|
10
|
+
from .covenants import (
|
|
11
|
+
Covenant,
|
|
12
|
+
CovenantObservation,
|
|
13
|
+
CovenantReport,
|
|
14
|
+
Direction,
|
|
15
|
+
Measure,
|
|
16
|
+
)
|
|
17
|
+
from .daycount import DayCount, year_fraction
|
|
18
|
+
from .debt import (
|
|
19
|
+
CapitalStructure,
|
|
20
|
+
CircularityError,
|
|
21
|
+
DebtPeriod,
|
|
22
|
+
DebtSchedule,
|
|
23
|
+
InterestBasis,
|
|
24
|
+
SweepGrid,
|
|
25
|
+
SweepStep,
|
|
26
|
+
Tranche,
|
|
27
|
+
TrancheKind,
|
|
28
|
+
TranchePeriod,
|
|
29
|
+
)
|
|
30
|
+
from .drivers import Driver
|
|
31
|
+
from .events import (
|
|
32
|
+
AddOn,
|
|
33
|
+
AddOnError,
|
|
34
|
+
AddOnOutcome,
|
|
35
|
+
BlendedEntry,
|
|
36
|
+
Draw,
|
|
37
|
+
Recapitalisation,
|
|
38
|
+
RecapitalisationError,
|
|
39
|
+
RecapitalisationOutcome,
|
|
40
|
+
Refinancing,
|
|
41
|
+
RefinancingError,
|
|
42
|
+
RefinancingOutcome,
|
|
43
|
+
)
|
|
44
|
+
from .incentive import (
|
|
45
|
+
IncentiveError,
|
|
46
|
+
OptionPool,
|
|
47
|
+
PoolOutcome,
|
|
48
|
+
Ratchet,
|
|
49
|
+
RatchetBand,
|
|
50
|
+
Vesting,
|
|
51
|
+
settle_pool,
|
|
52
|
+
)
|
|
53
|
+
from .money import Money, allocate_pro_rata, money, quantize, rate, rescale
|
|
54
|
+
from .operating import (
|
|
55
|
+
AcquiredStream,
|
|
56
|
+
OperatingAssumptions,
|
|
57
|
+
OperatingModel,
|
|
58
|
+
OperatingPeriod,
|
|
59
|
+
TaxResult,
|
|
60
|
+
apply_carryforward,
|
|
61
|
+
)
|
|
62
|
+
from .outcome import (
|
|
63
|
+
Attribution,
|
|
64
|
+
Distribution,
|
|
65
|
+
ExitValuation,
|
|
66
|
+
Outcome,
|
|
67
|
+
Security,
|
|
68
|
+
SecurityKind,
|
|
69
|
+
SecurityOutcome,
|
|
70
|
+
default_securities,
|
|
71
|
+
)
|
|
72
|
+
from .periods import Frequency, Period, PeriodGrid
|
|
73
|
+
from .report import Report, Section
|
|
74
|
+
from .returns import (
|
|
75
|
+
AmbiguousIRRError,
|
|
76
|
+
CashFlow,
|
|
77
|
+
CashFlowStream,
|
|
78
|
+
IRRError,
|
|
79
|
+
NoSignChangeError,
|
|
80
|
+
cagr,
|
|
81
|
+
irr_periodic,
|
|
82
|
+
moic,
|
|
83
|
+
npv_periodic,
|
|
84
|
+
)
|
|
85
|
+
from .sensitivity import (
|
|
86
|
+
Axis,
|
|
87
|
+
Breakeven,
|
|
88
|
+
Case,
|
|
89
|
+
Cell,
|
|
90
|
+
Dimension,
|
|
91
|
+
Grid,
|
|
92
|
+
Metric,
|
|
93
|
+
SensitivityError,
|
|
94
|
+
Unit,
|
|
95
|
+
solve,
|
|
96
|
+
)
|
|
97
|
+
from .spec import (
|
|
98
|
+
Deal,
|
|
99
|
+
DealSpecError,
|
|
100
|
+
EquityPlan,
|
|
101
|
+
Funding,
|
|
102
|
+
IncentivePlan,
|
|
103
|
+
SecurityPlan,
|
|
104
|
+
load_deal,
|
|
105
|
+
parse_deal,
|
|
106
|
+
)
|
|
107
|
+
from .tax import (
|
|
108
|
+
DEFAULT_INTEREST_LIMIT_RATE,
|
|
109
|
+
EarningsBase,
|
|
110
|
+
InterestDeduction,
|
|
111
|
+
InterestLimit,
|
|
112
|
+
LeveredTaxPeriod,
|
|
113
|
+
LeveredTaxSchedule,
|
|
114
|
+
TaxPolicy,
|
|
115
|
+
TaxPolicyError,
|
|
116
|
+
levered_tax,
|
|
117
|
+
levered_tax_for_model,
|
|
118
|
+
levered_tax_schedule,
|
|
119
|
+
)
|
|
120
|
+
from .transaction import (
|
|
121
|
+
DebtFunding,
|
|
122
|
+
EntryValuation,
|
|
123
|
+
LineItem,
|
|
124
|
+
SourcesAndUses,
|
|
125
|
+
Transaction,
|
|
126
|
+
UnbalancedTransactionError,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
__version__ = "0.1.0"
|
|
130
|
+
|
|
131
|
+
__all__ = [
|
|
132
|
+
"DEFAULT_INTEREST_LIMIT_RATE",
|
|
133
|
+
"AcquiredStream",
|
|
134
|
+
"AddOn",
|
|
135
|
+
"AddOnError",
|
|
136
|
+
"AddOnOutcome",
|
|
137
|
+
"AmbiguousIRRError",
|
|
138
|
+
"Attribution",
|
|
139
|
+
"Axis",
|
|
140
|
+
"BalanceSheetError",
|
|
141
|
+
"BlendedEntry",
|
|
142
|
+
"Breakeven",
|
|
143
|
+
"CapitalStructure",
|
|
144
|
+
"Case",
|
|
145
|
+
"CashFlow",
|
|
146
|
+
"CashFlowStream",
|
|
147
|
+
"Cell",
|
|
148
|
+
"CircularityError",
|
|
149
|
+
"Covenant",
|
|
150
|
+
"CovenantObservation",
|
|
151
|
+
"CovenantReport",
|
|
152
|
+
"DayCount",
|
|
153
|
+
"Deal",
|
|
154
|
+
"DealSpecError",
|
|
155
|
+
"DebtFunding",
|
|
156
|
+
"DebtPeriod",
|
|
157
|
+
"DebtSchedule",
|
|
158
|
+
"Dimension",
|
|
159
|
+
"Direction",
|
|
160
|
+
"Distribution",
|
|
161
|
+
"Draw",
|
|
162
|
+
"Driver",
|
|
163
|
+
"EarningsBase",
|
|
164
|
+
"EntryValuation",
|
|
165
|
+
"EquityPlan",
|
|
166
|
+
"ExitValuation",
|
|
167
|
+
"Frequency",
|
|
168
|
+
"Funding",
|
|
169
|
+
"Grid",
|
|
170
|
+
"IRRError",
|
|
171
|
+
"IncentiveError",
|
|
172
|
+
"IncentivePlan",
|
|
173
|
+
"InterestBasis",
|
|
174
|
+
"InterestDeduction",
|
|
175
|
+
"InterestLimit",
|
|
176
|
+
"LeveredTaxPeriod",
|
|
177
|
+
"LeveredTaxSchedule",
|
|
178
|
+
"LineItem",
|
|
179
|
+
"Measure",
|
|
180
|
+
"Metric",
|
|
181
|
+
"Money",
|
|
182
|
+
"NoSignChangeError",
|
|
183
|
+
"OpeningBalanceSheet",
|
|
184
|
+
"OperatingAssumptions",
|
|
185
|
+
"OperatingModel",
|
|
186
|
+
"OperatingPeriod",
|
|
187
|
+
"OptionPool",
|
|
188
|
+
"Outcome",
|
|
189
|
+
"Period",
|
|
190
|
+
"PeriodGrid",
|
|
191
|
+
"PoolOutcome",
|
|
192
|
+
"PurchaseAccounting",
|
|
193
|
+
"Ratchet",
|
|
194
|
+
"RatchetBand",
|
|
195
|
+
"Recapitalisation",
|
|
196
|
+
"RecapitalisationError",
|
|
197
|
+
"RecapitalisationOutcome",
|
|
198
|
+
"Refinancing",
|
|
199
|
+
"RefinancingError",
|
|
200
|
+
"RefinancingOutcome",
|
|
201
|
+
"Report",
|
|
202
|
+
"Section",
|
|
203
|
+
"Security",
|
|
204
|
+
"SecurityKind",
|
|
205
|
+
"SecurityOutcome",
|
|
206
|
+
"SecurityPlan",
|
|
207
|
+
"SensitivityError",
|
|
208
|
+
"SourcesAndUses",
|
|
209
|
+
"SweepGrid",
|
|
210
|
+
"SweepStep",
|
|
211
|
+
"TargetBookBalanceSheet",
|
|
212
|
+
"TaxPolicy",
|
|
213
|
+
"TaxPolicyError",
|
|
214
|
+
"TaxResult",
|
|
215
|
+
"Tranche",
|
|
216
|
+
"TrancheKind",
|
|
217
|
+
"TranchePeriod",
|
|
218
|
+
"Transaction",
|
|
219
|
+
"UnbalancedBalanceSheetError",
|
|
220
|
+
"UnbalancedTransactionError",
|
|
221
|
+
"Unit",
|
|
222
|
+
"Vesting",
|
|
223
|
+
"__version__",
|
|
224
|
+
"allocate_pro_rata",
|
|
225
|
+
"apply_carryforward",
|
|
226
|
+
"cagr",
|
|
227
|
+
"default_securities",
|
|
228
|
+
"irr_periodic",
|
|
229
|
+
"levered_tax",
|
|
230
|
+
"levered_tax_for_model",
|
|
231
|
+
"levered_tax_schedule",
|
|
232
|
+
"load_deal",
|
|
233
|
+
"moic",
|
|
234
|
+
"money",
|
|
235
|
+
"npv_periodic",
|
|
236
|
+
"parse_deal",
|
|
237
|
+
"quantize",
|
|
238
|
+
"rate",
|
|
239
|
+
"rescale",
|
|
240
|
+
"settle_pool",
|
|
241
|
+
"solve",
|
|
242
|
+
"year_fraction",
|
|
243
|
+
]
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
"""The balance sheet the target carries out of close.
|
|
2
|
+
|
|
3
|
+
A buyout does not leave the target's balance sheet where it found it. The old
|
|
4
|
+
capital structure is repaid, a new one is put in its place, the assets are
|
|
5
|
+
re-measured at what was just paid for them, and the difference between the
|
|
6
|
+
price and the identifiable net assets acquired becomes goodwill. Everything
|
|
7
|
+
downstream — the debt schedule, the exit equity bridge, every covenant that
|
|
8
|
+
references a balance — starts from this statement rather than from the target's
|
|
9
|
+
last audited accounts.
|
|
10
|
+
|
|
11
|
+
Three points are where an opening balance sheet usually goes wrong.
|
|
12
|
+
|
|
13
|
+
*The target's own goodwill does not survive.* Goodwill is the residual of a
|
|
14
|
+
previous transaction. It is written off and a new residual is struck against
|
|
15
|
+
the price just paid; carrying both counts the same intangible twice.
|
|
16
|
+
|
|
17
|
+
*A step-up that is not deductible creates a deferred tax liability.* Writing an
|
|
18
|
+
asset up to fair value in a stock deal raises book depreciation without raising
|
|
19
|
+
tax depreciation, so the model owes tax on the difference for the rest of the
|
|
20
|
+
asset's life. Recognising the step-up without the liability overstates equity
|
|
21
|
+
by the tax on it and understates goodwill by the same amount.
|
|
22
|
+
|
|
23
|
+
*Debt is carried at face.* Issue discount and financing fees are shown against
|
|
24
|
+
it as unamortised balances rather than netted into it, because interest accrues
|
|
25
|
+
on what is owed rather than on what was received, and the amortisation of those
|
|
26
|
+
balances is a separate charge with its own life.
|
|
27
|
+
|
|
28
|
+
The identity is enforced, not checked. Assets equal liabilities plus equity to
|
|
29
|
+
the cent or the object does not exist, which is the same discipline the funding
|
|
30
|
+
table holds itself to and for the same reason: an opening balance sheet that is
|
|
31
|
+
out by a small amount is not nearly right, it has a line item missing.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
from __future__ import annotations
|
|
35
|
+
|
|
36
|
+
from dataclasses import dataclass
|
|
37
|
+
|
|
38
|
+
from .money import ZERO, Money, Numeric, money
|
|
39
|
+
from .transaction import Transaction
|
|
40
|
+
|
|
41
|
+
__all__ = [
|
|
42
|
+
"BalanceSheetError",
|
|
43
|
+
"OpeningBalanceSheet",
|
|
44
|
+
"PurchaseAccounting",
|
|
45
|
+
"TargetBookBalanceSheet",
|
|
46
|
+
"UnbalancedBalanceSheetError",
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class BalanceSheetError(ValueError):
|
|
51
|
+
"""The book position given cannot be reconciled with the transaction."""
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class UnbalancedBalanceSheetError(BalanceSheetError):
|
|
55
|
+
"""Assets do not equal liabilities plus equity."""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass(frozen=True, slots=True)
|
|
59
|
+
class TargetBookBalanceSheet:
|
|
60
|
+
"""The target's own balance sheet, the day before close.
|
|
61
|
+
|
|
62
|
+
Totals rather than a full chart of accounts: purchase accounting only needs
|
|
63
|
+
to know what the book equity is, how much of the asset side is goodwill
|
|
64
|
+
that will be written off, and how the rest splits between cash and
|
|
65
|
+
everything else. The split matters because cash is the one asset that both
|
|
66
|
+
funds the deal and survives it unchanged.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
total_assets: Money
|
|
70
|
+
total_liabilities: Money
|
|
71
|
+
goodwill: Money = ZERO
|
|
72
|
+
|
|
73
|
+
@classmethod
|
|
74
|
+
def of(
|
|
75
|
+
cls,
|
|
76
|
+
total_assets: Numeric,
|
|
77
|
+
total_liabilities: Numeric,
|
|
78
|
+
*,
|
|
79
|
+
goodwill: Numeric = 0,
|
|
80
|
+
) -> TargetBookBalanceSheet:
|
|
81
|
+
return cls(
|
|
82
|
+
total_assets=money(total_assets),
|
|
83
|
+
total_liabilities=money(total_liabilities),
|
|
84
|
+
goodwill=money(goodwill),
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
def __post_init__(self) -> None:
|
|
88
|
+
if self.total_assets < 0:
|
|
89
|
+
raise BalanceSheetError("total assets must not be negative")
|
|
90
|
+
if self.total_liabilities < 0:
|
|
91
|
+
raise BalanceSheetError("total liabilities must not be negative")
|
|
92
|
+
if self.goodwill < 0:
|
|
93
|
+
raise BalanceSheetError("goodwill must not be negative")
|
|
94
|
+
if self.goodwill > self.total_assets:
|
|
95
|
+
raise BalanceSheetError(
|
|
96
|
+
f"goodwill ({self.goodwill}) exceeds total assets ({self.total_assets})"
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def book_equity(self) -> Money:
|
|
101
|
+
"""What the accounts say the equity is worth.
|
|
102
|
+
|
|
103
|
+
Frequently negative for a business that has been through a previous
|
|
104
|
+
buyout, which is not an error and is exactly the case that produces a
|
|
105
|
+
large goodwill number.
|
|
106
|
+
"""
|
|
107
|
+
return self.total_assets - self.total_liabilities
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@dataclass(frozen=True, slots=True)
|
|
111
|
+
class PurchaseAccounting:
|
|
112
|
+
"""How the price is allocated across what was bought.
|
|
113
|
+
|
|
114
|
+
``step_up`` writes identifiable assets up to fair value. ``step_up_tax_rate``
|
|
115
|
+
is the rate at which the resulting book/tax difference will be taxed; set it
|
|
116
|
+
to zero for an asset deal, where the step-up is deductible and no deferred
|
|
117
|
+
liability arises.
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
step_up: Money = ZERO
|
|
121
|
+
step_up_tax_rate: Money = ZERO
|
|
122
|
+
|
|
123
|
+
@classmethod
|
|
124
|
+
def of(cls, *, step_up: Numeric = 0, step_up_tax_rate: Numeric = 0) -> PurchaseAccounting:
|
|
125
|
+
return cls(step_up=money(step_up), step_up_tax_rate=money(step_up_tax_rate))
|
|
126
|
+
|
|
127
|
+
def __post_init__(self) -> None:
|
|
128
|
+
if self.step_up < 0:
|
|
129
|
+
raise BalanceSheetError("the asset step-up must not be negative")
|
|
130
|
+
if not (0 <= self.step_up_tax_rate <= 1):
|
|
131
|
+
raise BalanceSheetError("the step-up tax rate must be between 0 and 1")
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def deferred_tax_liability(self) -> Money:
|
|
135
|
+
"""Tax owed in future on the book/tax difference the step-up creates."""
|
|
136
|
+
return self.step_up * self.step_up_tax_rate
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@dataclass(frozen=True, slots=True)
|
|
140
|
+
class OpeningBalanceSheet:
|
|
141
|
+
"""The pro forma balance sheet at close.
|
|
142
|
+
|
|
143
|
+
Built by :meth:`recapitalise` rather than by hand, so every figure traces to
|
|
144
|
+
the transaction that produced it.
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
cash: Money
|
|
148
|
+
identifiable_assets: Money
|
|
149
|
+
goodwill: Money
|
|
150
|
+
deferred_financing_costs: Money
|
|
151
|
+
unamortised_issue_discount: Money
|
|
152
|
+
|
|
153
|
+
debt_at_face: Money
|
|
154
|
+
operating_liabilities: Money
|
|
155
|
+
deferred_tax_liability: Money
|
|
156
|
+
|
|
157
|
+
sponsor_equity: Money
|
|
158
|
+
rollover_equity: Money
|
|
159
|
+
expensed_at_close: Money
|
|
160
|
+
|
|
161
|
+
def __post_init__(self) -> None:
|
|
162
|
+
difference = self.total_assets - self.total_liabilities_and_equity
|
|
163
|
+
if difference != 0:
|
|
164
|
+
raise UnbalancedBalanceSheetError(
|
|
165
|
+
f"assets ({self.total_assets}) do not equal liabilities and equity "
|
|
166
|
+
f"({self.total_liabilities_and_equity}); the difference is {difference}"
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# -- Assets ----------------------------------------------------------
|
|
170
|
+
|
|
171
|
+
@property
|
|
172
|
+
def total_assets(self) -> Money:
|
|
173
|
+
return (
|
|
174
|
+
self.cash
|
|
175
|
+
+ self.identifiable_assets
|
|
176
|
+
+ self.goodwill
|
|
177
|
+
+ self.deferred_financing_costs
|
|
178
|
+
+ self.unamortised_issue_discount
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
# -- Liabilities and equity ------------------------------------------
|
|
182
|
+
|
|
183
|
+
@property
|
|
184
|
+
def total_liabilities(self) -> Money:
|
|
185
|
+
return self.debt_at_face + self.operating_liabilities + self.deferred_tax_liability
|
|
186
|
+
|
|
187
|
+
@property
|
|
188
|
+
def total_equity(self) -> Money:
|
|
189
|
+
"""Equity funded at close, less what was spent and never capitalised.
|
|
190
|
+
|
|
191
|
+
Transaction fees and any other closing payment that buys nothing are
|
|
192
|
+
charged here. They are cash out of the door on day one against which no
|
|
193
|
+
asset stands, so the equity the sponsor owns is worth less than the
|
|
194
|
+
cheque it wrote from the moment the deal signs.
|
|
195
|
+
"""
|
|
196
|
+
return self.sponsor_equity + self.rollover_equity - self.expensed_at_close
|
|
197
|
+
|
|
198
|
+
@property
|
|
199
|
+
def total_liabilities_and_equity(self) -> Money:
|
|
200
|
+
return self.total_liabilities + self.total_equity
|
|
201
|
+
|
|
202
|
+
# -- Read-across ------------------------------------------------------
|
|
203
|
+
|
|
204
|
+
@property
|
|
205
|
+
def net_debt(self) -> Money:
|
|
206
|
+
return self.debt_at_face - self.cash
|
|
207
|
+
|
|
208
|
+
@property
|
|
209
|
+
def goodwill_share_of_assets(self) -> Money:
|
|
210
|
+
"""Goodwill as a share of the opening asset base.
|
|
211
|
+
|
|
212
|
+
A high number is not a defect, but it is the part of the balance sheet
|
|
213
|
+
that is worth what the next buyer says it is worth, so it is the first
|
|
214
|
+
thing an impairment test comes for.
|
|
215
|
+
"""
|
|
216
|
+
if self.total_assets == 0:
|
|
217
|
+
return ZERO
|
|
218
|
+
return self.goodwill / self.total_assets
|
|
219
|
+
|
|
220
|
+
@classmethod
|
|
221
|
+
def recapitalise(
|
|
222
|
+
cls,
|
|
223
|
+
transaction: Transaction,
|
|
224
|
+
book: TargetBookBalanceSheet,
|
|
225
|
+
accounting: PurchaseAccounting | None = None,
|
|
226
|
+
) -> OpeningBalanceSheet:
|
|
227
|
+
"""Apply the transaction to the target's book position.
|
|
228
|
+
|
|
229
|
+
The consideration is the cheque to selling shareholders. Existing debt
|
|
230
|
+
is a liability the buyer assumes and repays at close with the proceeds
|
|
231
|
+
of the new structure, so it is netted inside the identifiable net assets
|
|
232
|
+
rather than added to the price.
|
|
233
|
+
"""
|
|
234
|
+
allocation = accounting or PurchaseAccounting()
|
|
235
|
+
valuation = transaction.valuation
|
|
236
|
+
|
|
237
|
+
if book.total_assets < valuation.existing_cash:
|
|
238
|
+
raise BalanceSheetError(
|
|
239
|
+
f"the book balance sheet holds {book.total_assets} of assets but the "
|
|
240
|
+
f"valuation was struck on {valuation.existing_cash} of cash, which is more"
|
|
241
|
+
)
|
|
242
|
+
if book.total_liabilities < valuation.existing_debt:
|
|
243
|
+
raise BalanceSheetError(
|
|
244
|
+
f"the book balance sheet carries {book.total_liabilities} of liabilities "
|
|
245
|
+
f"but the valuation was struck on {valuation.existing_debt} of debt, which "
|
|
246
|
+
f"is more"
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
non_cash_assets = book.total_assets - valuation.existing_cash
|
|
250
|
+
if book.goodwill > non_cash_assets:
|
|
251
|
+
raise BalanceSheetError(
|
|
252
|
+
f"goodwill ({book.goodwill}) exceeds the non-cash assets it sits inside "
|
|
253
|
+
f"({non_cash_assets})"
|
|
254
|
+
)
|
|
255
|
+
operating_liabilities = book.total_liabilities - valuation.existing_debt
|
|
256
|
+
|
|
257
|
+
dtl = allocation.deferred_tax_liability
|
|
258
|
+
identifiable = non_cash_assets - book.goodwill + allocation.step_up
|
|
259
|
+
capitalised_closing_costs = sum(
|
|
260
|
+
(item.amount for item in transaction.other_uses if item.capitalised), ZERO
|
|
261
|
+
)
|
|
262
|
+
expensed_closing_costs = sum(
|
|
263
|
+
(item.amount for item in transaction.other_uses if not item.capitalised), ZERO
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
# Fair value of the identifiable net assets acquired, with the existing
|
|
267
|
+
# debt netted off because the buyer takes it on and clears it at close.
|
|
268
|
+
identifiable_net_assets = (
|
|
269
|
+
valuation.existing_cash
|
|
270
|
+
+ identifiable
|
|
271
|
+
- operating_liabilities
|
|
272
|
+
- valuation.existing_debt
|
|
273
|
+
- dtl
|
|
274
|
+
)
|
|
275
|
+
goodwill = valuation.equity_purchase_price - identifiable_net_assets
|
|
276
|
+
|
|
277
|
+
cash = (
|
|
278
|
+
valuation.existing_cash
|
|
279
|
+
- transaction.cash_from_balance_sheet
|
|
280
|
+
+ transaction.cash_to_balance_sheet
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
return cls(
|
|
284
|
+
cash=cash,
|
|
285
|
+
identifiable_assets=identifiable + capitalised_closing_costs,
|
|
286
|
+
goodwill=goodwill,
|
|
287
|
+
deferred_financing_costs=transaction.financing_fees,
|
|
288
|
+
unamortised_issue_discount=transaction.original_issue_discount,
|
|
289
|
+
debt_at_face=transaction.total_debt,
|
|
290
|
+
operating_liabilities=operating_liabilities,
|
|
291
|
+
deferred_tax_liability=dtl,
|
|
292
|
+
sponsor_equity=transaction.sponsor_equity,
|
|
293
|
+
rollover_equity=transaction.rollover_equity,
|
|
294
|
+
expensed_at_close=transaction.transaction_fees + expensed_closing_costs,
|
|
295
|
+
)
|