Build an Interest Rate Calculator 📉
Have you ever wondered: "I borrowed $10,000 and pay $300/month for 3 years. What is my actual interest rate?"
A regular calculator cannot answer this.
Why? Because the Interest Rate (r) is trapped in the exponent of the loan formula.
Algebra cannot isolate r.
To solve this, we must use an Iterative Algorithm like the Newton-Raphson Method.
Step 1: The Problem (The Math) 🧮
The formula for a monthly loan payment (PMT) is:
PMT = Principal * [ r(1+r)^n ] / [ (1+r)^n - 1 ]
We know PMT, Principal, and n. We need to find r.
Since we can't rearrange the formula, we create a function f(r):
f(r) = Principal * [ r(1+r)^n ] / [ (1+r)^n - 1 ] - PMT
We want to find where f(r) = 0.
Step 2: The Algorithm (Newton-Raphson) 🤖
We guess a rate (e.g., 5%), check the error, and use calculus (the derivative) to improve our guess.
/* lib/finance-utils.js */
export const calculateRate = (principal, payment, periods) => {
let rate = 0.01; // Initial guess: 1% per month
let iteration = 0;
while (iteration < 50) { // Max 50 tries
const y = Math.pow(1 + rate, periods);
// f(r): Calculate payment with current rate guess - Actual Payment
const calculatedPayment = principal * ((rate * y) / (y - 1));
const error = calculatedPayment - payment;
// If error is tiny, we found it!
if (Math.abs(error) < 0.0001) return rate * 12 * 100; // Annual %
// f'(r): The Derivative (Slope) - Approximate for simplicity
// A simplified slope adjustment works for most loans
const slope = principal * periods * y / (y - 1);
// Newton's Step: x_new = x_old - f(x) / f'(x)
rate = rate - (error / slope);
iteration++;
}
return 0; // Failed to converge
}
Step 3: The React UI 💻
This tool feels like magic because users just type numbers and get a precise rate instantly.
"use client"
import { useState } from "react"
import { Search } from "lucide-react"
export default function RateFinder() {
const [amount, setAmount] = useState("")
const [months, setMonths] = useState("")
const [payment, setPayment] = useState("")
const [rate, setRate] = useState<number | null>(null)
const findRate = () => {
const P = parseFloat(amount)
const N = parseFloat(months)
const PMT = parseFloat(payment)
if(!P || !N || !PMT) return;
// Simplified Iterative Solver (Binary Search is safer than Newton if derivative is tricky)
let min = 0;
let max = 1; // 100% monthly rate
let guess = 0.5;
for(let i=0; i<60; i++) {
guess = (min + max) / 2;
const y = Math.pow(1+guess, N);
const calcPMT = P * ((guess * y) / (y - 1));
if (calcPMT > PMT) max = guess;
else min = guess;
}
setRate(guess * 12 * 100)
}
return (
<div className="max-w-md mx-auto p-6 bg-white border rounded-xl shadow-lg">
<h2 className="text-xl font-bold mb-6 text-slate-800 flex items-center gap-2">
<Search className="text-blue-600" />
Find My Interest Rate
</h2>
<div className="space-y-4">
<div>
<label className="text-xs font-bold text-slate-500 uppercase">Loan Amount ($)</label>
<input
value={amount} onChange={e => setAmount(e.target.value)}
className="w-full p-3 border rounded-lg bg-slate-50"
placeholder="10000"
/>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="text-xs font-bold text-slate-500 uppercase">Months</label>
<input
value={months} onChange={e => setMonths(e.target.value)}
className="w-full p-3 border rounded-lg bg-slate-50"
placeholder="36"
/>
</div>
<div>
<label className="text-xs font-bold text-slate-500 uppercase">Monthly Pay ($)</label>
<input
value={payment} onChange={e => setPayment(e.target.value)}
className="w-full p-3 border rounded-lg bg-slate-50"
placeholder="300"
/>
</div>
</div>
<button
onClick={findRate}
className="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 rounded-lg transition"
>
CALCULATE RATE
</button>
</div>
{rate !== null && (
<div className="mt-8 text-center animate-in zoom-in">
<div className="text-sm text-slate-400 uppercase font-semibold">Real Annual Interest Rate</div>
<div className="text-5xl font-black text-blue-600 tracking-tighter my-2">
{rate.toFixed(2)}%
</div>
<p className="text-xs text-slate-400 max-w-[200px] mx-auto">
This is the APR you are actually paying on this loan.
</p>
</div>
)}
</div>
)
}
Step 4: Binary Search Alternative 💡
In the simplified example above, I used Binary Search instead of Newton-Raphson. Why?
- Newton-Raphson is faster (requires fewer steps) but requires Calculus (Derivatives) and can crash if the slope is flat (Division by Zero).
- Binary Search is slower (requires ~60 loops) but is Robust. It simply narrows the range (High/Low) until it traps the answer. For a browser tool, 60 loops is instantaneous (0ms), so Binary Search is often the safer, crash-proof choice for developers!