Skip to content

Instantly share code, notes, and snippets.

@poezn
Created February 6, 2014 19:16
Show Gist options
  • Save poezn/8850760 to your computer and use it in GitHub Desktop.
Save poezn/8850760 to your computer and use it in GitHub Desktop.
Amortization schedule (area)
Display the source blob
Display the rendered blob
Raw
<text transform="translate(20, 250)" id="monthid"></text>
<text transform="translate(220, 250)" id="monthlypayment"></text>
<text transform="translate(20, 270)" class="interest"></text>
<text transform="translate(20, 290)" class="principal"></text>
<text transform="translate(80, 500)" text-anchor="middle">Paid interest</text>
<text transform="translate(380, 500)" text-anchor="middle">Future interest</text>
<g id="graph" transform="translate(50 50)">
</g>
<g id="overlays" transform="translate(50 50)">
<line opacity="0.2" transform="translate(104 100)" x1="10" x2="10" y1="-10" y2="230" stroke="#000" stroke-width="6"></line>
<!-- <path class="shadow" transform="translate(100 100)" d="M13 3L20 10L20 20L0 20L0 10L7 3Z" fill="#253799"></path>-->
</g>
{"description":"Amortization schedule (area)","endpoint":"","display":"svg","public":true,"require":[],"fileconfigs":{"inlet.js":{"default":true,"vim":false,"emacs":false,"fontSize":12},"_.md":{"default":true,"vim":false,"emacs":false,"fontSize":12},"config.json":{"default":true,"vim":false,"emacs":false,"fontSize":12},"assets.svg":{"default":true,"vim":false,"emacs":false,"fontSize":12},"styles.css":{"default":true,"vim":false,"emacs":false,"fontSize":12}},"fullscreen":false,"play":false,"loop":false,"restart":false,"autoinit":true,"pause":true,"loop_type":"pingpong","bv":false,"nclones":15,"clone_opacity":0.4,"duration":3000,"ease":"linear","dt":0.01,"inline-console":true,"thumbnail":"http://i.imgur.com/BJ7FdBZ.png"}
// A = i x P x ((1+i)^n)/(1+i)^n - 1
// A = monthly payment
// P = initial principal
// i = monthly interest rate
// n = number of payments
// ==================================
// HERE. THIS DATA CHANGES EVERYTHING
var apr = 5.90000000000001; // in percent
var term = 35; // in years
var p = 303122;
// ALRIGHT WE'RE DONE
// ==================================
var i = apr / 1200;
var n = term * 12;
var monthlyPayment = i * p * Math.pow((1+i), n) / (Math.pow((1+i), n) - 1);
var schedule = [];
var balance = p;
for (var j = 0; j < n; j++) {
var paidInterest = balance * i;
var paidPrincipal = monthlyPayment - paidInterest;
var balance = balance - paidPrincipal;
schedule.push({
amount: monthlyPayment,
paidPrincipal: paidPrincipal,
paidInterest: paidInterest,
balance: balance
})
}
// =========
var w = 525,
h = 200;
var scale = d3.scale.linear()
.domain([0, monthlyPayment])
.range([h, 0]);
var widthScale = d3.scale.linear()
.domain([0, schedule.length])
.range([0, w]);
var format = d3.format("4.2f");
var g = d3.select("#graph");
var renderInterest = function(d, idx) {
var barScale = d3.scale.linear()
.domain([0, d.amount])
.range([0, h]);
console.log(d);
var data = [
d.paidInterest,
d.paidPrincipal
];
var rects = d3.select("#overlays").selectAll("rect.bar")
.data(data);
rects.enter().append("rect")
.attr({
"class": function(d, i) {
return "bar " + (i === 0 ? "interest" : "principal");
},
"stroke": "#FFFFFF",
"stroke-opacity": 0.1,
"stroke-width": 2
});
rects.attr({
"transform": function(d, i) {
var tx = widthScale(idx) - 10,
ty = (i === 0 ? h - barScale(d) : 0);
return "translate(" + [tx, ty].join(" ") + ")"
},
"width": 20,
"height": function(d, i) {
return barScale(d)
}
});
d3.select("#monthid")
.text("Month " + (idx+1) + "/" + n);
d3.select("text.interest")
.text("Interest: $" + format(d.paidInterest));
d3.select("text.principal")
.text("Principal: $" + format(d.paidPrincipal));
d3.select("#monthlypayment")
.text("Monthly Payment: $" + format(monthlyPayment));
d3.selectAll(".shadow, #overlays line")
.attr({
"transform": function(d, i) {
var tx = -10 + widthScale(idx)
return "translate(" + tx + " -3)"
}
})
};
var interestArea = d3.svg.area()
.x(function(d, i) {
return widthScale(i);
})
.y(function(d, i) {
return scale(d.paidInterest);
})
.y0(h);
var principalArea = d3.svg.area()
.x(function(d, i) {
return widthScale(i);
})
.y(function(d, i) {
return scale(d.paidInterest);
})
.y0(0);
var area = g.selectAll(".area")
.data([
{
"path": interestArea(schedule),
"type": "interest"
}, {
"path": principalArea(schedule),
"type": "principal"
}
]);
area.enter().append("path");
area.attr({
"d": function(d, i) {
return d.path;
},
"class": function(d, i) {
return "area " + d.type;
}
})
.on("mousemove", function(ev) {
var mouse = d3.mouse(this);
var xPos = mouse[0];
var idx = Math.round(widthScale.invert(xPos));
renderInterest(schedule[idx], idx);
});
renderInterest(schedule[0], 0);
text {
font-size: 14px;
font-family: "Helvetica Neue", Helvetica, sans-serif
}
.interest {
fill: #CC7EAA;
}
.principal {
fill: #7E83CC;
}
rect.interest {
fill: #CC3289;
}
rect.principal {
fill: #404ACE;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment