Period-by-period mechanical debt-waterfall engine for structured finance
Project description
waterfall-py 💧
Period-by-period mechanical debt-waterfall engine for structured finance.
waterfall-py runs a mechanical, auditable cash-flow waterfall over a user-supplied CFADS stream and a capital stack of senior + mezzanine + residual equity. It is a tight core: it computes what the deal terms and the CFADS imply, period by period, and proves the arithmetic with built-in validation assertions. It does not project cash flows, make credit judgments, or model anything on the Limitations list.
Mechanical waterfall computation only. Not a credit recommendation. CFADS inputs are user-supplied projections, not modeled cash flows.
The single source of truth for every modeling decision is
docs/methodology.md, a copy of which is bundled in the
wheel and locatable via waterfall.get_methodology_path().
Installation
pip install waterfall-py
Both import waterfall_py and import waterfallpy resolve to the same package
as import waterfall.
Quickstart
from datetime import date
from waterfall import run, Deal, Tranche, ReserveConfig, CovenantConfig, SweepConfig, SweepBand
senior = Tranche(name="Term A", tranche_type="senior", principal=10_000_000,
coupon=0.06, day_count="ACT/360", amort_type="fully_amortizing",
term_periods=8)
mezz = Tranche(name="Mezz", tranche_type="mezzanine", principal=2_000_000,
coupon=0.10, amort_type="bullet", term_periods=8)
equity = Tranche(name="Sponsor Equity", tranche_type="equity", principal=3_000_000)
deal = Deal(
deal_close_date=date(2024, 1, 1),
operations_start_date=date(2024, 1, 1),
period_frequency="Q", # M / Q / SA / A
deal_type="PF", # PF or CRE
tranches=[senior, mezz, equity],
cfads_stream=[1_400_000] * 8, # one operating CFADS per period
data_currency="USD",
reporting_basis="calendar",
reserves=[ReserveConfig(reserve_type="DSRA", months_dsra=6, opening_balance=750_000)],
covenants=[CovenantConfig(metric="DSCR", trap=1.10, default=1.00)],
sweep=SweepConfig(bands=[SweepBand(4.0, 1.0), SweepBand(3.0, 0.5)]),
)
result = run(deal)
from waterfall.report import export
df = export.to_period_dataframe(result) # native DataFrame
print(export.render_text(result)) # text report (opens with the disclaimer)
export.to_excel(result, "deal.xlsx") # openpyxl workbook
Waterfall Priority (LOCKED)
Each operating period, operating cash (CFADS) — supplemented at the steps they fund by non-CFADS sources — is applied in one acyclic priority order:
1. Senior fees & administrative expenses
2. Senior debt service (interest, then scheduled principal, pari-passu pro-rata)
shortfall covered by: DSRA draw, then equity cash-injection cure
3. Required reserve funding / replenishment (DSRA to required, then others)
4. Mezzanine debt service
5. Mandatory ECF sweep — ECF ≡ cash remaining after steps 1–4; leverage-banded;
forced to 100% under a cash-trap
6. Permitted junior uses from retained ECF (6a capex / 6b top-ups / 6c distributions)
7. Residual to equity (absent an active cash-trap)
Reserve releases and event proceeds are applied on a separate proceeds path (to outstanding debt in priority, then equity — the equity leg gated under a cash-trap while debt remains). CFADS stays operating-only; every non-CFADS source is recorded separately.
Built-in validation assertions (primary defensibility)
Every period, the engine asserts — raising a typed exception on any failure:
- Source/use tie-out at close — sources = uses
- Period cash-conservation — Σ all modeled sources = Σ all modeled uses (enumerated from the per-period ledger)
- Principal trace — opening + facility draws − scheduled amort − prepayments − sweeps = closing, per tranche
- Interest reconciliation — rate × average balance × day-count fraction = period interest
- Reserve roll-forward — closing = opening + funding + top-ups − draws − releases, per reserve
- Capital-account roll-forward — opening + contributions − distributions = ending
Covenants
DSCR, LLCR, PLCR (PF only), LTV, debt yield, Debt/EBITDA, fixed-charge and interest coverage are defined inline by formula. A zero denominator (e.g. debt fully repaid) reports n/a — never ∞, 0, or a raised error. Each covenant carries a three-tier cushion: performance / trap / default. Every breach flag is labeled a mechanical test result computed by the engine from user-supplied CFADS and the deal terms — never a legal conclusion.
Out of scope (raise UnsupportedFeatureError)
B-piece / A-B mechanics, preferred equity, construction-period modeling, sculpted-to-DSCR amortization, defeasance, OID, multi-currency, ACT/ACT day-count, tax-credit / SPV-tax modeling, and NOI/CFADS projection. See the Limitations section of the methodology.
Running tests
PYTHONPATH=. pytest tests/ -v
License
MIT — Jaypatel1511
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file waterfall_py-0.2.0.tar.gz.
File metadata
- Download URL: waterfall_py-0.2.0.tar.gz
- Upload date:
- Size: 68.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5e5817bac1851d51a67a6e957e7f83a409cf0bc706158a63de3c29629b6cb820
|
|
| MD5 |
c31030abc07aaf425065474ae18ee728
|
|
| BLAKE2b-256 |
92ba63b33e528d55be203a0895cb97fb87d067e86c115c5836f2f41226df116d
|
Provenance
The following attestation bundles were made for waterfall_py-0.2.0.tar.gz:
Publisher:
release.yml on Jaypatel1511/waterfall-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
waterfall_py-0.2.0.tar.gz -
Subject digest:
5e5817bac1851d51a67a6e957e7f83a409cf0bc706158a63de3c29629b6cb820 - Sigstore transparency entry: 2169721289
- Sigstore integration time:
-
Permalink:
Jaypatel1511/waterfall-py@6a15aa603d869528209aa4a5a973659ed9d0a460 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Jaypatel1511
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6a15aa603d869528209aa4a5a973659ed9d0a460 -
Trigger Event:
push
-
Statement type:
File details
Details for the file waterfall_py-0.2.0-py3-none-any.whl.
File metadata
- Download URL: waterfall_py-0.2.0-py3-none-any.whl
- Upload date:
- Size: 53.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8d6e9f0eef15670e6c6f0105049378c3030955297088ddde9a1f7cb514e127a5
|
|
| MD5 |
5a24b4e159fd1181ed7ceb579a729813
|
|
| BLAKE2b-256 |
879b470fd227825797523a7f1dde9dd3fbaeb972f549d15f58b99484d6633b69
|
Provenance
The following attestation bundles were made for waterfall_py-0.2.0-py3-none-any.whl:
Publisher:
release.yml on Jaypatel1511/waterfall-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
waterfall_py-0.2.0-py3-none-any.whl -
Subject digest:
8d6e9f0eef15670e6c6f0105049378c3030955297088ddde9a1f7cb514e127a5 - Sigstore transparency entry: 2169721305
- Sigstore integration time:
-
Permalink:
Jaypatel1511/waterfall-py@6a15aa603d869528209aa4a5a973659ed9d0a460 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Jaypatel1511
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6a15aa603d869528209aa4a5a973659ed9d0a460 -
Trigger Event:
push
-
Statement type: