A Mortgage Calculator in JavaScript

Below is a simple loan amortization calculator implemented in JavaScript. It’s primarily intended to be used for home mortgages. To see it in action, copy it into an .html file and open that file in a web browser that runs JavaScript.  Or, for an online version, visit https://thiscouldbebetter.neocities.org/mortgagecalculator.html.

This doesn’t qualify as professional financial advice, by the way. I tested some scenarios against a similar calculator, on bankrate.com, and while the numbers didn’t match exactly, they were only ever off by a few cents. That’s close enough for my purposes, but I’m told that creditors can be surprisingly petty about a couple of cents sometimes, so be careful.

UPDATE 2015/11/16 – I have added some code to automatically set the current month and year as the start time, and to replace any previously generated payment schedule with the new one if the Calculate button is clicked again.

UPDATE 2016/01/13 – I have modified the code to include an extra column to the payment schedule table that shows the principal remaining after each payment.  As of this writing, the screenshot has not yet been updated accordingly.

LoanAmortizationCalculator



<html>
<body onload="body_Load();">

<!-- UI -->

<div id="divMain">

	<label>Principal:</label>
	<input id="inputPrincipal" type="number" value="300000"></input>
	<br />
	<label>Annual Percentage Rate:</label>
	<input id="inputInterestAPR" type="number" value="5"></input>
	<br />
	<label>Months to Maturity:</label>
	<input id="inputMonthsToMaturity" type="number" value="360"></input>
	<br />
	<label>Start Year/Month:</label>
	<input id="inputStartYear" type="number" value="2015"></input>
	<label>/</label>
	<input id="inputStartMonth" type="number" value="1"></input>
	<br />
	<label>Additional Principal Per Payment:</label>
	<input id="inputPrincipalPerPaymentAdditional" type="number" value="0"></input>
	<br />
	<button id="buttonCalculate" onclick="buttonCalculate_Click();">Calculate</button>
	<div id="divPaymentSchedule" />
</div>

<script type="text/javascript">

// events

function body_Load()
{
	var now = new Date();
	var year = now.getFullYear();
	var month = now.getMonth();
	document.getElementById("inputStartYear").value = year;
	document.getElementById("inputStartMonth").value = month;
}

function buttonCalculate_Click()
{
	var principal = parseFloat
	(
		document.getElementById("inputPrincipal").value
	);
	var interestAPR = parseFloat
	(
		document.getElementById("inputInterestAPR").value
	);
	var monthsToMaturity = parseInt
	(
		document.getElementById("inputMonthsToMaturity").value
	);
	var startYear = parseInt
	(
		document.getElementById("inputStartYear").value
	);
	var startMonth = parseInt
	(
		document.getElementById("inputStartMonth").value
	);
	var principalPerPaymentAdditional = parseFloat
	( 
		document.getElementById("inputPrincipalPerPaymentAdditional").value
	);

	var loan = new Loan
	(
		principal,
		interestAPR,
		monthsToMaturity,
		startYear,
		startMonth,
		principalPerPaymentAdditional
	);
	
	var paymentSchedule = loan.paymentSchedule();

	var divPaymentSchedule = document.getElementById("divPaymentSchedule");
	divPaymentSchedule.innerHTML = "";
	divPaymentSchedule.appendChild
	(
		paymentSchedule.toDOMElement()
	);
}

// classes


function Constants()
{
	// static class
}
{
	Constants.CentsPerDollar = 100;
	Constants.MonthsPerYear = 12;
}

function Loan
(
	principal, 
	interestAPR, // annual percentage rate
	monthsToMaturity, 
	startYear, 
	startMonth,
	principalPerPaymentAdditional
)
{
	this.principal = principal;
	this.interestAPR = interestAPR;
	this.monthsToMaturity = monthsToMaturity;
	this.startYear = startYear;
	this.startMonth = startMonth;
	this.principalPerPaymentAdditional = principalPerPaymentAdditional;
}
{
	Loan.prototype.paymentSchedule = function()
	{
		var payments = [];

		var yearCurrent = this.startYear;
		var monthCurrent = this.startMonth + 1;

		var centsPerDollar = Constants.CentsPerDollar;

		var principalRemainingInCents = 
			this.principal * centsPerDollar;

		var principalPerPaymentAdditionalInCents = 
			this.principalPerPaymentAdditional
			* centsPerDollar;

		var interestRatePerMonth = 
			this.interestAPR 
			/ 100 
			/ Constants.MonthsPerYear;

		var denominator = Math.pow
		(
			1 + interestRatePerMonth,
			this.monthsToMaturity
		) - 1;

		var amountPerPaymentInCents = Math.round
		(
			principalRemainingInCents
			*
			(
				interestRatePerMonth 
				/ denominator	
				+ interestRatePerMonth
			)
		);

		for (var t = 0; t < this.monthsToMaturity; t++)
		{
			var interestThisPaymentInCents = Math.round
			(
				principalRemainingInCents * interestRatePerMonth 
			);

			var principalThisPaymentInCents = 
				amountPerPaymentInCents
				- interestThisPaymentInCents
				+ principalPerPaymentAdditionalInCents;

			if (principalThisPaymentInCents > principalRemainingInCents)
			{
				principalThisPaymentInCents = principalRemainingInCents;
			}

			principalRemainingInCents -= principalThisPaymentInCents;
			
			var payment = new Payment
			(
				principalThisPaymentInCents,
				interestThisPaymentInCents,
				yearCurrent,
				monthCurrent
			);

			payments.push(payment);

			monthCurrent++;
			if (monthCurrent > Constants.MonthsPerYear)
			{
				yearCurrent++;
				monthCurrent = 1;
			}

			if (principalRemainingInCents <= 0)
			{
				break;
			}
		}

		var returnValue = new PaymentSchedule
		(
			this,
			payments
		);

		return returnValue;
	}
}

function Payment(principalInCents, interestInCents, year, month)
{
	this.principalInCents = principalInCents;
	this.interestInCents = interestInCents;
	this.year = year;
	this.month = month;
}
{
	Payment.prototype.amountInCents = function()
	{
		var returnValue = 
			this.principalInCents + this.interestInCents;
		return returnValue;
	}

	Payment.prototype.amountInDollars = function()
	{
		return this.amountInCents() / Constants.CentsPerDollar;
	}

	Payment.prototype.interestInDollars = function()
	{
		return this.interestInCents / Constants.CentsPerDollar;
	}

	Payment.prototype.principalInDollars = function()
	{
		return this.principalInCents / Constants.CentsPerDollar;
	}
}


function PaymentSchedule(loan, payments)
{
	this.loan = loan;
	this.payments = payments;
}
{
	PaymentSchedule.prototype.toDOMElement = function()
	{
		var returnValue = document.createElement("table");
		returnValue.style.border = "1px solid";

		returnValue.innerHTML = 
			"<tr><td>Date</td>"
			+ "<td>Amount Paid</td>"
			+ "<td>Principal Paid</td>"
			+ "<td>Interest Paid</td>"
			+ "<td>Principal Remaining</td></tr>";
	
		var centsPerDollar = Constants.CentsPerDollar;

		var principalRemainingInCents = 
			this.loan.principal * centsPerDollar;

		for (var i = 0; i < this.payments.length; i++)
		{
			var payment = this.payments[i];

			principalRemainingInCents -= payment.principalInCents;

			var domElementForPayment = document.createElement("tr");

			var paymentAsString = 
				"<td>" 
				+ payment.year + "/" + payment.month 
				+ "</td>"
				+ "<td>" 
				+ payment.amountInDollars()
				+ "</td>"
				+ "<td>" 
				+ payment.principalInDollars()
				+ "</td>"
				+ "<td>" 
				+ payment.interestInDollars() 
				+ "</td>"
				+ "<td>" 
				+ (principalRemainingInCents / centsPerDollar)
				+ "</td>";

			domElementForPayment.innerHTML = paymentAsString;

			returnValue.appendChild(domElementForPayment);
		}

		return returnValue;
	}
}

</script>
</body>
</html>


Advertisements
This entry was posted in Uncategorized and tagged , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s