Skip to content

Instantly share code, notes, and snippets.

@schmidsi
Last active January 12, 2024 00:54
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save schmidsi/e871a7c5372812f09978f2704bab10c4 to your computer and use it in GitHub Desktop.
Save schmidsi/e871a7c5372812f09978f2704bab10c4 to your computer and use it in GitHub Desktop.
Order Book Visualisation
license: gpl-3.0
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
#
# Install in Sublime: https://github.com/sindresorhus/editorconfig-sublime#readme
# tl;dr: EditorConfig in Package Control
#
# Install in Atom: https://github.com/sindresorhus/atom-editorconfig
# ```$ apm install editorconfig```
#
# Install in VS Code: https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig
# ```ext install EditorConfig```
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
node_modules

Order Book Visualisation

Preview on Bl.ocks.org

Source on gist.github.com

Node module on npmjs.com

Usage

This Bl.ock is a hybrid between a node module and a d3 Bl.ock. It is a d3 Bl.ock to showcase its possibility but also a node module to be reused in your other projects.

$ npm install --save @melonproject/orderbook-visualisation

To reuse it, the syntax is simple:

import d3 from 'd3';
import drawOrderbook from '@melonproject/orderbook-visualisation';

const svg = d3.select('svg.js-charts');

// ... load data somehow --> data

drawOrderbook(data, svg, d3);

Note: You need to pass d3 as reference (dependency injection) if it is not in the window scope.

[
{
"type": "bid",
"total": 7,
"price": 80.1
},
{
"type": "bid",
"total": 5,
"price": 80.2
},
{
"type": "bid",
"total": 4.5,
"price": 82
},
{
"type": "ask",
"total": 1,
"price": 92
},
{
"type": "bid",
"total": 4,
"price": 82.2
},
{
"type": "bid",
"total": 4,
"price": 82.3
},
{
"type": "bid",
"total": 3,
"price": 85
},
{
"type": "bid",
"total": 2,
"price": 86
},
{
"type": "bid",
"total": 1,
"price": 87
},
{
"type": "ask",
"total": 8,
"price": 97
},
{
"type": "ask",
"total": 1,
"price": 91
},
{
"type": "ask",
"total": 1,
"price": 92
},
{
"type": "ask",
"total": 2,
"price": 93
},
{
"type": "ask",
"total": 3,
"price": 94
},
{
"type": "ask",
"total": 5.3241,
"price": 95
},
{
"type": "ask",
"total": 6,
"price": 96
},
{
"type": "ask",
"total": 8,
"price": 98
}
]
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<style>
.bar.bid {
fill: rgba(40, 193, 44, 0.3);
}
.bar.ask {
fill: rgba(255, 0, 0, 0.4);
}
.bar:hover {
fill: #337ab7;
}
.axis--x path {
display: none;
}
.axis--y path {
display: none;
}
svg, html, body {
width: 100%;
height: 100%;
}
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<svg></svg>
<script src="./script.js"></script>
<script>
const svg = d3.select('svg');
d3.json('data.json', (error, unsortedData) => {
if (error) throw error;
draw(unsortedData, svg);
});
</script>
</body>
{
"name": "@melonproject/orderbook-visualisation",
"description": "Simple order book (or depth of market) visualisation with d3",
"version": "0.0.4",
"scripts": {
"start": "browser-sync start -s . -f ."
},
"main": "script.js",
"repository": {
"type": "git",
"url": "git@gist.github.com:e871a7c5372812f09978f2704bab10c4.git"
},
"keywords": [
"melonport",
"melonproject",
"orderbook",
"visualisation",
"block",
"d3",
"depth-of-market"
],
"author": "Simon Emanuel Schmid <simon@schmid.io>",
"license": "GPL-3.0",
"homepage": "https://bl.ocks.org/schmidsi/e871a7c5372812f09978f2704bab10c4",
"eslintConfig": {
"extends": "airbnb-base",
"env": {
"browser": true
}
},
"devDependencies": {
"browser-sync": "^2.18.11",
"eslint": "^3.19.0",
"eslint-config-airbnb-base": "^11.2.0",
"eslint-plugin-import": "^2.2.0"
}
}
const draw = (unsortedData, target, d3 = window.d3) => {
const margin = { top: 20, right: 20, bottom: 30, left: 40 };
const width = target.node().clientWidth - margin.left - margin.right;
const height = target.node().clientHeight - margin.top - margin.bottom;
const x = d3.scaleLinear().range([0, width]);
const y = d3.scaleLinear().range([height, 0]);
const g = target.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
const data = unsortedData.sort((a, b) => (a.price > b.price ? 1 : -1));
x.domain([
d3.min(data, d => d.price),
d3.max(data, d => d.price) + 1,
]);
y.domain([0, d3.max(data, d => d.total)]);
g.append('g')
.attr('class', 'axis axis--x')
.attr('transform', `translate(0,${height})`)
.call(d3.axisBottom(x));
g.append('g')
.attr('class', 'axis axis--y')
.call(d3.axisLeft(y));
// Define the div for the tooltip
const tooltip = d3.select('body').append('div')
.attr('class', 'orderbook-visualisation-tooltip')
.style('position', 'absolute')
.style('top', `${target.node().parentNode.offsetTop}px`)
.style('left', `${(target.node().parentNode.offsetLeft + margin.left + (width / 2)) - 100}px`)
.style('width', '200px')
.style('opacity', 0)
.html('');
g.selectAll('.bar')
.data(data)
.enter().append('rect')
.attr('class', d => `bar ${d.type}`)
.attr('x', d => x(d.price))
.attr('y', d => y(d.total))
.attr('width', (d, i) => {
// is there a next element and do they have the same type:
// fill until the next order
if (data[i + 1] && data[i + 1].type === d.type) {
return x(data[i + 1].price) - x(d.price);
// is there a next element and they don't have the same type:
// market price valley
} else if (data[i + 1]) {
return (x.range()[1] - x.range()[0]) / data.length;
}
// this is the last element: fill until the end of the graph
return x.range()[1] - x(d.price);
})
.attr('height', d => height - y(d.total))
.on('mouseover', (d) => {
tooltip.transition()
.duration(500)
.style('opacity', 1);
let html = '<table>';
Object.keys(d).forEach((key) => {
html += `<tr><td><b>${key}</b></td><td>${d[key]}</td></tr>`;
});
html += '</table>';
tooltip.html(html);
})
.on('mouseout', () =>
tooltip.transition().duration(500).style('opacity', 0),
);
};
module.exports = draw;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment