Skip to content

In-depth reference

The pro forma, line by line

Every pro-forma line — its exact label, the formula that produces it, what feeds it, and the sign convention — traced from inputs through NOI to the levered return.

The pro forma is not a spreadsheet you type into. It is the computed cash flow — the engine’s output, rendered. Every cell is a function of the inputs you set on the rent roll, the recovery matrix, the expense ledger, the debt terms, and the exit assumptions. This chapter documents each line: the label the workspace shows, the arithmetic behind it, the fields that feed it, and the sign it carries. When a number looks wrong, the fix is upstream — this page tells you exactly which input owns each line so you can find it.

Read this alongside Pro forma & analysis, which covers the tabs, toggles, and the vs-Broker workflow. This chapter is the derivation reference behind that surface.

The annual pro forma is a stack of signed rows. Revenue lines are positive. Deductions — vacancy, abatements, expenses, leasing costs, debt service — are carried negative in the ledger even though the underlying engine field is a positive magnitude. So “General Vacancy Loss” displays as −92,500 while the engine stores generalVacancy = 92,500. Two rows break that rule:

  • Base Rent Abatements is already stored negative by the engine, so it is displayed as-is (a reduction).
  • Absorption & Turnover Vacancy is stored as a positive magnitude and shown negated.

Subtotal rows — Scheduled Base Rental Revenue, Potential Gross Revenue, Effective Gross Revenue, Net Operating Income, Net Cash Flow, Cash Flow After Debt Service — are rendered bold. Everything between two subtotals foots to the lower subtotal exactly; there is no rounding slack, because each displayed value is roundCents-ed off the same monthly series before it is summed to the year.

The engine computes monthly, then rolls up to annual by summing each field across the fiscal year’s months. The monthly and annual pro formas therefore carry the same rows with the same formulas; the monthly grain simply exposes timing — the month a lease commences, an abatement burns off, or a vacancy hits. Where a line behaves differently monthly vs. annually, this chapter says so.


Revenue builds from the top of the rent roll down to Effective Gross Revenue in a fixed order. The occupied-tenant rent lines resolve first, then the vacant-space market rent, then the two structural haircuts.

Label: Potential Rental Revenue · Sign: positive

This is the engine’s potentialGrossRevenue field — gross scheduled rent for occupied space plus market rent on vacant space, before free rent is removed:

potentialGrossRevenue = scheduledBaseRental − abatements + absorptionVacancy

Because scheduledBaseRental already includes the (negative) abatement, adding it back and adding the absorption line means, equivalently:

Potential Rental Revenue = baseRent + stepRent + cpiRent + absorptionVacancy

It is the “everything is leased and paying” line: contract rent on occupied suites, plus what the vacant square footage would earn at market. The two lines directly beneath it strip the two things that keep you from collecting it — the vacant space itself, and any free rent.

Label: Absorption & Turnover Vacancy · Sign: negative (shown −absorptionVacancy)

The market rent attributed to space that is not yet leased. For each vacant suite the engine values the empty space at its market rent (from the suite’s Market Leasing Assumption, or the stated total for a ground/pad lease), and — if an absorption schedule is present — ramps that penalty down as the space leases up:

tenantVacancyMarketRent = fullVacancyRent × (1 − absorptionFactor)

fullVacancyRent is marketRentPSF × size / 12 per month (or the stated annual total ÷ 12 for a total-basis lease). The absorptionFactor interpolates from 0 (fully vacant) toward the scheduled target occupancy along the ramp. With no schedule, the full market rent is booked as vacancy every month until the suite commences. This line only accrues when absorption vacancy is enabled on the vacancy config; otherwise vacant space simply contributes zero and this row is $0.

Label: Base Rent Abatements · Sign: negative (stored negative, shown as-is)

Free rent — the concession months on a lease or a lease-up suite. The engine computes it in calcAbatement off the tenant’s abatement schedule against { base, step, cpi } for the month, and stores it as a negative number, so it reduces scheduled rent. Fully-abated months show the full base+step+CPI as a negative here and a matching positive in the rent components above, netting the suite to $0 collected while still counting it as occupied.

Label: Scheduled Base Rental Revenue · Sign: positive · Subtotal

The contract rent the model expects to bill occupied tenants, net of free rent:

scheduledBaseRental = baseRent + stepRent + cpiRent + abatements

This foots against the three rows above it — Potential Rental Revenue − Absorption & Turnover Vacancy + Base Rent Abatements = Scheduled Base Rental Revenue — by construction. Its four components come straight off the rent stack:

  • baseRent — the in-place contract rent for the month, from calcBaseRent(lease, state). Converted to a monthly dollar figure from whatever unit the rent roll carried ($/SF/yr, $/SF/mo, or annual total).
  • stepRent — the increment from contractual step/escalation schedules, calcStepRent. A step is expressed relative to base, so the sum baseRent + stepRent is the stepped contract rent in that month.
  • cpiRent — index-linked escalation, calcCPIRent, computed off basePlusStepMonthly and the prior cumulative CPI. The running CPI level resets to 0 when a lease rolls into a new (blended/option) term so the new term escalates off its own base, not the prior term’s.
  • abatements — as above, negative.

Label: Retail Sales Percent Revenue · Sign: positive

Overage / percentage rent for retail tenants, retailSalesRent. The engine computes it in calcRetailSalesRent from the tenant’s projected annual sales (grown by the 1-based analysis year) against the tenant’s breakpoint. Overage is recognized in the month it is earned — a reportingLagMonths value on the lease is captured for reference but not modeled, and the engine emits a warning when it is set, so you are never silently missing a lag you configured.

Label: Expense Reimbursements · Sign: positive

Tenant recoveries of operating expenses — reimbursementRevenue, summed across all tenants for the month. This is the output of the recovery engine: for each tenant it applies the recovery method (Net, base-year stop, gross-up, pooled), caps, and exclusions against the grossed-up reimbursable expense pool. The full mechanics live in Recoveries, end to end; on the pro forma it arrives as a single per-year total. When a computed management fee is flagged reimbursable, its marginal recovery is added into this line after the vacancy/credit-loss haircuts (a contractual pass-through is not occupancy-adjusted).

Label: Other Income · Sign: positive

Miscellaneous / additional revenue — miscellaneousRevenue — parking, storage, antenna, late fees, and anything else on the Additional Revenue ledger. Each source is booked at its stated amount (monthly or annualized ÷ 12), grown by its own inflation rate (or the general rate), and gated by any start/end month. An annualOverrides map keyed by fiscal year pins a specific year’s amount. Per Additional revenue, the sum of the per-source line items ties exactly to this total because both use the same per-source function.

Label: Potential Gross Revenue · Sign: positive · Subtotal

The full income base before structural loss — the engine’s potentialGrossIncome:

Potential Gross Revenue = scheduledBaseRental + retailSalesRent
+ reimbursementRevenue + miscellaneousRevenue

Label: General Vacancy Loss · Sign: negative (shown −generalVacancy)

The structural (as opposed to space-specific) vacancy allowance — the “you will never be 100% leased 100% of the time” haircut. Computed by calcGeneralVacancy as base × percent, where the base depends on the vacancy method:

Method Base the % applies to
PercentPGI Potential gross income (PGR: scheduled rent + retail + reimbursements + misc)
PercentSBR Scheduled base rental only (base + step + CPI, net of free rent)
PercentBaseReimb Scheduled base rental + reimbursements (the IC-memo commercial convention)
PercentAllRental All rental income (equivalent to PGI here)
None 0 — no general vacancy

Vacancy basis convention. Commercial deals default to a percentage of PGR (PercentPGI / PercentAllRental) — the haircut lands on the whole income base. The PercentSBR basis narrows it to scheduled base rent, which is the natural analog of the residential/multifamily gross potential rent convention where vacancy is a percentage of gross scheduled rent alone. PercentBaseReimb is the stricter commercial IC convention — base rent plus recoveries, with percentage and miscellaneous income left out of the haircut.

Two modifiers refine the raw haircut:

  • Excluded tenants. Credit tenants carved out of the general-vacancy haircut have their revenue (base rent and, for a PGI/all-rental basis, their reimbursements) removed from the base before the percentage is applied.
  • reduceVacancyByDowntime. When enabled, the raw vacancy is reduced by the absorption vacancy already booked above, so the two do not double-count: max(0, rawVacancy − absorptionVacancy).

Per-year overrides (generalVacancyAnnualOverrides) replace the percentage for a specific fiscal year.

