Financial KPIs Reference¶
This reference document provides detailed explanations of all financial Key Performance Indicators (KPIs) calculated by the Real Estate Investor application.
Overview¶
The application computes five primary financial metrics for investment properties:
- Net Operating Income (NOI) — Annual income minus operating expenses
- Capitalization Rate (Cap Rate) — Return on property value
- Cash-on-Cash Return — Return on cash invested
- Internal Rate of Return (IRR) — Time-weighted return
- Debt Service Coverage Ratio (DSCR) — Ability to cover debt payments
All calculations use Decimal precision for currency values to ensure accurate financial computations.
Net Operating Income (NOI)¶
Definition¶
Net Operating Income represents the annual income generated by a property minus all operating expenses, excluding debt service and capital expenditures.
Formula¶
Or:
Implementation¶
Located in investor_app/finance/utils.py:
def noi(monthly_income: Decimal, monthly_expenses: Decimal) -> Decimal:
return to_decimal(monthly_income) * Decimal(12) - to_decimal(
monthly_expenses
) * Decimal(12)
Calculation Details¶
-
Monthly Income is calculated as the sum of all rental income adjusted for vacancy:
-
Monthly Expenses include all operating expenses normalized to monthly amounts:
- Annual expenses are divided by 12
-
Monthly expenses are used as-is
-
Both values are annualized by multiplying by 12
Example¶
Property details: - Monthly rent: $2,500 - Vacancy rate: 5% (0.05) - Annual property tax: $4,200 - Annual insurance: $1,200 - Monthly maintenance: $200
Calculation:
Effective Monthly Income = $2,500 × (1 - 0.05) = $2,375
Annual Income = $2,375 × 12 = $28,500
Monthly Expenses = ($4,200/12) + ($1,200/12) + $200 = $350 + $100 + $200 = $650
Annual Expenses = $650 × 12 = $7,800
NOI = $28,500 - $7,800 = $20,700
Interpretation¶
- Positive NOI: Property generates more income than operating expenses
- Negative NOI: Property operates at a loss (before debt service)
- Higher NOI: Better property performance
Notes¶
- NOI excludes mortgage payments (debt service)
- NOI excludes capital expenditures (major repairs, improvements)
- NOI includes vacancy allowance through the effective gross income calculation
Capitalization Rate (Cap Rate)¶
Definition¶
The capitalization rate (cap rate) measures the rate of return on a real estate investment based on the property's net operating income.
Formula¶
Expressed as a percentage:
Implementation¶
Located in investor_app/finance/utils.py:
def cap_rate(annual_noi: Decimal, purchase_price: Decimal) -> Decimal:
if to_decimal(purchase_price) == 0:
return Decimal("0")
return to_decimal(annual_noi) / to_decimal(purchase_price)
Calculation Details¶
- Calculate annual NOI (see above)
- Divide NOI by the property's purchase price
- Result is stored as a decimal (e.g., 0.0591 represents 5.91%)
Example¶
Using the previous example: - NOI: $20,700 - Purchase price: $350,000
Calculation:
Interpretation¶
- Higher cap rate: Better return relative to property value
- Lower cap rate: Lower return, but potentially in a more stable/appreciating market
- Industry benchmarks:
- 4-6%: Low-risk, stable markets (Class A properties)
- 6-8%: Moderate-risk markets (Class B properties)
- 8%+: Higher-risk markets (Class C properties)
Uses¶
- Compare properties in the same market
- Estimate property value:
Property Value = NOI ÷ Cap Rate - Assess investment opportunities
Notes¶
- Cap rate does not account for financing costs
- Does not consider property appreciation or depreciation
- Does not factor in time value of money
- Best used for comparing similar properties in similar markets
Cash-on-Cash Return¶
Definition¶
Cash-on-Cash return measures the annual pre-tax cash flow relative to the total cash invested in the property.
Formula¶
Where:
Annual Cash Flow = NOI - Annual Debt Service
Total Cash Invested = Down Payment + Closing Costs + Initial Repairs
Implementation¶
Located in investor_app/finance/utils.py:
def cash_on_cash(annual_cash_flow: Decimal, total_cash_invested: Decimal) -> Decimal:
if to_decimal(total_cash_invested) == 0:
return Decimal("0")
return to_decimal(annual_cash_flow) / to_decimal(total_cash_invested)
Calculation Details (Current MVP)¶
In the current implementation:
- Total Cash Invested is assumed to be the full purchase price
- Annual Cash Flow is assumed to equal NOI (no debt service in MVP)
Future versions will incorporate: - Actual down payment amount - Closing costs and transaction fees - Initial repair and improvement costs - Mortgage payments (debt service)
Example¶
Simplified example (MVP): - Purchase price (cash invested): $350,000 - NOI (cash flow): $20,700
Calculation:
With financing (future): - Down payment (20%): $70,000 - Closing costs: $5,000 - Total cash invested: $75,000 - Annual mortgage payment: $15,000 - Annual cash flow: $20,700 - $15,000 = $5,700
Calculation:
Interpretation¶
- Positive return: Investment generates cash flow
- Negative return: Investment consumes cash
- Target benchmarks:
- 8-12%: Good cash-on-cash return
- 12%+: Excellent return
- <8%: May not justify the risk
Uses¶
- Evaluate leveraged vs. all-cash purchases
- Compare different financing options
- Assess short-term cash flow requirements
Notes¶
- Unlike cap rate, cash-on-cash accounts for financing
- Does not consider tax benefits or property appreciation
- Focuses on actual cash returned to investor
Internal Rate of Return (IRR)¶
Definition¶
Internal Rate of Return (IRR) is the discount rate that makes the net present value (NPV) of all cash flows equal to zero. It represents the time-weighted annual return on investment.
Formula¶
IRR is found by solving for r in:
Where: - CF₀ = Initial investment (negative) - CF₁...CFₙ = Cash flows in periods 1 through n - r = IRR
Implementation¶
Located in investor_app/finance/utils.py:
def irr(cashflows: Iterable[Decimal]) -> Decimal:
cf = np.array([float(c) for c in cashflows], dtype=float)
try:
return to_decimal(npf.irr(cf))
except Exception:
return Decimal("0")
Uses NumPy Financial's IRR function with fallback to zero on calculation errors.
Calculation Details (Current MVP)¶
The current implementation uses a simplified 12-month cash flow projection:
- Initial outlay:
-Purchase Price(month 0) - Monthly cash flows:
NOI ÷ 12(months 1-12)
Example:
cashflows = [
to_decimal(prop.purchase_price) * Decimal(-1), # Initial investment
annual_noi / Decimal(12), # Month 1
annual_noi / Decimal(12), # Month 2
...
annual_noi / Decimal(12), # Month 12
]
Example¶
Using the previous property: - Purchase price: $350,000 - NOI: $20,700 - Monthly cash flow: $20,700 ÷ 12 = $1,725
Cash flows:
The IRR calculation finds the monthly rate that makes NPV = 0, then annualizes it.
Interpretation¶
- Higher IRR: Better investment return
- IRR > Cost of Capital: Investment creates value
- IRR < Cost of Capital: Investment destroys value
- Target benchmarks:
- 10-15%: Good IRR for rental properties
- 15%+: Excellent IRR
- <10%: May not justify risk
Uses¶
- Compare investments with different time horizons
- Evaluate investments with varying cash flow patterns
- Consider both cash flow and capital appreciation
Notes¶
- IRR assumes reinvestment of cash flows at the IRR rate
- Multiple IRRs can exist for non-conventional cash flows
- Does not consider investment size (use NPV for that)
- Current MVP implementation is simplified; future versions will include:
- Property sale at end of holding period
- Actual debt service payments
- Capital expenditures
- Tax implications
Debt Service Coverage Ratio (DSCR)¶
Definition¶
Debt Service Coverage Ratio (DSCR) measures a property's ability to cover its debt obligations with its net operating income.
Formula¶
Where:
Implementation¶
Located in investor_app/finance/utils.py:
def dscr(annual_noi: Decimal, annual_debt_service: Decimal) -> Decimal:
if to_decimal(annual_debt_service) == 0:
return Decimal("0")
return to_decimal(annual_noi) / to_decimal(annual_debt_service)
Calculation Details¶
- Calculate annual NOI
- Calculate annual debt service (12 × monthly mortgage payment)
- Divide NOI by debt service
Example¶
Property with financing: - NOI: $20,700 - Monthly mortgage payment: $1,250 - Annual debt service: $1,250 × 12 = $15,000
Calculation:
Interpretation¶
- DSCR > 1.0: Property generates sufficient income to cover debt
- DSCR = 1.0: Property exactly covers debt (break-even)
- DSCR < 1.0: Property cannot cover debt from operating income
- Lender requirements:
- 1.25+: Most lenders' minimum requirement
- 1.35-1.50: Preferred range for investment properties
- 2.0+: Very strong coverage
Uses¶
- Assess loan qualification and risk
- Evaluate refinancing opportunities
- Monitor property performance over time
Notes¶
- Current MVP returns 0 when debt service is 0 (all-cash purchases)
- DSCR is crucial for financing approval
- Lenders may use different calculation methods
- Does not account for other expenses (taxes, insurance, capex)
Precision and Data Types¶
Decimal Usage¶
All financial calculations use Python's Decimal type for precision:
from decimal import Decimal
# Database fields use Decimal
purchase_price = models.DecimalField(max_digits=12, decimal_places=2)
# Calculations maintain Decimal precision
noi = Decimal(monthly_income) * Decimal(12)
Conversion Boundaries¶
- Database ↔ Python: Use
Decimalthroughout - NumPy/NumPy-Financial: Convert
Decimaltofloatfor calculations, then back toDecimal - Display: Format
Decimalvalues for user presentation
# Example: IRR calculation
cashflows_decimal = [Decimal("-350000"), Decimal("1725"), ...]
cashflows_float = [float(c) for c in cashflows_decimal] # Convert for NumPy
result_float = npf.irr(cashflows_float) # NumPy calculation
result_decimal = Decimal(str(result_float)) # Convert back
Quantization¶
Results are quantized for consistent decimal places:
# Cap rate stored with 4 decimal places (0.0591)
analysis.cap_rate = cap_rate(noi, price).quantize(Decimal("0.0001"))
# NOI stored with 2 decimal places ($20,700.00)
analysis.noi = noi_value.quantize(Decimal("0.01"))
Default Assumptions¶
Financial calculations use default rates from environment configuration:
# From .env.example
VACANCY_RATE=0.05 # 5% vacancy
MANAGEMENT_FEE_RATE=0.08 # 8% management fee
CAPEX_RESERVE_RATE=0.05 # 5% capital expenditure reserve
These can be overridden per property or rental income record.
Calculation Pipeline¶
The complete calculation flow in compute_analysis_for_property:
- Gather data:
- Fetch rental income records
-
Fetch operating expense records
-
Calculate monthly values:
- Sum effective gross income (rent × (1 - vacancy))
-
Sum monthly operating expenses
-
Calculate NOI:
- Annualize income and expenses
-
Subtract expenses from income
-
Calculate other metrics:
- Cap Rate: NOI ÷ purchase price
- Cash-on-Cash: cash flow ÷ cash invested
- DSCR: NOI ÷ debt service
-
IRR: time-weighted return
-
Store results:
- Create or update
InvestmentAnalysisrecord - Quantize all decimal values
- Save to database
Related Models¶
See also:
Source Code¶
Financial calculation utilities: investor_app/finance/utils.py
Model definitions: core/models.py