indiespirit / react-native-chart-kit

📊React Native Chart Kit: Line Chart, Bezier Line Chart, Progress Ring, Bar chart, Pie chart, Contribution graph (heatmap)
https://expo.io/@indiespirit/react-native-chart-kit
MIT License
2.83k stars 656 forks source link

Candle Stick chart Request #198

Open shibbyy opened 4 years ago

shibbyy commented 4 years ago

Feature request

vitorverasm commented 4 years ago

This would be a great feature to add. If no one looks into that I'll try to implement this.

StankovicMarko commented 4 years ago

any news about candle charts? @vitorverasm

Hermanya commented 4 years ago

No news

vitorverasm commented 4 years ago

Sorry, lately I've been so jammed with work. Hopefully someone take a look into that.

huyhai commented 4 years ago

up

izanf commented 3 years ago

up

Orange9000 commented 2 years ago

I made my own implementation of CandleStick chart by modifing the BarChart component (since I didn't need it in my project). It's wasn't that hard to implement, just a few lines of code. It aint worth a pull request since its barely customizable, but I can share a patch if anyone would like to try it.

Orange9000 commented 2 years ago

Well, someone liked my comment, so here it is. The patch is meant for version 6.10.0 BarChart would expect an array of objects which contain keys: high, low, open, close, timestamp.

diff --git a/node_modules/react-native-chart-kit/dist/BarChart.js b/node_modules/react-native-chart-kit/dist/BarChart.js
index 429d7d9..85f9519 100644
--- a/node_modules/react-native-chart-kit/dist/BarChart.js
+++ b/node_modules/react-native-chart-kit/dist/BarChart.js
@@ -24,29 +24,110 @@ var __assign = (this && this.__assign) || function () {
 };
 import React from "react";
 import { View } from "react-native";
-import { Defs, G, LinearGradient, Rect, Stop, Svg, Text } from "react-native-svg";
+import { Defs, G, LinearGradient, Rect, Stop, Svg, Text, Line } from "react-native-svg";
 import AbstractChart from "./AbstractChart";
 var barWidth = 32;
+
+function calcCandle({ data, width, height }) {
+  const count = data.length
+  const maxHighValue = Math.max(...data.map(e => e.high))
+  const minLowValue = Math.min(...data.map(e => e.low))
+  const pixelValue = (height) / (maxHighValue - minLowValue)
+  let singleBarWidth = (width / count)
+  if (singleBarWidth < 0) {
+    singleBarWidth = 1
+  }
+  return ({
+    pixelValue,
+    singleBarWidth,
+    minLowValue,
+  })
+}
+
 var BarChart = /** @class */ (function (_super) {
     __extends(BarChart, _super);
     function BarChart() {
         var _this = _super !== null && _super.apply(this, arguments) || this;
+        _this.dotsCoordinates = []
         _this.getBarPercentage = function () {
             var _a = _this.props.chartConfig.barPercentage, barPercentage = _a === void 0 ? 1 : _a;
             return barPercentage;
         };
         _this.renderBars = function (_a) {
+            _this.dotsCoordinates = []
+            var candleData = _a.candleData || {}
             var data = _a.data, width = _a.width, height = _a.height, paddingTop = _a.paddingTop, paddingRight = _a.paddingRight, barRadius = _a.barRadius, withCustomBarColorFromData = _a.withCustomBarColorFromData;
             var baseHeight = _this.calcBaseHeight(data, height);
+            var chartYSize = _this.props.chartYSize
+            var candleCalculations = calcCandle({ data: candleData, width, height: chartYSize })
+
+            const {
+              pixelValue,
+              singleBarWidth,
+              minLowValue
+            } = candleCalculations
+
+            function scale(value) {
+              return Math.ceil((value - minLowValue) * pixelValue)
+            }
+
             return data.map(function (x, i) {
-                var barHeight = _this.calcHeight(x, data, height);
-                var barWidth = 32 * _this.getBarPercentage();
-                return (<Rect key={Math.random()} x={paddingRight +
-                    (i * (width - paddingRight)) / data.length +
-                    barWidth / 2} y={((barHeight > 0 ? baseHeight - barHeight : baseHeight) / 4) * 3 +
-                    paddingTop} rx={barRadius} width={barWidth} height={(Math.abs(barHeight) / 4) * 3} fill={withCustomBarColorFromData
-                    ? "url(#customColor_0_" + i + ")"
-                    : "url(#fillShadowGradient)"}/>);
+                let {
+                  close,
+                  open,
+                  high,
+                  low,
+                  timestamp,
+                } = candleData[i] || {}
+
+                var barWidth = singleBarWidth;
+                const barSpacing = 1
+                let rectWidth = barWidth - barSpacing * 2
+
+                const lineStartX = i * (width) / data.length + barWidth / 2
+                const lineEndX = lineStartX
+                let rectStartX = (i * width) / data.length + barSpacing
+
+                let rectHeight = scale(Math.max(open, close)) - scale(Math.min(open, close))
+                let lineStartY = chartYSize - scale(high)
+                let lineEndY = chartYSize - scale(low)
+                let rectStartY = chartYSize - scale(Math.max(open, close))
+
+                const color = open < close ? '#34C800' : '#E05353'
+
+                _this.dotsCoordinates.push({
+                  x: lineStartX,
+                  y: lineStartY,
+                  value: data[i]
+                })
+
+                if (rectHeight == 0) {
+                  rectHeight = 1
+                  rectStartY = lineStartY + 1
+                }
+
+                if (rectWidth <= 2) {
+                  rectWidth = 3
+                  rectStartX -= 0.5
+                }
+
+                return (
+                  <Svg key={`${timestamp}_${i}`}>
+                    <Line
+                      x1={lineStartX}
+                      y1={lineStartY}
+                      x2={lineEndX}
+                      y2={lineEndY}
+                      stroke={color}
+                      strokeWidth="1.5"
+                    />
+                    <Rect
+                      x={rectStartX}
+                      y={rectStartY}
+                      width={rectWidth} height={rectHeight} fill={color}
+                    />
+                  </Svg>
+                )
             });
         };
         _this.renderBarTops = function (_a) {
@@ -56,8 +137,7 @@ var BarChart = /** @class */ (function (_super) {
                 var barHeight = _this.calcHeight(x, data, height);
                 var barWidth = 32 * _this.getBarPercentage();
                 return (<Rect key={Math.random()} x={paddingRight +
-                    (i * (width - paddingRight)) / data.length +
-                    barWidth / 2} y={((baseHeight - barHeight) / 4) * 3 + paddingTop} width={barWidth} height={2} fill={_this.props.chartConfig.color(0.6)}/>);
+                    (i * (width - paddingRight)) / data.length + 0} y={((baseHeight - barHeight) / 4) * 3 + paddingTop} width={barWidth} height={2} fill={_this.props.chartConfig.color(0.6)}/>);
             });
         };
         _this.renderColors = function (_a) {
@@ -94,7 +174,7 @@ var BarChart = /** @class */ (function (_super) {
     BarChart.prototype.render = function () {
         var _a;
         var _b = this.props, width = _b.width, height = _b.height, data = _b.data, _c = _b.style, style = _c === void 0 ? {} : _c, _d = _b.withHorizontalLabels, withHorizontalLabels = _d === void 0 ? true : _d, _e = _b.withVerticalLabels, withVerticalLabels = _e === void 0 ? true : _e, _f = _b.verticalLabelRotation, verticalLabelRotation = _f === void 0 ? 0 : _f, _g = _b.horizontalLabelRotation, horizontalLabelRotation = _g === void 0 ? 0 : _g, _h = _b.withInnerLines, withInnerLines = _h === void 0 ? true : _h, _j = _b.showBarTops, showBarTops = _j === void 0 ? true : _j, _k = _b.withCustomBarColorFromData, withCustomBarColorFromData = _k === void 0 ? false : _k, _l = _b.showValuesOnTopOfBars, showValuesOnTopOfBars = _l === void 0 ? false : _l, _m = _b.flatColor, flatColor = _m === void 0 ? false : _m, _o = _b.segments, segments = _o === void 0 ? 4 : _o;
-        var _p = style.borderRadius, borderRadius = _p === void 0 ? 0 : _p, _q = style.paddingTop, paddingTop = _q === void 0 ? 16 : _q, _r = style.paddingRight, paddingRight = _r === void 0 ? 64 : _r;
+        var _p = style.borderRadius, borderRadius = _p === void 0 ? 0 : _p, _q = style.paddingTop, paddingTop = _q === void 0 ? 16 : _q, _r = style.paddingRight, paddingRight = 0;
         var config = {
             width: width,
             height: height,
@@ -121,6 +201,9 @@ var BarChart = /** @class */ (function (_super) {
             ? this.renderHorizontalLines(__assign(__assign({}, config), { count: segments, paddingTop: paddingTop }))
             : null}
           </G>
+          <G>
+            {this.renderBars(__assign(__assign({}, config), { candleData: data.datasets[0].candleData, data: data.datasets[0].data, paddingTop: paddingTop, paddingRight: paddingRight, withCustomBarColorFromData: withCustomBarColorFromData }))}
+          </G>
           <G>
             {withHorizontalLabels
             ? this.renderHorizontalLabels(__assign(__assign({}, config), { count: segments, data: data.datasets[0].data, paddingTop: paddingTop, paddingRight: paddingRight }))
@@ -131,9 +214,6 @@ var BarChart = /** @class */ (function (_super) {
             ? this.renderVerticalLabels(__assign(__assign({}, config), { labels: data.labels, paddingRight: paddingRight, paddingTop: paddingTop, horizontalOffset: barWidth * this.getBarPercentage() }))
             : null}
           </G>
-          <G>
-            {this.renderBars(__assign(__assign({}, config), { data: data.datasets[0].data, paddingTop: paddingTop, paddingRight: paddingRight, withCustomBarColorFromData: withCustomBarColorFromData }))}
-          </G>
           <G>
             {showValuesOnTopOfBars &&
             this.renderValuesOnTopOfBars(__assign(__assign({}, config), { data: data.datasets[0].data, paddingTop: paddingTop, paddingRight: paddingRight }))}
Orange9000 commented 2 years ago

It may not install on a latest version, but you can modify the module code directly. I suggest installing an addon for .patch files syntax highlighting in your IDE so it is easier to read.