Skip to content

Instantly share code, notes, and snippets.

@tophtucker
Last active October 9, 2015 08:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tophtucker/77a322f02f13bf9b475d to your computer and use it in GitHub Desktop.
Save tophtucker/77a322f02f13bf9b475d to your computer and use it in GitHub Desktop.
How LIBOR works

Every day, 16 banks say how much it might cost them to borrow in various currencies, for various lengths of time. The four highest and four lowest figures are discarded, and the average of the remaining eight become that day’s Libor. In this toy model of how Libor works, you have total control over how one bank sets its rates, and some random partial influence on other banks, who will tend to follow you. Move your mouse along the axis to set your rate. Read the full story

<!DOCTYPE html>
<html>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title></title>
<link rel="stylesheet" type="text/css" href="main.css"/>
<body>
<p>Every day, 16 banks say how much it might cost them to borrow in various currencies, for various lengths of time. The four highest and four lowest figures are discarded, and the average of the remaining eight become that day’s Libor.</p>
<svg></svg>
<p>In this toy model of how Libor works, you have total control over how one bank sets its rates, and some random partial influence on other banks, who will tend to follow you. Move your mouse along the axis to set your rate.</p>
</body>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js" charset="utf-8"></script>
<script src="//cdn.rawgit.com/gka/d3-jetpack/master/d3-jetpack.js" charset="utf-8"></script>
<script src="main.js" charset="utf-8"></script>
</html>
body {
font: 12px sans-serif;
width: 960px;
margin: 2em auto;
}
svg {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #ccc;
shape-rendering: crispEdges;
}
.axis text {
fill: #ccc;
}
.dot {
stroke: #000;
}
svg > g {
pointer-events: all;
cursor: ew-resize;
}
g.bank text {
text-anchor: middle;
}
line.libor-span {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
stroke-width: 3;
}
g.bank:not(.accepted) {
fill: #ccc;
}
g.libor-mark text {
text-anchor: middle;
}
g.bank.captured {
fill: red;
}
g.bank line.influence {
stroke: red;
shape-rendering: crispEdges;
}
var margin = {top: 20, right: 20, bottom: 30, left: 20},
width = 960 - margin.left - margin.right,
height = 80 - margin.top - margin.bottom;
var svg = d3.select("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append('rect')
.attr('class', 'click-capture')
.style('visibility', 'hidden')
.attr('x', 0)
.attr('y', 0)
.attr('width', width)
.attr('height', height+margin.bottom);
///
var mouse = false;
var percentage = d3.format(".0%");
var percentage2 = d3.format(".2%");
var numberOfBanks = 16,
liborLowPass = 4,
liborHighPass = 12;
var rRand = d3.random.normal(.05, .01),
drRand = d3.random.normal(0, .00005),
influenceRand = d3.random.logNormal(0,1);
var rates = d3.range(numberOfBanks).map(function(d, i) {
return {
"name": "Bank " + (i+1),
"r": rRand(),
"vr": drRand(),
"influence": influenceRand()/2000,
"captured": false
};
})
var x = d3.scale.linear()
.domain([0,.1])
.range([0,width])
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickSize("10")
.tickFormat(percentage);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.text("Rate");
var bankG = svg.selectAll("g.bank")
.data(rates, ƒ('name'))
.enter()
.append("g.bank");
bankG.append("circle")
.attr("cx", 0)
.attr("cy", 0)
.attr("r", 2);
bankG.append("text")
.attr("dy", "-.5em")
.text(ƒ('name'));
// bankG.append("line.influence")
// .attr("y1", 0)
// .attr("y2", 0)
// .attr("x1", 0)
// .attr("x2", 0);
var liborSpan = svg.append("line.libor-span")
.attr("y1", height)
.attr("y2", height);
var liborMark = svg.append("g.libor-mark");
liborMark.append("path")
.attr("d", d3.svg.symbol().type("triangle-up"));
liborMark.append("text")
.attr("dy", "15px");
d3.timer(function(t) {
rates.forEach(function(d,i) {
if(d.captured) return;
d.r += d.vr;
d.vr += drRand() - 0.2 * d.vr;
if(mouse) {
d.vr += d.influence * (x.invert(mouse[0]) - d.r);
}
})
rates.sort(function(a,b) {
return a.r-b.r;
});
var liborRates = rates.slice(liborLowPass,liborHighPass);
var liborExtent = d3.extent(liborRates, ƒ('r'));
var liborRate = liborRates.reduce(function(a, b) { return a + b.r; }, 0) / liborRates.length;
bankG
.attr("transform", function(d) { return "translate("+x(d.r)+"," + height/2 + ")"; })
.classed("captured", ƒ('captured'))
.classed("accepted", function(d) { return d.r >= liborExtent[0] && d.r <= liborExtent[1]; });
liborMark
.attr("transform", function(d) { return "translate(" + x(liborRate) + "," + (height+5) + ")"; })
.select("text").text(percentage2(liborRate));
liborSpan
.attr("x1", function(d) { return x(liborExtent[0]); })
.attr("x2", function(d) { return x(liborExtent[1]); })
// if(mouse) {
// bankG.select("line.influence").attr("x2", function(d) {
// return d.influence * (mouse[0] - x(d.r));
// });
// } else {
// bankG.select("line.influence").attr("x2", 0);
// }
});
svg.on("mouseenter", function() {
mouse = d3.mouse(this);
rates.filter(function(d,i) { return d.name=="Bank 1"; })[0].captured = true;
})
svg.on("mouseleave", function() {
mouse = false;
rates.filter(function(d,i) { return d.name=="Bank 1"; })[0].captured = false;
})
svg.on("mousemove", function() {
mouse = d3.mouse(this);
rates.filter(function(d,i) { return d.name=="Bank 1"; })[0].r = x.invert(d3.mouse(this)[0]);
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment