{
  "schemaVersion": 1,
  "slug": "loan-comparison-calculator",
  "category": "finance",
  "kind": "implementation",
  "independentValidation": false,
  "implementationHash": "f26c46a7863d757196137be7b1ab824702610340506b4423744e0def4af0c2f0",
  "provenance": "Generated from the calculation contract used by the published page. Publication is not external validation.",
  "sourceFiles": [
    "data/calculators/loan-comparison-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": "M = P × r × (1 + r)^n ÷ ((1 + r)^n − 1); r = rate ÷ 100 ÷ 12; n = round(years × 12); total = M × n; interest = total − P",
    "vars": [
      [
        "P",
        "loan amount"
      ],
      [
        "rate",
        "annual interest rate (%)"
      ],
      [
        "years",
        "term in years"
      ],
      [
        "r",
        "monthly interest rate"
      ],
      [
        "n",
        "number of months"
      ],
      [
        "M",
        "monthly payment"
      ]
    ],
    "note": "Applied to offers A and B; interestSaving = |interest A − interest B|; if r = 0, M = P ÷ n."
  },
  "externalEvidence": {
    "reviewedAt": "2026-10-04",
    "sources": [
      {
        "name": "OpenStax — Loan amortization",
        "url": "https://openstax.org/books/principles-finance-2e/pages/8-3-loan-amortization"
      }
    ],
    "basis": "Fixed monthly payment = P × i / (1 - (1 + i)^(-n)); i is annual percent / 1200. At zero interest, payment = P / n.",
    "limitations": "Fixed nominal rate and monthly payments; fees, insurance, taxes and lender-specific daily accrual are excluded. Each term is rounded to a whole number of months; lower monthly payment does not mean lower total interest.",
    "implementationHash": "f26c46a7863d757196137be7b1ab824702610340506b4423744e0def4af0c2f0"
  },
  "contract": {
    "compute": "const pay=(P,rate,y)=>{ const r=rate/100/12, n=Math.round(y*12); return r&&n ? P*r*Math.pow(1+r,n)/(Math.pow(1+r,n)-1) : (n? P/n : 0); };\nconst mA=pay(amount,rateA,yearsA), mB=pay(amount,rateB,yearsB);\nconst nA=Math.round(yearsA*12), nB=Math.round(yearsB*12);\nconst tA=mA*nA, tB=mB*nB;\nconst iA=tA-amount, iB=tB-amount;\nreturn { paymentComparison: mA+\" / \"+mB, termA:yearsA, termB:yearsB, monthlyA: mA, monthlyB: mB, totalA: tA, totalB: tB, interestA: iA, interestB: iB, interestSaving: Math.abs(iA-iB) };",
    "inputs": [
      {
        "name": "amount",
        "type": "number",
        "default": 200000,
        "min": 0,
        "max": 100000000,
        "step": 1000
      },
      {
        "name": "rateA",
        "type": "number",
        "default": 5.5,
        "min": 0,
        "max": 30,
        "step": 0.05,
        "slider": true
      },
      {
        "name": "yearsA",
        "type": "number",
        "default": 30,
        "min": 1,
        "max": 40,
        "step": 1,
        "slider": true
      },
      {
        "name": "rateB",
        "type": "number",
        "default": 4.8,
        "min": 0,
        "max": 30,
        "step": 0.05,
        "slider": true
      },
      {
        "name": "yearsB",
        "type": "number",
        "default": 15,
        "min": 1,
        "max": 40,
        "step": 1,
        "slider": true
      }
    ],
    "outputs": [
      {
        "name": "paymentComparison",
        "type": "text",
        "components": [
          "monthlyA",
          "monthlyB"
        ],
        "componentPrecision": 2,
        "unit": ""
      },
      {
        "name": "monthlyA",
        "precision": 2,
        "unit": "$"
      },
      {
        "name": "monthlyB",
        "precision": 2,
        "unit": "$"
      },
      {
        "name": "totalA",
        "precision": 0,
        "unit": "$"
      },
      {
        "name": "totalB",
        "precision": 0,
        "unit": "$"
      },
      {
        "name": "interestA",
        "precision": 0,
        "unit": "$"
      },
      {
        "name": "interestB",
        "precision": 0,
        "unit": "$"
      },
      {
        "name": "interestSaving",
        "precision": 0,
        "unit": "$"
      },
      {
        "name": "termA",
        "precision": 0,
        "unit": "years"
      },
      {
        "name": "termB",
        "precision": 0,
        "unit": "years"
      }
    ],
    "interpret": null
  }
}
