react-financial-charts
Version:
React charts specific to finance.
154 lines • 7.64 kB
JavaScript
import { group } from "d3-array";
import * as React from "react";
import GenericChartComponent from "../GenericChartComponent";
import { getAxisCanvas } from "../GenericComponent";
import { colorToRGBA, functor, head, isDefined, plotDataLengthBarWidth, } from "../utils";
export class CandlestickSeries extends React.Component {
constructor() {
super(...arguments);
this.drawOnCanvas = (ctx, moreProps) => {
this.drawOnCanvasPrivate(ctx, this.props, moreProps);
};
this.renderSVG = (moreProps) => {
const { className, wickClassName, candleClassName } = this.props;
const { xScale, chartConfig: { yScale }, plotData, xAccessor } = moreProps;
const candleData = this.getCandleData(this.props, xAccessor, xScale, yScale, plotData);
return (React.createElement("g", { className: className },
React.createElement("g", { className: wickClassName, key: "wicks" }, this.getWicksSVG(candleData)),
React.createElement("g", { className: candleClassName, key: "candles" }, this.getCandlesSVG(this.props, candleData))));
};
this.getWicksSVG = (candleData) => {
const wicks = candleData
.map((each, idx) => {
const d = each.wick;
return (React.createElement("path", { key: idx, className: each.className, stroke: d.stroke, d: `M${d.x},${d.y1} L${d.x},${d.y2} M${d.x},${d.y3} L${d.x},${d.y4}` }));
});
return wicks;
};
this.getCandlesSVG = (props, candleData) => {
const { opacity, candleStrokeWidth } = props;
const candles = candleData.map((d, idx) => {
if (d.width <= 1) {
return (React.createElement("line", { className: d.className, key: idx, x1: d.x, y1: d.y, x2: d.x, y2: d.y + d.height, stroke: d.fill }));
}
else if (d.height === 0) {
return (React.createElement("line", { key: idx, x1: d.x, y1: d.y, x2: d.x + d.width, y2: d.y + d.height, stroke: d.fill }));
}
return (React.createElement("rect", { key: idx, className: d.className, fillOpacity: opacity, x: d.x, y: d.y, width: d.width, height: d.height, fill: d.fill, stroke: d.stroke, strokeWidth: candleStrokeWidth }));
});
return candles;
};
this.drawOnCanvasPrivate = (ctx, props, moreProps) => {
const { opacity, candleStrokeWidth = CandlestickSeries.defaultProps.candleStrokeWidth, } = props;
const { xScale, chartConfig: { yScale }, plotData, xAccessor } = moreProps;
const candleData = this.getCandleData(props, xAccessor, xScale, yScale, plotData);
const wickNest = group(candleData, (d) => d.wick.stroke);
wickNest.forEach((values, key) => {
ctx.strokeStyle = key;
ctx.fillStyle = key;
values.forEach((each) => {
const d = each.wick;
ctx.fillRect(d.x - 0.5, d.y1, 1, d.y2 - d.y1);
ctx.fillRect(d.x - 0.5, d.y3, 1, d.y4 - d.y3);
});
});
// @ts-ignore
const candleNest = group(candleData, (d) => d.stroke, (d) => d.fill);
candleNest.forEach((strokeValues, strokeKey) => {
if (strokeKey !== "none") {
ctx.strokeStyle = strokeKey;
ctx.lineWidth = candleStrokeWidth;
}
strokeValues.forEach((values, key) => {
const fillStyle = head(values).width <= 1
? key
: colorToRGBA(key, opacity);
ctx.fillStyle = fillStyle;
values.forEach((d) => {
if (d.width <= 1) {
ctx.fillRect(d.x - 0.5, d.y, 1, d.height);
}
else if (d.height === 0) {
ctx.fillRect(d.x, d.y - 0.5, d.width, 1);
}
else {
ctx.fillRect(d.x, d.y, d.width, d.height);
if (strokeKey !== "none") {
ctx.strokeRect(d.x, d.y, d.width, d.height);
}
}
});
});
});
};
this.getCandleData = (props, xAccessor, xScale, yScale, plotData) => {
const { wickStroke: wickStrokeProp } = props;
const wickStroke = functor(wickStrokeProp);
const { classNames, fill: fillProp, stroke: strokeProp, yAccessor } = props;
const className = functor(classNames);
const fill = functor(fillProp);
const stroke = functor(strokeProp);
const widthFunctor = functor(props.width);
const width = widthFunctor(props, {
xScale,
xAccessor,
plotData,
});
const trueOffset = 0.5 * width;
const offset = trueOffset > 0.7
? Math.round(trueOffset)
: Math.floor(trueOffset);
const candles = [];
// tslint:disable-next-line: prefer-for-of
for (let i = 0; i < plotData.length; i++) {
const d = plotData[i];
if (isDefined(yAccessor(d).close)) {
const x = Math.round(xScale(xAccessor(d)));
const ohlc = yAccessor(d);
const y = Math.round(yScale(Math.max(ohlc.open, ohlc.close)));
const height = Math.max(1, Math.round(Math.abs(yScale(ohlc.open) - yScale(ohlc.close))));
candles.push({
// type: "line"
x: x - offset,
y,
wick: {
stroke: wickStroke(ohlc),
x,
y1: Math.round(yScale(ohlc.high)),
y2: y,
y3: y + height,
y4: Math.round(yScale(ohlc.low)),
},
height,
width: offset * 2,
className: className(ohlc),
fill: fill(ohlc),
stroke: stroke(ohlc),
direction: (ohlc.close - ohlc.open),
});
}
}
return candles;
};
}
render() {
const { clip } = this.props;
return (React.createElement(GenericChartComponent, { clip: clip, svgDraw: this.renderSVG, canvasDraw: this.drawOnCanvas, canvasToDraw: getAxisCanvas, drawOn: ["pan"] }));
}
}
CandlestickSeries.defaultProps = {
candleClassName: "react-financial-charts-candlestick-candle",
candleStrokeWidth: 0.5,
className: "react-financial-charts-candlestick",
classNames: (d) => d.close > d.open ? "up" : "down",
clip: true,
fill: (d) => d.close > d.open ? "#26a69a" : "#ef5350",
opacity: 1,
stroke: (d) => d.close > d.open ? "#26a69a" : "#ef5350",
wickClassName: "react-financial-charts-candlestick-wick",
wickStroke: (d) => d.close > d.open ? "#26a69a" : "#ef5350",
width: plotDataLengthBarWidth,
widthRatio: 0.8,
yAccessor: (d) => ({ open: d.open, high: d.high, low: d.low, close: d.close }),
};
//# sourceMappingURL=CandlestickSeries.js.map