Skip to main content
CFDLAcademy
All chapters

Part 4 · The CRE capstone · Chapter 22 of 28

Capstone: revenue

A building earns like a portfolio of promises, each on its own paper. This chapter writes Harbor Point's rent roll — five revenue claims and two deductions — almost entirely in the pack's vocabulary, because these are the standard leases chapter 19 said contracts exist for. Watch two things as it assembles. First, how each contract's terms carry exactly the numbers a leasing brief would state. Second, how the storylines stay separate: the reviewer who cares about the retail deal reads the retail contracts and nothing else.

The anchor lease

entity asset harbor : CRE.Asset.RealProperty {
  state lease_up
  stabilized_occupancy = 0.95
}

// The anchor office lease: 1.86M/yr, leasing up over twelve months.
contract cre.lease on entity asset.harbor {
  term 2027-07..2030-12
  terms {
    rent = 155000
    lease_up_months = 12
  }
}

Lease-up is stated on the lease. The term says when leasing begins and lease_up_months says how long it takes; the pack applies chapter 9's clamp ramp from the term's start and publishes it as the building's occupancy, so a variable expense can read asset.harbor.occupancy rather than restate it. No date is written twice: twelve months from a July 2027 start puts stabilization at mid-2028, which is why the skeleton's phases drew the lease-up phase where they did.

The asset starts state lease_up, and its stabilized_occupancy says where the property machine moves it to stabilized: when the published occupancy reaches 95%. An asset that omits it is stabilized at full occupancy, 100%, so state it whenever the underwriting calls a lower level stabilized.

The retail suite, at unit grain

Chapter 16's entity-grain decision said retail behaves differently, so it gets the lease-by-lease contract with the full institutional term sheet:

// The retail suite: escalating, three months free, TI/LC on day one,
// expense recoveries above a stop at the tenant's pro-rata share.
contract cre.lease_unit.retail on entity asset.harbor {
  term 2027-10..2030-12
  terms {
    rent_year = 480000
    escalation = 0.03
    free_rent_months = 3
    ti_total = 250000
    lc_total = 90000
    opex_year = 950000
    opex_escalation = 0.03
    expense_stop_year = 950000
    pro_rata_share = 0.2
  }
}

One contract, four streams in the lowering — and each is a lesson from earlier in the course arriving as a term. Base rent escalates 3% on lease anniversaries (the stepped claim from chapter 9, not the smooth one). Abatement books the three free months as their own deduction line, rather than net them invisibly into rent. Gross potential revenue and the incentive both stay visible, which is how an institutional pro forma reports and how a reviewer audits a concession. Recoveries reimburse opex above the stop at the tenant's 20% share. They are zero in the base year by construction, because the stop equals year-one opex, and they grow as opex escalates past it — a term sheet's most fiddly clause, in five terms. TI/LC lands as capital on day one. The .retail suffix names this lease's streams (cre.unit.base_rent.retail), so a second suite later is another contract, not a renaming exercise.

The recovery clause here is the estimate form: a stated opex_year and stop. A unit lease can instead bill the building's actual expenses. recoveries = "gross", "net", "nnn" or "modified_gross" names the structure, and the lease bills its pro_rata_share of the expenses of its recovery_pool above a stop: expense_stop_psf on the premises, or a base_year's actual expenses. A cam_cap_pct caps their growth, and gross_up_occupancy grosses the variable share up where the building is less full. The rent itself may be stated as the agreement states it: rent_psf on the premises' area, or rent_per_unit_month on a count of units (count). A unit lease that names a market set, market_leasing = inputs.<set>, rolls at expiry: its unit moves to the rollover state, and the set decides whether it renews, is re-let or goes vacant, or the lease's outcome says which.

Overage, other income, and the deductions

// Overage rent on the retailer's sales above the natural breakpoint.
contract cre.percentage_rent.retail on entity asset.harbor {
  term 2028-01..2030-12
  terms {
    sales_year = 6000000
    breakpoint_year = 4800000
    overage_pct = 0.06
    sales_growth = 0.03
  }
}

