Nothing
test_that("lease_effective_rent reproduces a constant annuity", {
out <- lease_effective_rent(
cashflows = rep(100, 5),
discount_rate = 0.08,
timing = "arrears",
perspective = "landlord"
)
expect_equal(out$equivalent_annuity, 100, tolerance = 1e-10)
expect_equal(out$effective_rent, 100, tolerance = 1e-10)
})
test_that("lease_effective_rent captures concessions and area normalization", {
base <- lease_effective_rent(
cashflows = rep(100, 5),
discount_rate = 0.08,
area = 10,
timing = "arrears",
perspective = "landlord"
)
concessive <- lease_effective_rent(
cashflows = c(0, 100, 100, 100, 100),
discount_rate = 0.08,
area = 10,
timing = "arrears",
perspective = "landlord"
)
expect_lt(concessive$effective_rent, base$effective_rent)
expect_equal(base$effective_rent_per_area, base$effective_rent / 10, tolerance = 1e-10)
})
test_that("lease_effective_rent stays symmetric across landlord and tenant perspectives", {
landlord <- lease_effective_rent(
cashflows = rep(100, 4),
discount_rate = 0.07,
timing = "arrears",
perspective = "landlord"
)
tenant <- lease_effective_rent(
cashflows = rep(-100, 4),
discount_rate = 0.07,
timing = "arrears",
perspective = "tenant"
)
expect_equal(abs(landlord$effective_rent), abs(tenant$effective_rent), tolerance = 1e-10)
expect_equal(landlord$effective_rent, -tenant$effective_rent, tolerance = 1e-10)
})
test_that("underwrite_loan identifies an LTV binding constraint", {
uw <- underwrite_loan(
noi = 1e6,
value = 5e6,
rate_annual = 0.05,
maturity = 5,
type = "bullet",
dscr_min = 1.25,
ltv_max = 0.50,
debt_yield_min = 0.08
)
expect_equal(uw$binding_constraint, "ltv")
expect_equal(uw$max_loan, uw$max_loan_ltv, tolerance = 1e-10)
expect_equal(uw$implied_ltv, 0.5, tolerance = 1e-10)
expect_equal(
uw$payment_year1,
debt_built_schedule(uw$max_loan, 0.05, 5, type = "bullet")$payment[2],
tolerance = 1e-10
)
})
test_that("underwrite_loan identifies a DSCR binding constraint", {
uw <- underwrite_loan(
noi = 5e5,
value = 20e6,
rate_annual = 0.06,
maturity = 5,
type = "amort",
dscr_min = 1.25,
ltv_max = 0.80,
debt_yield_min = 0.03
)
expect_equal(uw$binding_constraint, "dscr")
expect_equal(uw$max_loan, uw$max_loan_dscr, tolerance = 1e-10)
expect_gte(uw$implied_dscr, 1.25 - 1e-6)
expect_equal(
uw$payment_year1,
debt_built_schedule(uw$max_loan, 0.06, 5, type = "amort")$payment[2],
tolerance = 1e-10
)
})
test_that("underwrite_loan identifies a debt-yield binding constraint", {
uw <- underwrite_loan(
noi = 5e5,
value = 20e6,
rate_annual = 0.03,
maturity = 5,
type = "bullet",
dscr_min = 1.25,
ltv_max = 0.90,
debt_yield_min = 0.08
)
expect_equal(uw$binding_constraint, "debt_yield")
expect_equal(uw$max_loan, uw$max_loan_debt_yield, tolerance = 1e-10)
expect_equal(uw$implied_debt_yield, 0.08, tolerance = 1e-8)
})
test_that("compare_financing_scenarios surfaces operations and terminal shares", {
dcf <- dcf_calculate(1e7, 0.05, 0.055, 5, 0.07)
cmp <- compare_financing_scenarios(
dcf_res = dcf,
acq_price = 1e7,
ltv = 0.60,
rate = 0.045,
maturity = 5
)
expect_true(all(c("ops_share", "tv_share") %in% names(cmp$summary)))
expect_true(all(abs(cmp$summary$ops_share + cmp$summary$tv_share - 1) < 1e-10))
})
test_that("tax_run_spv computes depreciation, exit gain, taxes, and after-tax equity flows", {
basis <- tibble::tibble(
year = 0:4,
noi = c(0, 140, 150, 160, 170),
capex = c(0, 0, 20, 0, 0),
interest = c(0, 30, 25, 20, 0),
sale_proceeds = c(0, 0, 0, 0, 900),
pre_tax_equity_cf = c(-1000, 110, 105, 120, 950)
)
spec <- tax_spec_spv(
corp_tax_rate = 0.25,
depreciation_spec = depreciation_spec(
acquisition_split = tibble::tribble(
~bucket, ~share, ~life_years, ~method, ~depreciable,
"land", 0.20, NA, "none", FALSE,
"building", 0.80, 4, "straight_line", TRUE
),
capex_bucket = "building",
start_rule = "full_year"
)
)
out <- tax_run_spv(basis, spec, acquisition_price = 1000)
tbl <- out$tax_table
expect_equal(tbl$tax_depreciation, c(0, 200, 205, 205, 205), tolerance = 1e-10)
expect_equal(tbl$book_value_close_pre_exit[5], 205, tolerance = 1e-10)
expect_equal(tbl$taxable_exit_gain_loss[5], 695, tolerance = 1e-10)
expect_equal(tbl$loss_cf_open[5], 235, tolerance = 1e-10)
expect_equal(tbl$loss_cf_used[5], 235, tolerance = 1e-10)
expect_equal(tbl$taxable_income_post_losses[5], 425, tolerance = 1e-10)
expect_equal(tbl$cash_is[5], 106.25, tolerance = 1e-10)
expect_equal(tbl$after_tax_equity_cf[5], 843.75, tolerance = 1e-10)
expect_equal(out$summary$total_cash_is, 106.25, tolerance = 1e-10)
})
test_that("tax_run_spv supports next-year depreciation and capped use of losses", {
basis <- tibble::tibble(
year = 0:3,
noi = c(0, 40, 300, 500),
capex = c(0, 0, 0, 0),
interest = c(0, 0, 0, 0),
sale_proceeds = c(0, 0, 0, 0)
)
spec <- tax_spec_spv(
corp_tax_rate = 0.30,
depreciation_spec = depreciation_spec(
acquisition_split = tibble::tribble(
~bucket, ~share, ~life_years, ~method, ~depreciable,
"land", 0.20, NA, "none", FALSE,
"building", 0.80, 2, "straight_line", TRUE
),
capex_bucket = "building",
start_rule = "next_year"
),
loss_rule = loss_rule(
carryforward = TRUE,
carryforward_years = Inf,
offset_cap_pct = 0.50
)
)
out <- tax_run_spv(basis, spec, acquisition_price = 1000)
tbl <- out$tax_table
expect_equal(tbl$tax_depreciation, c(0, 0, 400, 400), tolerance = 1e-10)
expect_equal(tbl$loss_generated[2], 0, tolerance = 1e-10)
expect_equal(tbl$loss_generated[3], 100, tolerance = 1e-10)
expect_equal(tbl$loss_cf_open[4], 100, tolerance = 1e-10)
expect_equal(tbl$loss_cf_used[4], 50, tolerance = 1e-10)
expect_equal(tbl$taxable_income_post_losses[4], 50, tolerance = 1e-10)
expect_equal(tbl$cash_is[4], 15, tolerance = 1e-10)
})
test_that("tax_basis_spv extracts a tax basis from a pre-tax case", {
deal <- deal_spec(
price = 10e6,
entry_yield = 0.055,
horizon_years = 5,
debt = debt_terms(ltv = 0.5, rate = 0.04, type = "bullet")
)
res <- analyze_deal(deal)
basis <- tax_basis_spv(res)
expect_true(all(c(
"year", "noi", "capex", "interest", "sale_proceeds",
"pre_tax_equity_cf", "acquisition_price"
) %in% names(basis)))
expect_equal(length(unique(basis$acquisition_price)), 1L)
expect_equal(unique(basis$acquisition_price), res$pricing$price_ht, tolerance = 1e-8)
})
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.