Label: Credit Loss · Sign: negative (shown −creditCollectionLoss)

Collection / credit loss, calcCreditLoss(base, percent) = max(0, base × percent). It shares the vacancy method’s base: PercentSBR narrows both the vacancy and the credit-loss base to scheduled base rent, PercentBaseReimb narrows both to base + reimbursements, and every other method leaves credit loss on full PGI. Excluded-tenant carve-outs apply here too, via a separate credit-loss exclusion set, and per-year overrides (creditLossAnnualOverrides) replace the rate for a given year. When the credit-loss percentage is 0 (the default), this line is $0.

Label: Effective Gross Revenue · Sign: positive · Subtotal

The revenue the property actually realizes:

effectiveGrossRevenue = potentialGrossIncome − generalVacancy − creditCollectionLoss

If a reimbursable management fee is present, its recovery is added to EGR after this subtraction (see the management-fee note below), so a deal with a reimbursable fee shows an EGR marginally above the pure PGI − vacancy − credit figure. EGR is the base for the PercentEGI management fee and the numerator of economic occupancy.


Label: Total Operating Expenses · Sign: negative (shown −totalOperatingExpenses) · Expandable

The sum of every above-line operating expense for the month, expanded into one child row per expense line (its name from the ledger). Each line is:

inflatedAnnual = annualOverride[fiscalYear] ?? applyInflation(amount, month, method, factors)
actual = calcExpenseActual(expense, occupancyPercent, inflatedAnnual)

applyInflation grows the stated amount from the base to the current month at the line’s own inflation rate, a named growth schedule, or per-year growth overrides (which compound year over year and take precedence). calcExpenseActual then applies the fixed/variable split: a variable expense scales by occupancyPercent, a fixed expense does not.

Below-line expenses (position: 'belowLine' — reserves and similar) are excluded from this total and from NOI; they appear further down in the capital block. This matters for recoveries: below-line costs are never recovered, because recovering a cost that never hit NOI would book phantom revenue.

The management fee is special-cased. When managementFeeConfig.method is anything other than the inert Fixed (an expense-row fee):

  1. Any existing Management-category expense line is skipped in the loop above, so the computed fee is not double-counted against a stray management line.

  2. The fee is computed on a well-defined, non-circular base:

    Method Fee
    PercentEGI effectiveGrossRevenue × value
    PercentOpEx totalOperatingExpenses × value (the pre-fee, ex-Management total)
    PerSF value × totalRentableArea ÷ 12
    FixedAmount value ÷ 12
  3. Inflation is applied only to the dollar-denominated methods (PerSF, FixedAmount). A PercentEGI / PercentOpEx fee rides a base that already grows with the model, so inflating it again would compound growth twice.

  4. The fee is added to totalOperatingExpenses and appears as a Management Fee child row.

  5. If the fee is flagged reimbursable, its marginal recovery (a base pass vs. a with-fee pass on a throwaway cap tracker) is added to reimbursement revenue and to EGR — after vacancy and credit loss, since a contractual pass-through is not occupancy-adjusted.


Label: Net Operating Income · Sign: positive · Subtotal

netOperatingIncome = effectiveGrossRevenue − totalOperatingExpenses

Per the glossary, NOI is EGR less operating expenses, before capital costs, leasing costs, below-line reserves, and debt. It is the single most-scrutinized number in the model and the sole oracle for every capitalized metric — going-in cap, exit value, debt yield, DSCR. Nothing re-derives NOI from its parts; every surface reads annuals[].netOperatingIncome (see Trace any number).

The canonical NOI labels distinguish three period/scope flavors that are easy to conflate:

  • Year 1 NOI (EGI − OpEx)annuals[0].netOperatingIncome, the in-place / going-in NOI.
  • Forward NOI @ Exitmetrics.stabilizedNOI, the forward-12 NOI that capitalizes the reversion (not the last hold-year in-place NOI).
  • Yr-N NOI (in-place)annuals[hold-year].netOperatingIncome, kept for surfaces that genuinely want the final hold-year in-place figure.

These lines sit below NOI — they reduce cash flow but not NOI. On the overview cash flow they are grouped under Leasing & Capital Costs; the annual and monthly ledgers list them individually.

Label: Tenant Improvements · Sign: negative (shown −tenantImprovements)

