{
  "schemaVersion": 1,
  "slug": "rent-vs-buy-calculator",
  "category": "finance",
  "kind": "implementation",
  "independentValidation": false,
  "implementationHash": "43a74047b14452a0c2dfbeebf1a02d4fd85411e639fc0e3d3f8d47c8e9806f0f",
  "provenance": "Generated from the calculation contract used by the published page. Publication is not external validation.",
  "sourceFiles": [
    "data/calculators/rent-vs-buy-calculator.json",
    "src/engine.js"
  ],
  "runtimeSource": "/sources/engine.txt",
  "runtimeHash": "5c888d8898afbdee36267bb9977829e1e2ac568f6677c8ee87469c139864645c",
  "scope": "The compute function records implemented equations, constants, lookup tables and custom rules. Inputs record defaults and allowed values; outputs record precision and units. Defaults are examples, not recommendations. General background references are not evidence for this specific algorithm.",
  "publishedFormula": {
    "formula": "net ownership cost = down payment + purchase fees + payments + ownership expenses − sale equity; buyer net worth = sale equity + invested savings; difference in wealth = buyer − renter",
    "vars": [],
    "note": "Net cost and terminal net worth are separate views. This simplified scenario excludes income-tax effects and does not recommend a purchase."
  },
  "externalEvidence": {
    "reviewedAt": "2026-10-31",
    "basis": "Comparison years and mortgage term are independent. Monthly ownership cash flow includes the mortgage until payoff and the entered annual ownership-cost rate. Rent and home values change annually; the monthly spending advantage is invested by whichever household spends less, with equal initial capital. Purchase closing costs and sale costs are explicit; sale costs apply to future home value. Net cost and terminal net worth are separate views. This simplified scenario excludes income-tax effects and does not recommend a purchase.",
    "limitations": "Net cost and terminal net worth are separate views. This simplified scenario excludes income-tax effects and does not recommend a purchase.",
    "implementationHash": "43a74047b14452a0c2dfbeebf1a02d4fd85411e639fc0e3d3f8d47c8e9806f0f",
    "sources": [
      {
        "name": "CFPB — costs of taking out a mortgage",
        "url": "https://www.consumerfinance.gov/ask-cfpb/what-costs-come-with-taking-out-a-mortgage-en-153/"
      }
    ],
    "referenceRole": "input"
  },
  "contract": {
    "compute": "try{if(((true)&&(!Number.isFinite(monthlyRent)||monthlyRent<0||monthlyRent>1000000))||((true)&&(!Number.isFinite(homePrice)||homePrice<0||homePrice>100000000))||((true)&&(!Number.isFinite(downPercent)||downPercent<0||downPercent>100))||((true)&&(!Number.isFinite(rate)||rate<0||rate>30))||((true)&&(!Number.isFinite(years)||years<1||years>50))||((true)&&(!Number.isFinite(mortgageTermYears)||mortgageTermYears<1||mortgageTermYears>50))||((true)&&(!Number.isFinite(ownCostRate)||ownCostRate<0||ownCostRate>30))||((true)&&(!Number.isFinite(rentIncrease)||rentIncrease<-50||rentIncrease>30))||((true)&&(!Number.isFinite(appreciation)||appreciation<-50||appreciation>30))||((true)&&(!Number.isFinite(investmentReturn)||investmentReturn<-50||investmentReturn>30))||((true)&&(!Number.isFinite(purchaseClosingPercent)||purchaseClosingPercent<0||purchaseClosingPercent>20))||((true)&&(!Number.isFinite(sellingCostPercent)||sellingCostPercent<0||sellingCostPercent>20)))throw new Error('inputError');const calculate=function(){const h=(function context() {\n  const DAY=86400000;\n  const own=(o,k)=>Object.prototype.hasOwnProperty.call(o,k);\n  const fail=()=>{throw new Error('inputError');};\n  const num=(v,min=-Infinity,max=Infinity,integer=false)=>{\n    if(!Number.isFinite(v)||v<min||v>max||(integer&&!Number.isInteger(v)))fail();return v;\n  };\n  const choice=(v,values)=>{if(!values.includes(v))fail();return v;};\n  const date=s=>{if(typeof s!=='string'||!/^\\d{4}-\\d{2}-\\d{2}$/.test(s)||s.startsWith('0000'))fail();const d=new Date(s+'T00:00:00Z');if(!Number.isFinite(+d)||d.toISOString().slice(0,10)!==s)fail();return d;};\n  const iso=d=>{if(!Number.isFinite(+d)||d.getUTCFullYear()<1||d.getUTCFullYear()>9999)fail();return d.toISOString().slice(0,10);};\n  const shift=(d,n)=>{const r=new Date(+d+n*DAY);iso(r);return r;};\n  const month=(d,n)=>{const r=new Date(+d);r.setUTCDate(1);r.setUTCMonth(r.getUTCMonth()+n);const end=new Date(+r);end.setUTCMonth(end.getUTCMonth()+1,0);r.setUTCDate(Math.min(d.getUTCDate(),end.getUTCDate()));iso(r);return r;};\n  const weekdays=['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];\n  const work=(d,count)=>{let n=Math.floor(count/7)*5;for(let k=0;k<count%7;k++){const w=(d.getUTCDay()+k)%7;if(w!==0&&w!==6)n++;}return n;};\n  const wholeMonths=(a,b)=>{let n=(b.getUTCFullYear()-a.getUTCFullYear())*12+b.getUTCMonth()-a.getUTCMonth();if(n>=0&&+month(a,n)>+b)n--;if(n<0&&+month(a,n)<+b)n++;return n;};\n  return {DAY,own,fail,num,choice,date,iso,shift,month,weekdays,work,wholeMonths};\n})();const result=(function(v,h){\n    const {num}=h;num(v.homePrice,0,1e8);num(v.downPercent,0,100);num(v.rate,0,30);num(v.years,1,50,true);num(v.mortgageTermYears,1,50,true);num(v.monthlyRent,0,1e6);num(v.ownCostRate,0,30);num(v.rentIncrease,-50,30);num(v.appreciation,-50,30);num(v.investmentReturn,-50,30);num(v.purchaseClosingPercent,0,20);num(v.sellingCostPercent,0,20);\n    const downPayment=v.homePrice*v.downPercent/100,closing=v.homePrice*v.purchaseClosingPercent/100,loan=v.homePrice-downPayment,r=v.rate/1200,term=v.mortgageTermYears*12,n=v.years*12;\n    const monthlyMortgage=loan===0?0:r?loan*r/(-Math.expm1(-term*Math.log1p(r))):loan/term;\n    let balance=loan,renter=downPayment+closing,buyerInvestment=0,rentTotal=0,paid=0,ownPaid=0,homeValue=v.homePrice;\n    for(let k=0;k<n;k++){\n      const rent=v.monthlyRent*Math.pow(1+v.rentIncrease/100,Math.floor(k/12)),ownerCost=homeValue*v.ownCostRate/1200,payment=k<term?monthlyMortgage:0;\n      renter*=1+v.investmentReturn/1200;buyerInvestment*=1+v.investmentReturn/1200;\n      const gap=payment+ownerCost-rent;if(gap>0)renter+=gap;else buyerInvestment-=gap;\n      if(k<term)balance=Math.max(0,balance*(1+r)-payment);rentTotal+=rent;paid+=payment;ownPaid+=ownerCost;\n      if((k+1)%12===0)homeValue*=1+v.appreciation/100;\n    }\n    const sellingCosts=homeValue*v.sellingCostPercent/100,equity=homeValue-balance-sellingCosts,buyNetWorth=equity+buyerInvestment,rentNetWorth=renter,buyTotal=downPayment+closing+paid+ownPaid-equity,wealthDifference=buyNetWorth-rentNetWorth;\n    return {rentTotal,buyTotal,difference:rentTotal-buyTotal,monthlyBuyCost:buyTotal/n,downPayment,monthlyMortgage,buyNetWorth,rentNetWorth,wealthDifference,mortgageBalance:balance,homeValue,sellingCosts,purchaseClosingCosts:closing,verdict:Math.abs(wealthDifference)<1e-7?'Both scenarios have the same net worth':wealthDifference>0?'Buying comes out ahead over '+v.years+' years':'Renting and investing comes out ahead over '+v.years+' years'};\n  })({\"homePrice\":(homePrice),\"years\":(years),\"monthlyRent\":(monthlyRent),\"downPercent\":(downPercent),\"rate\":(rate),\"mortgageTermYears\":(mortgageTermYears),\"ownCostRate\":(ownCostRate),\"rentIncrease\":(rentIncrease),\"appreciation\":(appreciation),\"investmentReturn\":(investmentReturn),\"purchaseClosingPercent\":(purchaseClosingPercent),\"sellingCostPercent\":(sellingCostPercent)},h);return result;};const out=calculate();return Object.fromEntries([\"rentTotal\",\"buyTotal\",\"difference\",\"monthlyBuyCost\",\"buyNetWorth\",\"rentNetWorth\",\"mortgageBalance\",\"homeValue\",\"sellingCosts\",\"purchaseClosingCosts\",\"monthlyMortgage\",\"wealthDifference\",\"downPayment\",\"verdict\"].map(k=>[k,out[k]??null]).concat(Object.entries(out).filter(([k])=>k.startsWith('__'))));}catch(error){return Object.assign(Object.fromEntries([\"rentTotal\",\"buyTotal\",\"difference\",\"monthlyBuyCost\",\"buyNetWorth\",\"rentNetWorth\",\"mortgageBalance\",\"homeValue\",\"sellingCosts\",\"purchaseClosingCosts\",\"monthlyMortgage\",\"wealthDifference\",\"downPayment\",\"verdict\"].map(k=>[k,null])),{__error:error.message==='openingsExceedWallArea'?'openingsExceedWallArea':'inputError'});}",
    "inputs": [
      {
        "name": "monthlyRent",
        "type": "number",
        "default": 2000,
        "min": 0,
        "max": 1000000,
        "step": 50,
        "strictRange": true,
        "requiredValue": true
      },
      {
        "name": "homePrice",
        "type": "number",
        "default": 400000,
        "min": 0,
        "max": 100000000,
        "step": 1000,
        "strictRange": true,
        "requiredValue": true
      },
      {
        "name": "downPercent",
        "type": "number",
        "default": 20,
        "min": 0,
        "max": 100,
        "step": 1,
        "slider": true,
        "strictRange": true,
        "requiredValue": true
      },
      {
        "name": "rate",
        "type": "number",
        "default": 6.5,
        "min": 0,
        "max": 30,
        "step": 0.01,
        "slider": true,
        "strictRange": true,
        "requiredValue": true
      },
      {
        "name": "years",
        "type": "number",
        "default": 10,
        "min": 1,
        "max": 50,
        "step": 1,
        "slider": true,
        "strictRange": true,
        "requiredValue": true
      },
      {
        "name": "mortgageTermYears",
        "type": "number",
        "default": 30,
        "min": 1,
        "max": 50,
        "step": 1,
        "slider": true,
        "strictRange": true,
        "requiredValue": true
      },
      {
        "name": "ownCostRate",
        "type": "number",
        "default": 2.5,
        "min": 0,
        "max": 30,
        "step": 0.1,
        "slider": true,
        "strictRange": true,
        "requiredValue": true
      },
      {
        "type": "number",
        "default": 0,
        "min": -50,
        "max": 30,
        "step": 0.01,
        "strictRange": true,
        "requiredValue": true,
        "advanced": true,
        "name": "rentIncrease"
      },
      {
        "type": "number",
        "default": 0,
        "min": -50,
        "max": 30,
        "step": 0.01,
        "strictRange": true,
        "requiredValue": true,
        "advanced": true,
        "name": "appreciation"
      },
      {
        "type": "number",
        "default": 0,
        "min": -50,
        "max": 30,
        "step": 0.01,
        "strictRange": true,
        "requiredValue": true,
        "advanced": true,
        "name": "investmentReturn"
      },
      {
        "type": "number",
        "default": 0,
        "min": 0,
        "max": 20,
        "step": 0.01,
        "strictRange": true,
        "requiredValue": true,
        "advanced": true,
        "name": "purchaseClosingPercent"
      },
      {
        "type": "number",
        "default": 0,
        "min": 0,
        "max": 20,
        "step": 0.01,
        "strictRange": true,
        "requiredValue": true,
        "advanced": true,
        "name": "sellingCostPercent"
      },
      {
        "name": "resultKind",
        "type": "select",
        "default": "difference",
        "options": [
          {
            "value": "difference"
          },
          {
            "value": "wealthDifference"
          }
        ],
        "advanced": true
      }
    ],
    "outputs": [
      {
        "name": "rentTotal",
        "precision": 2,
        "unit": "$",
        "notation": "auto"
      },
      {
        "name": "buyTotal",
        "precision": 2,
        "unit": "$",
        "notation": "auto"
      },
      {
        "name": "difference",
        "precision": 2,
        "unit": "$",
        "notation": "auto"
      },
      {
        "name": "monthlyBuyCost",
        "precision": 2,
        "unit": "$",
        "notation": "auto"
      },
      {
        "name": "buyNetWorth",
        "precision": 0,
        "unit": "$",
        "notation": "auto"
      },
      {
        "name": "rentNetWorth",
        "precision": 0,
        "unit": "$",
        "notation": "auto"
      },
      {
        "precision": 2,
        "unit": "$",
        "name": "mortgageBalance",
        "notation": "auto"
      },
      {
        "precision": 2,
        "unit": "$",
        "name": "homeValue",
        "notation": "auto"
      },
      {
        "precision": 2,
        "unit": "$",
        "name": "sellingCosts",
        "notation": "auto"
      },
      {
        "precision": 2,
        "unit": "$",
        "name": "purchaseClosingCosts",
        "notation": "auto"
      },
      {
        "name": "monthlyMortgage",
        "precision": 2,
        "unit": "$",
        "notation": "auto"
      },
      {
        "precision": 2,
        "unit": "$",
        "name": "wealthDifference",
        "notation": "auto"
      },
      {
        "name": "downPayment",
        "precision": 0,
        "unit": "$",
        "notation": "auto"
      },
      {
        "name": "verdict",
        "precision": 0,
        "unit": "",
        "type": "text"
      }
    ],
    "interpret": null
  }
}
