市面上有更多關於股票的K線圖,但有時候特殊情況下仍然需要自己高度客製化,就需要自己來動手嚕一張了。
根據圖上需要的部分,分成以下幾個部分。
根據基本K線圖需要的資料,為以下幾種資料源。
箱型為開盤價格以及收盤價格,如果收盤比開盤開,代表紅色(漲),反之則為綠(跌)。
const datas = d3.csvParse(dataCSV);
const width = 1200;
const height = 800;
const padding = 50;
const innerWidth = width - padding * 2;
const innerHeight = height - padding * 2;
// g
const svg = d3.select('svg').attr('width', width).attr('height', height);
const rootLayer = svg.append('g').attr('transform', `translate(${padding}, ${padding})`);
const axisLayer = rootLayer.append('g');
const xAxisLayer = axisLayer.append('g').attr('transform', `translate(${0}, ${innerHeight})`);
const yAxisLayer = axisLayer.append('g');
const lineLayer = rootLayer.append('g');
const rectLayer = rootLayer.append('g');
// ["Date", "Open", "High", "Low", "Close", "Adj Close", "Volume"]
// inital
let xExtent;
let yExtent;
let xScale;
let yScale;
let xAxis;
let yAxis;
let lines;
let rects;
dataCSV
為股價的資料,將會轉為datas
轉成json
。
剩下為準備塗層,以及宣告基本變數。
// 抓出X最大日期及最小日期以及Y軸最大值及最小值。
const calcExtent = () => {
xExtent = [d3.min(datas, data => new Date(data.Date)), d3.max(datas, data => new Date(data.Date))];
yExtent = [d3.min(datas, data => data.Low), d3.max(datas, data => data.High)];
}
// 計算`Scale`算出比例,透過`nice`可以讓我們的`yScale`上下預留一點空。
const calcScale = () => {
xScale = d3.scaleTime().domain(xExtent).range([0, innerWidth]);
yScale = d3.scaleLinear().domain(yExtent).range([innerHeight, 0]).nice();
}
// 繪製Axis比例尺。
const paintAxis = () => {
xAxis = d3.axisBottom().scale(xScale);
yAxis = d3.axisLeft().scale(yScale);
yAxisLayer.call(yAxis);
xAxisLayer.call(xAxis);
}
// 繪製K線圖的線
const paintLines = () => {
lines = lineLayer
.selectAll('line')
.data(datas, data => data.Date)
.enter()
.append('line')
.attr('class', 'line')
.attr('x1', (d, i) => xScale(new Date(d.Date)))
.attr('x2', (d, i) => xScale(new Date(d.Date)))
.attr('y1', d => yScale(d.High))
.attr('y2', d => yScale(d.Low))
// 這邊要判斷開盤以及收盤漲跌
.attr("stroke", d => (d.Open === d.Close) ? "white" : (d.Open > d.Close) ? "green" : "red");
}
// 繪製K線圖的方形
const paintRects = () => {
rects = rectLayer
.selectAll('rect')
.data(datas, data => data.Date)
.enter()
.append('rect')
.attr('class', 'rect')
.attr('x', d => xScale(new Date(d.Date)) - 2)
// 這邊要注意是取得開盤收盤之間最大值,因為目前上方y值為大,所以是取Open, Close之間最大值,來當作方塊的y。
.attr('y', d => yScale(Math.max(d.Open, d.Close)))
.attr('width', 5)
.attr('height', d => Math.abs(yScale(d.Open) - yScale(d.Close)))
.attr('fill', d => (d.Open > d.Close) ? 'green': 'red')
}
calcExtent();
calcScale();
paintAxis();
paintLines();
paintRects();
輕鬆可以嚕出一個K線圖,還可以搭配d3.tip
或是d3.zoom
做到更多功能的K線圖。但還是傾向套用現成圖表比較安全。