TI dollars disbursed when a lease commences in the month. For a standard lease: (leasingCosts.tenantImprovements per SF) × size, booked in the lease’s in-month (or start month). For a blended roll, the blended lease’s tiPerSF × size. TI is a per-SF cost, so a 0-SF pad lease correctly books $0.

Label: Leasing Commissions · Sign: negative (shown −leasingCommissions)

LC dollars at commencement. A commission may be quoted as a flat $/SF or as a { percent, basis } rate (a percentage of total lease value); the engine normalizes it to $/SF at the lease’s rent and term via leasingCommissionToPerSf before posting lcPerSf × size. A total-basis blended re-lease books its stated absolute commission (lcTotal) so a ground/pad lease books the real number rather than $0.

Both TI and LC are rollover/lease-up costs: they hit in the month a suite (re)commences — a new lease, a renewal at rollover, or a vacant suite absorbed by its MLA. Years with no rollover carry $0 on both lines. The per-SF versions (tenantImprovementsPerSF, leasingCommissionsPerSF) are layered onto the annual summary as debt/operating metrics.

Label: Capital Expenditures · Sign: negative (shown −capitalExpenditures)

Non-leasing capital spend from the capital plan, calcCapExForMonth. Each item books by amountType:

  • lumpSum — the full amount in its scheduled month.
  • recurring — the amount every recurringIntervalMonths from recurringStartMonth.
  • percentRevenueamount × currentMonthPGR (a fraction of the month’s potential gross revenue — the top-line potentialGrossRevenue, not inflated again, since the revenue base already grew).
  • perUnitamount × totalRentableArea.

Nominal amount types (lumpSum / recurring / perUnit) are inflated to the period; percentRevenue is not. A per-year annualOverrides map pins a specific year. Debt-funded flagged items also drive loan draws (Good News Funding), but the spend still appears here.

Label: Capital Reserves / Below-Line Expenses · Sign: negative (shown −belowLineExpenses)

The belowLineExpenses total — reserve lines flagged position: 'belowLine'. Excluded from OpEx and NOI (above), they are subtracted here, between NOI and Net Cash Flow.


Label: Net Cash Flow · Sign: positive · Subtotal

Unlevered cash flow — the property’s cash generation before financing:

netCashFlowBeforeDebtService = netOperatingIncome
− belowLineExpenses − tenantImprovements − leasingCommissions − capitalExpenditures

This is the series the unlevered IRR and equity multiple run on. It is also the “Net Cash Flow” the overview groups the individual capital lines beneath.


The annual ledger splits debt service into two rows; the overview and monthly ledgers show a single Debt Service line.

  • Debt Service (Interest)−debtServiceInterest
  • Debt Service (Principal)−debtServicePrincipal

Both come from the debt ledger’s monthly amortization (advanceDebtLedgerMonth) across every loan. The combined Debt Service on the overview/monthly ledger is −debtService, which includes financing fees and any maturity balloon; the interest+principal split on the annual ledger is the recurring service. A separate debtServiceForDscr (recurring service, excluding origination fees, balloons, and refi payoffs) is what DSCR divides by. See Debt & financing, in depth for the ledger mechanics.

Label: Cash Flow After Debt Service · Sign: positive · Subtotal

Levered cash flow — what reaches equity:

cashFlowAfterDebtService = netCashFlowBeforeDebtService − debtService + loanFundingProceeds

loanFundingProceeds picks up draw-mechanic loan fundings in the month they fund. On the overview cash flow, Year 0 of this row is the levered equity outflow (−equityInvested), not the full unlevered acquisition cost — so the row ties to the engine’s levered series that drives the levered IRR. This is the series the levered IRR, equity multiple, cash-on-cash, and return-on-equity all consume.

Label: DSCR · Format: ratio · (annual ledger only)

dscr = netOperatingIncome ÷ debtServiceForDscr

Per year, using recurring debt service (origination fees, balloons, and refi payoffs excluded). A year with no debt service has no DSCR and renders -- rather than a misleading 0.00x. The header reports the hold’s average and minimum DSCR, computed across hold years only (reversion year excluded).


The returns are computed once, on the assembled monthly cash-flow series, in runCalculation. Every figure below is verified against lib/engine/returns.ts, exit-settlement.ts, resale.ts, and deal-metrics.ts.

