The pro-formas you get with multifamily listings tell you what the seller wants you to see. Gross rent, a happy cap rate, maybe a generous NOI. They don’t tell you what you’d actually take home, what happens when rates move, what the exit looks like, or whether you’d be writing yourself a check every month to live there.
I’ve been looking at multifamily properties in Colorado as a house-hack play (live in one unit, rent the others, let the property pay most of your mortgage). After looking at maybe twenty listings, I realized I was doing the same back-of-napkin math every time — and getting it wrong half the time because I’d forget about PMI, or assume the wrong vacancy rate, or skip §121 tax implications entirely.
So I built my own underwriting tool. It’s a native macOS app I run locally. I forward listing emails from my agent, it parses them, enriches each property with public county and city data, runs seven different financial scenarios, and gives me a full decision-grade report in about 90 seconds.
This is what it does and how it works.
The problem it solves
Every listing pro-forma I’ve ever seen has three flaws:
- It uses gross rent, not effective. No vacancy. No turnover. No bad-debt allowance. Real-world effective rent runs 5–25% below gross depending on the rental strategy.
- It ignores capital expenditure. Roof, HVAC, plumbing — these have real annual reserve costs that don’t show up until they bite you for $15,000.
- It hides the financing reality. Property tax and insurance scale with price, not rent. On a $1M+ property, ~$1,000/mo of carry comes from price-tied costs regardless of how much rent you collect. A pro-forma that doesn’t break that out is hiding the real picture.
I wanted a tool that gave me the actual numbers, with every assumption visible and every line item auditable.
What it does
The tool walks a property top-to-bottom. Here’s what you see, in the order it shows up:
Financing & offer price
This is where I tune the deal. The panel has:
- A working offer price — what I’d actually pay, separate from the listed price
- Three break-even offer prices computed by binary-searching through the scenario math:
- House-hack break-even: the highest price where owning here costs me ≤ what I’d pay in rent elsewhere
- Cash-flow positive today: where the deal generates positive cash flow even while I’m living in one unit rent-free
- Self-sustaining post-move-out: where the property pays for itself after I move out and rent every unit
- Per-deal financing overrides — interest rate, PMI, down payment %, closing costs, property tax %, insurance % — because not every property gets the same quote
- Two dial sliders for hold horizon (1–30 years) and owner-occupancy (0–60 months), which drive the projection length and §121 use-test math
Headline KPIs (six click-to-expand tiles)
- Owner monthly (today) — what I’d write a check for each month after collecting rent from other units
- Fully rented CF/mo (post-move-out) — what the deal does once I’m out and every unit is at market
- DSCR — the lender’s debt-coverage ratio
- Cap rate — unlevered yield
- Cash to close — the wire amount, broken down into down payment + closing costs + buffer, with reserves shown separately
- Conservative cash flow — stressed for vacancy + rate + rents
Each tile expands into a full math breakdown when you click it. No black boxes.
Verdict + zoning
A six-signal scorecard rates each deal “strong / moderate / weak / poor” across owner cost vs rent, DSCR, legal/unit confidence, utility structure, STR dependency, and capex exposure.
The zoning section explains what zone the property is in, whether short-term rentals are allowed (primary vs non-primary), what the city’s color-coded STR map says, and links straight to the relevant city resources.
Units & rents
Per-unit cards (one per unit) with:
- Auto-calculated baseline rent from HUD Fair Market Rents × multiplier
- Editable market rent overrides for LT, MTR, and STR
- A Rentometer link-out pre-populated with the address and bedroom count
- Strategy toggle (LT / MTR / STR) — with a note explaining when each strategy kicks in (vacant unit → immediately; current lease → at expiry)
- Lease end date input
Mixed strategy results
Mixed mode lets each unit run its own strategy. The panel shows blended cash flow, owner cost, DSCR, and cap rate with a full waterfall breakdown.
Sensitivity heatmap
A 2D grid of what happens to the chosen KPI under combinations of rent shock (–15% to +10%) and rate shock (–1 to +1 percentage points). Green = better, red = worse. The bordered center cell is the deal as listed. The contour where green flips to red tells you how much movement the deal can absorb before it stops working.
Scenarios
A structured side-by-side comparison of Base / Fully Rented / Conservative — organized as Income → Carry → Operating expenses → Bottom line → Returns, with explicit + / − / = math operators on every line so you can read the calculation top-down.
STR & MTR upside panels
Each shows the explicit assumptions the model is using (multipliers, vacancy buffers, opex premiums, license fees) and then the full LT-vs-alt-strategy waterfall.
5-year wealth forecast
This is my favorite part. A stacked-bar chart that decomposes wealth at year N into four sources:
- Initial down payment
- Cumulative cash flow received
- Principal paydown (what your tenant pays down for you)
- Property appreciation
Watching those bars grow over five years shows you where the money actually comes from in real estate. Usually appreciation dominates early; paydown and CF compound later.
Exit scenarios at year N
For each exit path (hold, sell, 1031, HELOC) the model computes proceeds, selling costs, loan payoff, §121 exclusion (with the real 2-of-5 use test, not just ownership), depreciation recapture, capital gains, total tax, and net cash.
The §121 logic includes an “actionable hint” — if you’d fail the use test by a small margin, it tells you how many more months of occupancy you’d need to recover up to $X of exclusion.
The technology stack
Nothing fancy. Boring, durable, fast.
- Python 3.12 + FastAPI for the backend
- HTMX for the frontend, with server-rendered Jinja templates and partial swaps — no React, no client-side state, no build step
- SQLite in WAL mode for storage. Single file, ACID, runs forever.
- SQLAlchemy with Alembic for schema migrations
- pywebview wrapping the FastAPI app in a native macOS Cocoa window — so it looks and behaves like a real desktop app
- PyInstaller for distribution — packages everything into a
.app bundle that runs on any Mac without dependencies
- 175+ tests covering finance math, projections, exits, sensitivity, scoring, parsing — pinned with the kind of invariants that catch regressions (e.g., “NOI must always equal effective rent minus opex”)
The data sources
This is where the rigor actually lives. Every number traces back to a citable public source:
- HUD Fair Market Rents — for baseline per-bedroom market rents
- Apartment List + Zumper — for the rent-growth band (low / mid / high YoY)
- Bureau of Labor Statistics CPI — macro inflation anchor for rent component
- County ArcGIS REST API — for parcel data, tax/schedule numbers, prior sales history, ownership info, and mailing address (to detect absentee owners)
- City STR Zoning ArcGIS — for live short-term rental zoning lookup
- Gmail API — for ingesting forwarded listing emails (OAuth, token stored in macOS Keychain)
- Rentometer link-out — one-click pre-filled comp lookup for ground-truth rent verification
When I add an assumption to the model, I cite the source. When the source changes, I update the YAML.
How I built it
Honestly, the interesting part of this project isn’t the financial math — that’s well-trodden. What’s interesting is that I shipped something this thorough as a solo founder in a few weeks.
I built it with Claude Code. Agent-assisted development, but I wrote the requirements, defined the financial math, and reviewed every line of code that landed. The AI didn’t make it easier to be lazy — it made it possible to ship something with real depth. I’d sketch a feature, the agent would scaffold it, I’d verify the math against external calculators, write the test, then ship.
Halfway through I asked a separate agent to do a bug-hunter audit of the entire repository. It found ten real correctness issues — an SSRF vulnerability, a session leak, an inconsistent vacancy assumption between two code paths, an off-by-one in the owner-occupancy ramp, a §121 calculator that ignored the use test. I fixed all of them in one pass.
This is the part that makes AI-assisted dev different from previous productivity waves. Not “the AI writes my code.” More like “I get a senior engineering team’s worth of leverage for the parts I’d otherwise skip.”
Why local-first
The whole thing runs on my Mac. No cloud, no auth, no SaaS subscription, no telemetry. My deal data — addresses, offer prices, financing decisions — lives on my machine.
Three reasons:
- It’s mine. Real estate decisions involve sensitive financial information. There’s no reason that needs to live on someone else’s server.
- It’s durable. If the parcel-enrichment API I use disappears tomorrow, the tool still works. If my internet is down, the tool still works. Tools that depend on cloud services age fast.
- It’s instant. No login. No loading spinner. I click an email link, the deal opens, the math is done.
What’s next
Right now the tool is for me (and my wife). It does what I need it to do. I’ll probably keep tuning the multipliers and YAML configs as I see more deals and learn what was wrong.
Whether this becomes something I share with other people depends on whether they ask. For now, it’s the most useful piece of software I’ve ever built for my own use.
Way more useful than a Zestimate.