// Parking and signage.
contract cre.revenue_line on entity asset.harbor {
  term 2027-07..2030-12
  terms {
    amount = 18000
  }
}

// Property operating costs, escalating.
contract cre.opex_line on entity asset.harbor {
  term 2027-07..2030-12
  terms {
    amount_year = 950000
    growth_rate = 0.03
  }
}

// A stabilized-vacancy allowance once operating.
contract cre.vacancy_loss on entity asset.harbor {
  term 2028-07..2030-12
  terms {
    rate = 0.05
    potential_gross_year = 2560000
  }
}

Percentage rent is chapter 9's guarded kicker as a contract — 6% of sales above the breakpoint, the guard inside the pack's max(0, …). The vacancy allowance runs only from stabilization. During lease-up, vacancy is in the ramp, and an allowance on top would double-count the same emptiness. That is precisely the kind of claim-overlap a reviewer reading storyline-by-storyline catches, and a formula auditor does not.

What the numbers say

Run the checkpoint. The office lease totals 5,657,500 — check the shape, not just the sum: half-rent months climbing the ramp, then level. Retail base rent runs 1.6 million with the abatement's −120,000 beside it (three months × 40,000, the anchor to verify by hand this chapter). Recoveries are small and start at zero — if that zero surprised you, reread the stop; the term sheet's economics are working exactly as written. And the model's total is still −13.3 million, because a rent roll is not a deal: nothing has funded the 17.4 million yet. NOI now exists, though — the pack derives it from these categorized streams — and NOI is what the next two chapters finance and sell on.

The pack's report set is already in the results. Three statements: operating, by month; operating_annual, by year; and sources_and_uses. Three slices: capitalization, unlevered and levered, the last two each with an IRR and a multiple. The domain.cre metrics include noi, dscr, leasing_costs and expense_ratio. A rent roll or a debt schedule is a table over the results rather than a statement: each lease is in graph.contracts with its subject, term and rent, and each loan publishes its balance as an account.

What can go wrong

Terms that drift from the phases. The lease starts at 2027-07 and the lease-up phase ends at 2028-06 — today those agree with the lease's term and lease_up_months = 12. Change one without the others and the model still compiles; only a reader notices the deal disagreeing with itself. Keep the story in one place (the phases) and derive where you can.

A vacancy allowance during lease-up. This is the double-count described above, and the single most common CRE pro forma error. In this model it is impossible to make silently, because the vacancy contract's term states where it applies.

A blended retail claim. The alternative to lease_unit.retail was folding retail into the anchor's rent. Chapter 16's test said no — different behavior, different grain — and the free-rent negotiation that produced the abatement line is the evidence: you could not have stated it inside a blend.

Exercises

Exercise

Build the rent roll

Add Harbor Point's five revenue claims and its operating costs, each on entity asset.harbor. The asset starts in lease-up; state its stabilized_occupancy (0.95) so the property machine can move it to stabilized.

  1. Add the anchor office lease: 155,000 a month, with a twelve-month lease-up.
  2. Add the retail suite as a cre.lease_unit. Give it escalating rent, three months free, and TI/LC on day one. Set recoveries above a 950,000 stop at a 20% share.
  3. Add overage rent on the retailer's sales.
  4. Add parking as other income, cre.revenue_line.
  5. Add property opex that escalates at 3%.
  6. Add a 5% stabilized vacancy allowance.

Check two shapes in the series after the run:

  • The office lease climbs its ramp through mid-2028.
  • The retail abatement line offsets exactly the three free months.

The model is still deeply negative — revenue has arrived, but nothing has funded the build. Funding is chapter 22's job.

Loading exercise…

Then, on your own:

  1. Add a second retail suite — cre.lease_unit.kiosk on the same asset, 120,000 a year, starting 2028-01, one free month, no recoveries — and confirm the suffix keeps its streams separate in the results. One more lease is one more contract: that is unit grain paying rent.
  2. The retailer's broker calls: sales are tracking 5.4 million, not 6. Change one term, rerun, and find every number that moved. The blast radius of a term is the pack's category discipline made visible.