Two dated series are assembled by buildExitSettlement, each ending at the sale month (the hold end — the reversion year is sliced off so it never leaks into a return):

Levered (drives levered IRR / EM / NPV):

[ { −equityInvested, at analysisStartDate },
{ cashFlowAfterDebtService[m], at month-end } for each held month ]

Unlevered (drives unlevered IRR / EM / NPV):

[ { −acquisitionCost, at analysisStartDate },
{ netCashFlowBeforeDebtService[m], at month-end } for each held month ]

where equityInvested = acquisitionCost − initialLoanProceeds (initial-month loan fundings only). Each monthly flow is dated at the end of its month (addMonthsUTC(month.date, 1)); the initial outflow is dated at the analysis start. The net resale proceeds are folded into the final held month: levered gets resale.net − loanPayoff − prepaymentPenalty, unlevered gets resale.net.

Both IRRs are XIRR on the dated series above: calcXIRR. The solver is Newton’s method with a bracketing-bisection fallback, discounting each flow by its actual elapsed time:

XNPV(rate) = Σ amount / (1 + rate) ^ yearsBetween(t0, t)
yearsBetween(a, b) = (b − a) in days / 365 // ACT/365

So t0 is the analysis start date carrying −equityInvested (levered) or −acquisitionCost (unlevered), flows are monthly and dated, and the day-count is ACT/365. XIRR is the annual rate that zeroes XNPV; it reproduces Excel’s =XIRR (see the XIRR Working report and the glossary).

Guardrails:

  • IRR is only reported when it converges and its magnitude is ≤ 200% (MAX_REPORTABLE_IRR = 2); otherwise the metric is suppressed and a warning is emitted (“Verify leverage, purchase price, and exit assumptions”).
  • Levered IRR is only computed when equity invested is meaningful — at least 0.1% of acquisition cost. At ≥100% leverage the levered block is suppressed with a warning rather than dividing by a near-zero equity base.

Label: Equity Multiple · levered and unlevered

equityMultiple = Σ(distributions) / equityInvested
= Σ(cashflows[1:]) / (−cashflows[0])

calcEquityMultiple treats index 0 as the single equity contribution at close and every later period as a net distribution (a negative period nets against distributions rather than being reclassified as a fresh capital call). This keeps the multiple invariant to intra-hold timing. Levered uses the levered series; unlevered uses the unlevered series. roi = equityMultiple − 1.

Going-In Cap Rate — annualized Year-1 NOI over price:

goingInCapRate = (annuals[0].netOperatingIncome × 12/monthsInYear₁) / acquisitionCost

The 12/monthsInYear₁ factor annualizes a stub first year (a mid-year close), so a 6-month year 1 does not halve the cap rate.

Terminal (Forward) NOI — the reversion year’s NOI. Default method is ForwardTwelve: the 12 months after the sale month — the buyer’s forward year — annualized. This is a real modeled year (scheduled steps, rollovers to market, downtime, reimbursement resets, expense inflation), not the last hold year grown by a flat rate.

Gross resale value — for a cap-rate exit:

grossResaleValue = terminalNOI / exitCapRate

where exitCapRate here is the stated assumption (resaleConfig.capRate). Other exit methods set the value directly: SpecifiedAmount uses the entered dollar figure; ValuePerUnit uses valuePerUnit × totalArea.

Selling costs & net proceeds:

sellingCosts = grossResaleValue × sellingCostsPercent + sellingCostsFixed
net = max(0, gross − sellingCosts − capitalToStabilize)

capitalToStabilize (TI + LC + stripped free rent) is netted only on the Stabilized reversion basis; on the default Actual basis it is $0.

Implied Exit Cap Rate — the engine-realized cap:

exitCapRate (implied) = terminalNOI / grossResaleValue

For a cap-rate exit this equals the assumption; for SpecifiedAmount / ValuePerUnit it diverges, which is exactly why the two carry distinct labels — Exit Cap Assumption vs. Implied Exit Cap — and never share one.

Net resale proceeds after debt (into the levered final flow):

netResaleProceedsAfterDebt = resale.net − loanPayoff − prepaymentPenalty

Derived in buildDealMetrics from series already assembled — every ratio guards its denominator and reports null, never NaN:

  • Debt Yield = annualized Year-1 NOI / initial loan principal.
  • Loan Constant = annualized Year-1 recurring debt service / initial loan principal.
  • Positive Leverage = going-in cap rate > loan constant.
  • LTV = initial loan principal / acquisition cost.
  • Yield on Cost = stabilized NOI / acquisition cost; Development Spread = yield on cost − exit cap assumption.
  • Gross Rent Multiplier = acquisition cost / annualized Year-1 scheduled base rental.
  • DCF Value = unlevered NPV + acquisition cost.
  • Peak Equity = the trough of the cumulative levered series; Payback Months = the first month the cumulative levered series turns ≥ 0.
  • Cash-on-Cash (per year) = cashFlowAfterDebtService / equityInvested, annualized on stub years.
  • Return on Equity (per year) = cash flow after debt service ÷ a cost-basis equity roll-forward (initial equity + cumulative principal amortized through the prior year, adjusted for net refinance cash).
  • Economic Occupancy (per year) = EGR ÷ gross potential income — read straight off the engine, never re-derived from a rent-only base.

When a broker pro forma was among the sources, the vs Broker tab reconciles your modeled Year 1 against the broker’s, line by line. Each row carries the broker figure, your figure, the dollar and percent delta, a severity chip, and the OM page citation. The reconciliation is built in broker-reconciliation-view-model.ts:

  • Delta = ours − broker; delta % = delta / |broker|.
  • Severity is assigned by magnitude — but only to unambiguous component rows and the clean OpEx subtotal. EGI/NOI subtotals are not flagged, because a broker that states vacancy and credit loss separately makes its gross-revenue sum diverge from your post-vacancy EGI/NOI for a benign reason.
  • A line is Reconciled when the delta is within tolerance — under $1,000 absolute, or within 2% of the broker figure — or when you accept it.
  • Expandable rows show your drivers: the base-rent line decomposes into base, step, CPI, percentage/sales rent, and abatements (which foot to scheduled base rental); expense rows decompose into category line items.

Work it top to bottom after an OM build — this is the fidelity check. See Pro forma & analysis for the Reconcile / Jump / Fix-with-agent actions.


One occupied tenant, one vacant suite, a full year — every line tying from inputs to the levered cash flow. Inputs:

  • Building: 100,000 SF total.
  • Tenant A: 80,000 SF, $20.00/SF/yr base, NNN, no steps/CPI/abatements this year. Annual base rent = 80,000 × $20.00 = $1,600,000.
  • Vacant suite: 20,000 SF, market rent $18.00/SF/yr, absorption vacancy enabled, no absorption schedule (fully vacant all year). Vacancy market rent = 20,000 × $18.00 = $360,000.
  • Recoveries: Tenant A reimburses $200,000 for the year.
  • Other income: parking, $50,000.
  • General vacancy: PercentPGI, 5%. Credit loss: 1% (PGI basis).
  • Operating expenses (above-line, ex-management): $400,000.
  • Management fee: PercentEGI, 3%, not reimbursable.
  • Capital: $50,000 reserve capex; no TI/LC (no rollover), no below-line.
  • Debt: $10,000,000 interest-only at 5.0% → $500,000 annual interest, $0 principal.
  • Acquisition cost: $18,000,000. Equity = 18,000,000 − 10,000,000 = $8,000,000.
  1. Revenue stack

    Line Amount Note
    Potential Rental Revenue 1,960,000 1,600,000 base + 360,000 absorption (pre-abatement)
    less Absorption & Turnover Vacancy (360,000) vacant suite at market
    less Base Rent Abatements 0 no free rent
    Scheduled Base Rental Revenue 1,600,000 ties: 1,960,000 − 360,000 − 0
    Retail Sales Percent Revenue 0
    Expense Reimbursements 200,000 Tenant A recoveries
    Other Income 50,000 parking
    Potential Gross Revenue 1,850,000 1,600,000 + 0 + 200,000 + 50,000
  2. Structural loss → EGR

    Line Amount Note
    less General Vacancy Loss (92,500) 5% × 1,850,000
    less Credit Loss (18,500) 1% × 1,850,000
    Effective Gross Revenue 1,739,000 1,850,000 − 92,500 − 18,500
  3. Operating expenses

    Line Amount Note
    Operating expenses (ex-mgmt) (400,000)
    Management Fee (52,170) 3% × 1,739,000 EGR
    Total Operating Expenses (452,170)
  4. NOI

    NOI = 1,739,000 − 452,170 = 1,286,830
  5. Capital → unlevered cash flow

    Line Amount
    Net Operating Income 1,286,830
    less Tenant Improvements 0
    less Leasing Commissions 0
    less Capital Expenditures (50,000)
    less Capital Reserves / Below-Line 0
    Net Cash Flow (before debt) 1,236,830
  6. Debt → levered cash flow

    Line Amount
    Net Cash Flow (before debt) 1,236,830
    less Debt Service (Interest) (500,000)
    less Debt Service (Principal) 0
    Cash Flow After Debt Service 736,830
  7. Ratios off this year

    • Going-in cap = 1,286,830 / 18,000,000 = 7.15%
    • DSCR = 1,286,830 / 500,000 = 2.57x
    • Debt yield = 1,286,830 / 10,000,000 = 12.87%
    • Year-1 cash-on-cash = 736,830 / 8,000,000 = 9.21%

