Author: Package cre.dcf

# Leases and effective rent: verifying the internal coherence of the income chain

#### Package cre.dcf

## 1 Purpose

This vignette checks the link between lease inputs and the operating-income block of the DCF.
It formalizes two accounting relationships that underpin the package:

\[ \text{NOI}_t = \text{GEI}_t - \text{OPEX}_t \]

\[ \text{PBTCF}_t = \text{NOI}_t - \text{CAPEX}_t \]

where

- GEI (Gross Effective Income) represents the potential rent actually received after adjusting for vacancy, rent-free periods, and incentives,
- OPEX includes recurrent operating costs borne by the owner, and
- CAPEX captures periodic reinvestments necessary to maintain the property’s income capacity, and
- PBTCF is the property-before-tax cash flow used before debt.

It checks that rental assumptions are correctly transmitted to operating performance.

## 2 Building the case and extracting the cash-flow table

    # 1.1 Load a preset configuration including explicit lease events

    cfg_path <- system.file("extdata", "preset_default.yml", package = "cre.dcf")
    stopifnot(nzchar(cfg_path))

    cfg  <- yaml::read_yaml(cfg_path)
    case <- run_case(cfg)

    cf <- case$cashflows

    # 1.2 Verify that all required variables are present

    required_cols <- c("year", "gei", "opex", "capex", "noi", "pbtcf")
    stopifnot(all(required_cols %in% names(cf)))

`preset_default.yml` encodes a simple leasing pattern that is useful for testing how assumptions propagate into GEI and NOI over time.

## 3 Analytical structure of the income chain

    ## 2. Analytical structure of the income chain

    # 2.1 NOI as implemented in the engine: GEI - OPEX
    cf <- cf |>
      mutate(
        noi_from_gei_opex   = gei - opex,
        resid_noi_core      = noi_from_gei_opex - noi,
        pbtcf_from_noi_capex = noi - capex,
        resid_pbtcf         = pbtcf_from_noi_capex - pbtcf
      )

    gei_min <- min(cf$gei, na.rm = TRUE)
    gei_max <- max(cf$gei, na.rm = TRUE)
    noi_min <- min(cf$noi, na.rm = TRUE)
    noi_max <- max(cf$noi, na.rm = TRUE)

    max_abs_resid_core <- max(abs(cf$resid_noi_core), na.rm = TRUE)

    cat(
      "\nIncome chain check (NOI identity):\n",
      sprintf("• Minimum GEI: %s\n", formatC(gei_min, format = 'f', big.mark = " ")),
      sprintf("• Maximum GEI: %s\n", formatC(gei_max, format = 'f', big.mark = " ")),
      sprintf("• Minimum NOI: %s\n", formatC(noi_min, format = 'f', big.mark = " ")),
      sprintf("• Maximum NOI: %s\n", formatC(noi_max, format = 'f', big.mark = " ")),
      sprintf("• Max |(GEI - OPEX) - NOI|: %s\n",
              formatC(max_abs_resid_core, format = 'f', big.mark = " ")),
      sprintf("• Max |(NOI - CAPEX) - PBTCF|: %s\n",
              formatC(max(abs(cf$resid_pbtcf), na.rm = TRUE), format = 'f', big.mark = " "))
    )

    ##
    ## Income chain check (NOI identity):
    ##  • Minimum GEI: 0.0000
    ##  • Maximum GEI: 204 020.0000
    ##  • Minimum NOI: 0.0000
    ##  • Maximum NOI: 204 020.0000
    ##  • Max |(GEI - OPEX) - NOI|: 0.0000
    ##  • Max |(NOI - CAPEX) - PBTCF|: 0.0000

`resid_noi_core` measures the gap from the accounting identity. In a clean setup, it should be numerically close to zero.

## 4 Logical consistency checks

The vignette focuses on coherence conditions that should hold across a broad range of strategies, including transitional years.

    ## 3. Logical and accounting consistency checks

    # 3.1 Finiteness
    stopifnot(all(is.finite(cf$gei)))
    stopifnot(all(is.finite(cf$opex)))
    stopifnot(all(is.finite(cf$capex)))
    stopifnot(all(is.finite(cf$noi)))

    # 3.2 Non-negative OPEX / CAPEX
    stopifnot(min(cf$opex,  na.rm = TRUE)  >= -1e-8)
    stopifnot(min(cf$capex, na.rm = TRUE)  >= -1e-8)

    # 3.3 NOI never exceeds GEI when costs are non-negative
    stopifnot(all(cf$noi <= cf$gei + 1e-8))

    # 3.4 NOI core identity: GEI - OPEX == NOI
    stopifnot(all(abs(cf$resid_noi_core) < 1e-6))

    # 3.5 PBTCF identity: NOI - CAPEX == PBTCF
    stopifnot(all(abs(cf$resid_pbtcf) < 1e-6))

    cat(
      "\n✓ Accounting checks passed:\n",
      "  • NOI in the engine is equal to GEI minus OPEX.\n",
      "  • PBTCF is equal to NOI minus CAPEX.\n",
      "  • OPEX and CAPEX remain non-negative, and NOI never exceeds GEI.\n"
    )

    ##
    ## ✓ Accounting checks passed:
    ##    • NOI in the engine is equal to GEI minus OPEX.
    ##    • PBTCF is equal to NOI minus CAPEX.
    ##    • OPEX and CAPEX remain non-negative, and NOI never exceeds GEI.

These tests ensure that:

- GEI, OPEX, CAPEX, and NOI are all finite;

- cost blocks are not spuriously negative;

- NOI does not exceed GEI when costs are non-negative;

the identities \[NOI_t = GEI_t - OPEX_t\] and \[PBTCF_t = NOI_t - CAPEX_t\] hold up to numerical tolerance.

## 5 Sign and distribution of NOI

In many real cases, especially for value-added or opportunistic strategies, NOI can be temporarily negative. It is therefore more useful to describe its distribution than to force it to stay positive.

    ## 4.1 Share of periods with negative NOI

    neg_noi_share <- mean(cf$noi < 0, na.rm = TRUE)

    cat(
    "\nNOI sign check:\n",
    sprintf("• Share of periods with NOI < 0: %.1f%%\n", 100 * neg_noi_share),
    if (neg_noi_share > 0)
    "  --> Indicates at least one transitional year with negative operating result (vacancy, works, etc.).\n"
    else
    "  --> All periods exhibit non-negative operating result in this configuration.\n"
    )

    ##
    ## NOI sign check:
    ##  • Share of periods with NOI < 0: 0.0%
    ##    --> All periods exhibit non-negative operating result in this configuration.

This section does not impose an extra constraint. It simply shows whether the income profile includes transitional loss-making years.

## 6 Illustration of the income chain

    cf |>
      select(year, gei, opex, capex, noi, pbtcf,
             noi_from_gei_opex, pbtcf_from_noi_capex, resid_noi_core, resid_pbtcf) |>
      head(10) |>
      knitr::kable(
        digits  = 2,
        caption = "GEI -> NOI -> PBTCF identities (first 10 years)"
      )

GEI -> NOI -> PBTCF identities (first 10 years)
year | gei | opex | capex | noi | pbtcf | noi_from_gei_opex | pbtcf_from_noi_capex | resid_noi_core | resid_pbtcf
--- | --- | --- | --- | --- | --- | --- | --- | --- | ---
0 | 0.00 | 0.00 | 0.00 | 0.0 | 0.0 | 0.0 | 0.0 | 0 | 0
1 | 200000.00 | 0.00 | 0.00 | 200000.0 | 200000.0 | 200000.0 | 200000.0 | 0 | 0
2 | 202000.00 | 0.00 | 0.00 | 202000.0 | 202000.0 | 202000.0 | 202000.0 | 0 | 0
3 | 204020.00 | 0.00 | 0.00 | 204020.0 | 204020.0 | 204020.0 | 204020.0 | 0 | 0
4 | 61818.06 | 61818.06 | 309090.30 | 0.0 | -309090.3 | 0.0 | -309090.3 | 0 | 0
5 | 197714.76 | 0.00 | 29657.21 | 197714.8 | 168057.5 | 197714.8 | 168057.5 | 0 | 0

This table makes the GEI–OPEX–CAPEX–NOI cascade explicit in the time dimension and shows how the residual remains numerically negligible.

## 7 Interpretation

The numerical checks and the illustrative table jointly indicate that:

Gross Effective Income (GEI) correctly translates the contractual rent schedule into cash inflows, after adjusting for vacancy, rent-free periods, and any explicit incentives embedded in the lease events of preset_default.yml;

Operating expenses (OPEX) are deducted in a mechanically consistent way to obtain NOI in each period;

CAPEX then turns NOI into PBTCF without hidden adjustments;

the residuals of the GEI -> NOI and NOI -> PBTCF identities are effectively zero, confirming the internal accounting closure of the model.

From an analytical standpoint, this vignette demonstrates that lease-level assumptions (areas, headline rents, indexation, renewal or relocation events, vacancy durations, capex per square metre) propagate transparently into the operating-income block of the DCF engine.

In applied work, such validation is essential: it ensures that observed differences in NOI trajectories across scenarios or assets can be interpreted as stemming from genuine differences in lease structure and asset management strategy rather than from hidden computational artefacts.