Daily Mortgage Rates - Cashflow Rentals | USA Property Investments
Skip to content
Our daily report covering US mortgage rates for domestic homebuyers, investors, and foreign nationals
Page load link
*/
(function(){
const $ = (id) => document.getElementById(id);
const fmt = (n, opts={}) => {
const v = (isFinite(n) ? n : 0);
const o = { style:'currency', currency:'USD', maximumFractionDigits:0, ...opts };
return new Intl.NumberFormat('en-US', o).format(v);
};
const pct = (n, d=2) => `${(isFinite(n)? n : 0).toFixed(d)}%`;function payment(principal, annualRatePct, years) {
const r = (annualRatePct/100)/12;
const n = years*12;
if (r === 0) return principal/n;
return principal * (r * Math.pow(1+r, n)) / (Math.pow(1+r, n) - 1);
}
function balanceAfter(principal, annualRatePct, years, monthsElapsed) {
const r = (annualRatePct/100)/12;
const n = years*12;
if (r === 0) return principal * (1 - monthsElapsed/n);
const pmt = payment(principal, annualRatePct, years);
return principal*Math.pow(1+r, monthsElapsed) - pmt*( (Math.pow(1+r, monthsElapsed) - 1)/r );
}
function cumulativePrincipalPaid(principal, annualRatePct, years, monthsElapsed){
return principal - balanceAfter(principal, annualRatePct, years, monthsElapsed);
}const ids = [
'price','downPct','rate','term','rent','otherIncome','vacancy','mgmtPct',
'taxesAnnual','insAnnual','hoaMonthly','maintPct','repairsMonthly','otherMonthly','closingPct','initialRepairs',
'holdYears','appreciation','rentGrowth','expGrowth','sellCostPct','landPct','incomeTaxPct','cgTaxPct'
];
const out = {
cashflow: $('m-cashflow'),
caprate: $('m-caprate'),
coc: $('m-coc'),
dscr: $('m-dscr'),
ads: $('m-ads'),
noi: $('m-noi'),gsr: $('o-gsr'),
vac: $('o-vac'),
egi: $('o-egi'),
opex:$('o-opex'),
mort:$('o-mort'),
oNoi:$('o-noi'),ltSale: $('lt-sale'),
ltBal: $('lt-balance'),
ltSell: $('lt-sellcosts'),
ltCF: $('lt-cf'),
ltPrin: $('lt-principal'),
ltNet: $('lt-net'),
ltProfit: $('lt-profit'),
ltRoi: $('lt-roi'),
ltAnn: $('lt-annualized'),cashNeeded: $('m-cashneeded')
};
if (!out.cashflow) return;// Defaults
const defaults = {
price: 300000, downPct: 25, rate: 7.25, term: 30,
rent: 2200, otherIncome: 0, vacancy: 5, mgmtPct: 8,
taxesAnnual: 3600, insAnnual: 1500, hoaMonthly: 0, maintPct: 8,
repairsMonthly: 100, otherMonthly: 0, closingPct: 3, initialRepairs: 0,
holdYears: 5, appreciation: 3, rentGrowth: 2, expGrowth: 2, sellCostPct: 8,
landPct: 20, incomeTaxPct: 24, cgTaxPct: 15
};// Initialize placeholders
ids.forEach(id => { const el=$(id); if(el && !el.value) el.value = defaults[id] ?? ''; });function valNum(id){
const el = $(id);
if(!el) return 0;
const v = parseFloat(String(el.value).replace(/,/g,''));
return isFinite(v) ? v : 0;
}function recalc(){
// Inputs
const price = valNum('price');
const downPct = valNum('downPct');
const rate = valNum('rate');
const term = valNum('term');const rent = valNum('rent');
const otherIncome = valNum('otherIncome');
const vacancy = valNum('vacancy');
const mgmtPct = valNum('mgmtPct');const taxesAnnual = valNum('taxesAnnual');
const insAnnual = valNum('insAnnual');
const hoaMonthly = valNum('hoaMonthly');
const maintPct = valNum('maintPct');const repairsMonthly = valNum('repairsMonthly');
const otherMonthly = valNum('otherMonthly');
const closingPct = valNum('closingPct');
const initialRepairs = valNum('initialRepairs');const holdYears = valNum('holdYears');
const appreciation = valNum('appreciation');
const rentGrowth = valNum('rentGrowth');
const expGrowth = valNum('expGrowth');
const sellCostPct = valNum('sellCostPct');// Financing
const downPayment = price * (downPct/100);
const loanAmount = Math.max(price - downPayment, 0);
const mortPmt = (loanAmount>0 && term>0) ? payment(loanAmount, rate, term) : 0;
const ads = mortPmt * 12;// Income (monthly)
const grossMonthly = rent + otherIncome;
const vacancyLoss = grossMonthly * (vacancy/100);
const egiMonthly = grossMonthly - vacancyLoss;// Operating expenses (monthly)
const mgmtMonthly = rent * (mgmtPct/100); // mgmt on rent only (common)
const maintMonthly = rent * (maintPct/100);
const taxesMonthly = taxesAnnual/12;
const insMonthly = insAnnual/12;const opexMonthly = mgmtMonthly + maintMonthly + taxesMonthly + insMonthly + hoaMonthly + repairsMonthly + otherMonthly;// NOI (annual, pre-debt)
const noiAnnual = (egiMonthly - opexMonthly) * 12;// Metrics
const capRate = price>0 ? (noiAnnual/price)*100 : 0;
const monthlyCashFlow = egiMonthly - opexMonthly - mortPmt;
const cashNeeded = downPayment + (price * (closingPct/100)) + initialRepairs;
const coc = cashNeeded>0 ? ((monthlyCashFlow*12)/cashNeeded)*100 : 0;
const dscr = ads>0 ? (noiAnnual/ads) : 0;// Long-term (simplified projections)
const months = holdYears*12;
const futurePrice = price * Math.pow(1 + (appreciation/100), holdYears);
const sellCosts = futurePrice * (sellCostPct/100);
const loanBal = balanceAfter(loanAmount, rate, term, months);
const principalPaid = cumulativePrincipalPaid(loanAmount, rate, term, months);// Grow rent & expenses with separate growth rates (straight-line monthly comp approx)
const growthRentMonthly = rent * Math.pow(1 + (rentGrowth/100), holdYears);
const avgRentMonthly = (rent + growthRentMonthly)/2; // simple average over period
const avgOtherIncMonthly = otherIncome; // assume flat
const avgGrossMonthly = avgRentMonthly + avgOtherIncMonthly;
const avgVacLoss = avgGrossMonthly * (vacancy/100);
const avgMgmtMonthly = avgRentMonthly * (mgmtPct/100);
const growthMaintMonthly = (rent * (maintPct/100)) * Math.pow(1 + (expGrowth/100), holdYears);
const avgMaintMonthly = (rent*(maintPct/100) + growthMaintMonthly)/2;const growthTaxesMonthly = taxesMonthly * Math.pow(1 + (expGrowth/100), holdYears);
const avgTaxesMonthly = (taxesMonthly + growthTaxesMonthly)/2;const growthInsMonthly = insMonthly * Math.pow(1 + (expGrowth/100), holdYears);
const avgInsMonthly = (insMonthly + growthInsMonthly)/2;const growthHOA = hoaMonthly * Math.pow(1 + (expGrowth/100), holdYears);
const avgHOA = (hoaMonthly + growthHOA)/2;const growthRep = repairsMonthly * Math.pow(1 + (expGrowth/100), holdYears);
const avgRep = (repairsMonthly + growthRep)/2;const growthOther = otherMonthly * Math.pow(1 + (expGrowth/100), holdYears);
const avgOther = (otherMonthly + growthOther)/2;const avgEGIMonthly = avgGrossMonthly - avgVacLoss;
const avgOpexMonthly = avgMgmtMonthly + avgMaintMonthly + avgTaxesMonthly + avgInsMonthly + avgHOA + avgRep + avgOther;
const avgNOIAnnual = (avgEGIMonthly - avgOpexMonthly) * 12;
const avgMonthlyCashFlow = avgEGIMonthly - avgOpexMonthly - mortPmt;const cfCumulative = avgMonthlyCashFlow * months;
const netProceeds = Math.max(futurePrice - sellCosts - loanBal, 0);
const totalProfitPreTax = cfCumulative + principalPaid + (netProceeds); // double counts? No: netProceeds already subtracts remaining loan; principalPaid counts via cash flow equity growth — we keep both cash flow + net proceeds (which is equity at sale). principalPaid is embedded in netProceeds vs original loan? Common approaches vary; we’ll show both CF and Net separately below and include principal explicitly.
// To avoid double counting principal: treat netProceeds as (future equity realized) and ONLY add CF. We'll present principalPaid separately in UI.
const totalProfit = cfCumulative + netProceeds; // preferred: CF + Net Sale (no double-count)
const roiTotal = cashNeeded>0 ? (totalProfit / cashNeeded) * 100 : 0;
const annualizedSimple = holdYears>0 ? (roiTotal/holdYears) : 0;// Outputs
out.cashflow.textContent = fmt(monthlyCashFlow, {maximumFractionDigits:0});
out.caprate.textContent = pct(capRate, 2);
out.noi.textContent = `NOI: ${fmt(noiAnnual, {maximumFractionDigits:0})}/yr`;
out.coc.textContent = pct(coc, 2);
out.cashNeeded.textContent = `Cash Invested: ${fmt(cashNeeded, {maximumFractionDigits:0})}`;
out.dscr.textContent = `${(isFinite(dscr)? dscr : 0).toFixed(2)}×`;
out.ads.textContent = `Annual Debt Service: ${fmt(ads, {maximumFractionDigits:0})}`;out.gsr.textContent = fmt(grossMonthly, {maximumFractionDigits:0});
out.vac.textContent = `−${fmt(vacancyLoss, {maximumFractionDigits:0})}`;
out.egi.textContent = fmt(egiMonthly, {maximumFractionDigits:0});
out.opex.textContent = `−${fmt(opexMonthly, {maximumFractionDigits:0})}`;
out.oNoi.textContent = fmt(noiAnnual, {maximumFractionDigits:0});
out.mort.textContent = `−${fmt(mortPmt, {maximumFractionDigits:0})}`;out.ltSale.textContent = fmt(futurePrice, {maximumFractionDigits:0});
out.ltBal.textContent = fmt(Math.max(loanBal,0), {maximumFractionDigits:0});
out.ltSell.textContent = `−${fmt(sellCosts, {maximumFractionDigits:0})}`;
out.ltCF.textContent = fmt(cfCumulative, {maximumFractionDigits:0});
out.ltPrin.textContent = fmt(Math.max(principalPaid,0), {maximumFractionDigits:0});
out.ltNet.textContent = fmt(netProceeds, {maximumFractionDigits:0});out.ltProfit.textContent = fmt(totalProfit, {maximumFractionDigits:0});
out.ltRoi.textContent = pct(roiTotal, 2);
out.ltAnn.textContent = pct(annualizedSimple, 2);
}// Wire up listeners
ids.forEach(id=>{
const el = $(id);
if(!el) return;
el.addEventListener('input', recalc);
el.addEventListener('change', recalc);
});// Buttons
const btnSample = document.getElementById('rpc-load-sample');
const btnReset = document.getElementById('rpc-reset');btnSample && btnSample.addEventListener('click', ()=>{
Object.entries(defaults).forEach(([k,v])=>{ const el=$(k); if(el) el.value=v; });
recalc();
const adv = document.getElementById('rpc-adv');
if(adv && !adv.open) adv.open = true;
});
btnReset && btnReset.addEventListener('click', ()=>{
ids.forEach(id=>{ const el=$(id); if(el){ el.value=''; }});
// sensible blank state
recalc();
});// Initial
if (out.cashflow) { recalc(); }
})();