Every subtotal foots to the lines above it, and every ratio reads off the same NOI and cash-flow figures the pro forma displays.


  • Monthly is the source. Each field is computed monthly, roundCents-ed, then summed to the year. Annual = Σ months. There is no independent annual computation to drift from the monthly grain.
  • Subtotals foot exactly. Scheduled Base Rental, Potential Gross Revenue, EGR, NOI, Net Cash Flow, and Cash Flow After Debt Service are each the sum of their component rows to the cent.
  • Signs are display conventions. The engine stores deductions as positive magnitudes (except abatements, stored negative); the ledger negates them for display. When you read a raw engine field in the inspector, expect the unsigned magnitude.
  • The reversion year is not the hold. It appears as a trailing column but is excluded from IRR, equity multiple, average occupancy, and DSCR.
  • Guarded ratios. Every derived ratio returns -- (null) rather than NaN/Infinity when its denominator is non-positive.

Troubleshooting: “this line looks wrong”

Section titled “Troubleshooting: “this line looks wrong””
  1. Potential Rental Revenue looks too high — it includes market rent on vacant space (absorption vacancy). Check the next row: Absorption & Turnover Vacancy strips it back out. If the vacant suite’s market rent is wrong, fix its MLA. Remember this row is pre-abatement gross rent, not what you collect.

  2. Scheduled Base Rental is below contract rent — free rent. Look at Base Rent Abatements; a fully-abated month shows the full rent as a negative here. Check the tenant’s abatement schedule on the rent roll.

  3. General Vacancy Loss seems double-counted with the vacant suite — the vacant suite is already valued in Absorption & Turnover Vacancy. If you want the structural haircut reduced by that, enable reduceVacancyByDowntime. Otherwise both apply by design.

  4. Vacancy or credit loss is haircutting the wrong income — check the vacancy method. PercentSBR narrows both to scheduled base rent; PercentBaseReimb to base + reimbursements; PercentPGI/PercentAllRental apply to full PGR. Credit loss follows the vacancy method’s base.

  5. Total Operating Expenses jumped and there is a Management Fee line — a computed fee (any method but Fixed) supersedes a Management-category expense line and is added on top. Confirm you are not also carrying a manual management expense; the engine skips the Management category to avoid double-counting, but only for the computed-fee case.

  6. NOI won’t tie to your hand calc — NOI is EGR − Total Operating Expenses, and Total Operating Expenses excludes below-line reserves. If your hand calc subtracted reserves before NOI, that is the gap; reserves come out below NOI, between NOI and Net Cash Flow.

  7. Levered cash flow / IRR is blank — equity invested may be below 0.1% of acquisition cost (≥100% leverage), or the IRR did not converge / exceeded 200%. The warnings panel names which. Check leverage, price, and exit assumptions.

  8. Exit value doesn’t match your cap × NOI — the exit capitalizes the forward-12 terminal NOI (the reversion year), not the last hold-year NOI. The header’s Forward NOI @ Exit is the number being capitalized; Implied Exit Cap = terminal NOI ÷ gross value will equal your assumption for a cap-rate exit and diverge for a specified-amount or per-unit exit.

  9. A number is off and you can’t tell why — click the cell. Every value is traceable to its inputs; see Trace any number.