binbot-charts 0.4.3 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/components/datafeed.js +100 -103
- package/dist/components/index.js +1 -1
- package/package.json +3 -2
- package/dist/components/TVChartContainer.js +0 -122
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Import it in your project as a React component
|
|
|
6
6
|
|
|
7
7
|
## How to start
|
|
8
8
|
|
|
9
|
-
1. Run `
|
|
9
|
+
1. Run `npm install && npm start`. It will build the project and open a default browser with the Charting Library.
|
|
10
10
|
2. `library_path` should be `node_modules/dist/charting_library`
|
|
11
11
|
3. Write a script to copy `charting_library` to `public/charting_library` during build. E.g. `cp -r node_modules/dist/charting_library/ src/public/charting_library`
|
|
12
12
|
|
|
@@ -15,4 +15,4 @@ Import it in your project as a React component
|
|
|
15
15
|
This project was bootstrapped with [Create React App](https://github.com/facebookincubator/create-react-app).
|
|
16
16
|
|
|
17
17
|
## Notes
|
|
18
|
-
The earliest supported version of the charting library for these examples is `v20`.
|
|
18
|
+
The earliest supported version of Node of the charting library for these examples is `v20`.
|
|
@@ -6,9 +6,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
var _helpers = require("./helpers.js");
|
|
8
8
|
var _streaming = require("./streaming.js");
|
|
9
|
-
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
10
|
-
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
|
|
11
|
-
function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
|
|
12
9
|
const binanceResolutions = ["1s", "1m", "3m", "5m", "15m", "30m", "1h", "2h", "4h", "6h", "8h", "12h", "1d", "3d", "1w", "1M"];
|
|
13
10
|
const getConfigurationData = async () => {
|
|
14
11
|
return {
|
|
@@ -36,111 +33,105 @@ const getConfigurationData = async () => {
|
|
|
36
33
|
class Datafeed {
|
|
37
34
|
constructor() {
|
|
38
35
|
let timescaleMarks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
|
|
39
|
-
let
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
36
|
+
let interval = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "1h";
|
|
37
|
+
this.streaming = null;
|
|
38
|
+
this.timescaleMarks = timescaleMarks;
|
|
39
|
+
this.interval = interval;
|
|
40
|
+
}
|
|
41
|
+
onReady = async callback => {
|
|
42
|
+
this.configurationData = await getConfigurationData();
|
|
43
|
+
callback(this.configurationData);
|
|
44
|
+
};
|
|
45
|
+
searchSymbols = async (userInput, exchange, symbolType, onResultReadyCallback) => {
|
|
46
|
+
const symbols = await (0, _helpers.getAllSymbols)(userInput);
|
|
47
|
+
onResultReadyCallback(symbols);
|
|
48
|
+
};
|
|
49
|
+
resolveSymbol = async (symbolName, onSymbolResolvedCallback, onResolveErrorCallback) => {
|
|
50
|
+
if (!symbolName) {
|
|
51
|
+
await onResolveErrorCallback("cannot resolve symbol");
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const symbolInfo = () => {
|
|
55
|
+
return {
|
|
56
|
+
ticker: symbolName,
|
|
57
|
+
name: symbolName,
|
|
58
|
+
ticker: symbolName,
|
|
59
|
+
description: symbolName,
|
|
60
|
+
type: "crypto",
|
|
61
|
+
session: "24x7",
|
|
62
|
+
timezone: "Etc/UTC",
|
|
63
|
+
exchange: "Binance",
|
|
64
|
+
minmov: 100,
|
|
65
|
+
pricescale: 100000000,
|
|
66
|
+
has_daily: true,
|
|
67
|
+
has_intraday: true,
|
|
68
|
+
has_no_volume: false,
|
|
69
|
+
has_seconds: true,
|
|
70
|
+
seconds_multipliers: [1],
|
|
71
|
+
volume: "hundreds",
|
|
72
|
+
volume_precision: 9,
|
|
73
|
+
data_status: "streaming",
|
|
74
|
+
resolution: "1h"
|
|
75
75
|
};
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
interval = resolution + "m";
|
|
92
|
-
}
|
|
76
|
+
};
|
|
77
|
+
const symbol = await symbolInfo();
|
|
78
|
+
onSymbolResolvedCallback(symbol);
|
|
79
|
+
};
|
|
80
|
+
getBars = async (symbolInfo, resolution, periodParams, onHistoryCallback, onErrorCallback) => {
|
|
81
|
+
const {
|
|
82
|
+
from,
|
|
83
|
+
to,
|
|
84
|
+
firstDataRequest
|
|
85
|
+
} = periodParams;
|
|
86
|
+
let interval = "60"; // 1 hour
|
|
87
|
+
// Calculate interval using resolution data
|
|
88
|
+
if (!/[a-zA-Z]$/.test(resolution)) {
|
|
89
|
+
if (parseInt(resolution) >= 60) {
|
|
90
|
+
interval = parseInt(resolution) / 60 + "h";
|
|
93
91
|
} else {
|
|
94
|
-
interval = resolution
|
|
92
|
+
interval = resolution + "m";
|
|
95
93
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
let bars = [];
|
|
114
|
-
data.forEach(bar => {
|
|
115
|
-
if (bar[0] >= from * 1000 && bar[0] < to * 1000) {
|
|
116
|
-
bars = [...bars, {
|
|
117
|
-
time: bar[0],
|
|
118
|
-
low: bar[3],
|
|
119
|
-
high: bar[2],
|
|
120
|
-
open: bar[1],
|
|
121
|
-
close: bar[4],
|
|
122
|
-
volume: bar[5]
|
|
123
|
-
}];
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
onHistoryCallback(bars, {
|
|
127
|
-
noData: false
|
|
94
|
+
} else {
|
|
95
|
+
interval = resolution.toLowerCase().replace(/[a-z]\b/g, c => c.toLowerCase());
|
|
96
|
+
}
|
|
97
|
+
let urlParameters = {
|
|
98
|
+
symbol: symbolInfo.name,
|
|
99
|
+
interval: interval,
|
|
100
|
+
startTime: Math.abs(from * 1000),
|
|
101
|
+
endTime: Math.abs(to * 1000),
|
|
102
|
+
limit: 600
|
|
103
|
+
};
|
|
104
|
+
const query = Object.keys(urlParameters).map(name => `${name}=${encodeURIComponent(urlParameters[name])}`).join("&");
|
|
105
|
+
try {
|
|
106
|
+
const data = await (0, _helpers.makeApiRequest)(`api/v3/uiKlines?${query}`);
|
|
107
|
+
if (data.Response && data.Response === "Error" || data.length === 0) {
|
|
108
|
+
// "noData" should be set if there is no data in the requested period.
|
|
109
|
+
onHistoryCallback([], {
|
|
110
|
+
noData: true
|
|
128
111
|
});
|
|
129
|
-
|
|
130
|
-
console.log("[getBars]: Get error", error);
|
|
131
|
-
onErrorCallback(error);
|
|
112
|
+
return;
|
|
132
113
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
114
|
+
let bars = [];
|
|
115
|
+
data.forEach(bar => {
|
|
116
|
+
if (bar[0] >= from * 1000 && bar[0] < to * 1000) {
|
|
117
|
+
bars = [...bars, {
|
|
118
|
+
time: bar[0],
|
|
119
|
+
low: bar[3],
|
|
120
|
+
high: bar[2],
|
|
121
|
+
open: bar[1],
|
|
122
|
+
close: bar[4],
|
|
123
|
+
volume: bar[5]
|
|
124
|
+
}];
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
onHistoryCallback(bars, {
|
|
128
|
+
noData: false
|
|
129
|
+
});
|
|
130
|
+
} catch (error) {
|
|
131
|
+
console.log("[getBars]: Get error", error);
|
|
132
|
+
onErrorCallback(error);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
144
135
|
getTimescaleMarks(symbolInfo, from, to, onDataCallback, resolution) {
|
|
145
136
|
if (this.timescaleMarks.length > 0) {
|
|
146
137
|
let timescaleMarks = Object.assign([], this.timescaleMarks);
|
|
@@ -152,5 +143,11 @@ class Datafeed {
|
|
|
152
143
|
const serverTime = data.serverTime / 1000;
|
|
153
144
|
onServertimeCallback(serverTime);
|
|
154
145
|
}
|
|
146
|
+
subscribeBars = (symbolInfo, resolution, onRealtimeCallback, subscribeUID, onResetCacheNeededCallback) => {
|
|
147
|
+
(0, _streaming.subscribeOnStream)(symbolInfo, resolution, onRealtimeCallback, subscribeUID, onResetCacheNeededCallback, this.interval);
|
|
148
|
+
};
|
|
149
|
+
unsubscribeBars = subscriberUID => {
|
|
150
|
+
(0, _streaming.unsubscribeFromStream)(subscriberUID);
|
|
151
|
+
};
|
|
155
152
|
}
|
|
156
153
|
exports.default = Datafeed;
|
package/dist/components/index.js
CHANGED
|
@@ -10,4 +10,4 @@ Object.defineProperty(exports, "TVChartContainer", {
|
|
|
10
10
|
}
|
|
11
11
|
});
|
|
12
12
|
var _TVChartContainer = _interopRequireDefault(require("./TVChartContainer"));
|
|
13
|
-
function _interopRequireDefault(
|
|
13
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.
|
|
2
|
+
"version": "0.5.1",
|
|
3
3
|
"name": "binbot-charts",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"react": "^17.0.1",
|
|
@@ -11,11 +11,12 @@
|
|
|
11
11
|
"@babel/core": "^7.18.13",
|
|
12
12
|
"@babel/polyfill": "^7.12.1",
|
|
13
13
|
"@babel/preset-env": "^7.19.0",
|
|
14
|
+
"@types/node": "^22.7.5",
|
|
14
15
|
"@types/react": "^18.0.26",
|
|
15
16
|
"@types/react-dom": "^18.0.10",
|
|
16
17
|
"immer": "^9.0.17",
|
|
17
18
|
"react-scripts": "^5.0.1",
|
|
18
|
-
"typescript": "^4.9.
|
|
19
|
+
"typescript": "^4.9.5"
|
|
19
20
|
},
|
|
20
21
|
"scripts": {
|
|
21
22
|
"start": "react-scripts start",
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.default = TVChartContainer;
|
|
7
|
-
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
-
var _charting_library = require("../charting_library");
|
|
9
|
-
var _datafeed = _interopRequireDefault(require("./datafeed"));
|
|
10
|
-
var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
11
|
-
var _useImmer = require("use-immer");
|
|
12
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
|
-
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
14
|
-
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
15
|
-
function TVChartContainer(_ref) {
|
|
16
|
-
let {
|
|
17
|
-
symbol = "BTCUSDT",
|
|
18
|
-
interval = "1h",
|
|
19
|
-
libraryPath = "/charting_library/",
|
|
20
|
-
timescaleMarks = [],
|
|
21
|
-
orderLines = [],
|
|
22
|
-
height = "calc(100vh - 80px)",
|
|
23
|
-
onTick,
|
|
24
|
-
getLatestBar
|
|
25
|
-
} = _ref;
|
|
26
|
-
const containerRef = (0, _react.useRef)(null);
|
|
27
|
-
const [chartOrderLines, setChartOrderLines] = (0, _useImmer.useImmer)([]);
|
|
28
|
-
const [widgetState, setWidgetState] = (0, _useImmer.useImmer)(null);
|
|
29
|
-
const [symbolState] = (0, _react.useState)(null);
|
|
30
|
-
const prevTimescaleMarks = (0, _react.useRef)(timescaleMarks);
|
|
31
|
-
(0, _react.useEffect)(() => {
|
|
32
|
-
if (!widgetState) {
|
|
33
|
-
initializeChart("1h");
|
|
34
|
-
}
|
|
35
|
-
if (orderLines && orderLines.length > 0) {
|
|
36
|
-
updateOrderLines(orderLines);
|
|
37
|
-
}
|
|
38
|
-
if (widgetState && symbol !== symbolState) {
|
|
39
|
-
widgetState.setSymbol(symbol, interval);
|
|
40
|
-
}
|
|
41
|
-
if (widgetState && prevTimescaleMarks.current && timescaleMarks !== prevTimescaleMarks.current) {
|
|
42
|
-
widgetState._options.datafeed.timescaleMarks = timescaleMarks;
|
|
43
|
-
prevTimescaleMarks.current = timescaleMarks;
|
|
44
|
-
}
|
|
45
|
-
}, [orderLines, timescaleMarks]);
|
|
46
|
-
const initializeChart = interval => {
|
|
47
|
-
const widgetOptions = {
|
|
48
|
-
symbol: symbol,
|
|
49
|
-
datafeed: new _datafeed.default(timescaleMarks, interval),
|
|
50
|
-
interval: interval,
|
|
51
|
-
container: containerRef.current,
|
|
52
|
-
library_path: libraryPath,
|
|
53
|
-
locale: "en",
|
|
54
|
-
fullscreen: false,
|
|
55
|
-
autosize: true,
|
|
56
|
-
studies_overrides: {},
|
|
57
|
-
symbol_search_request_delay: 1000,
|
|
58
|
-
overrides: {
|
|
59
|
-
volumePaneSize: "small",
|
|
60
|
-
"mainSeriesProperties.barStyle.dontDrawOpen": false
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
|
-
const tvWidget = new _charting_library.widget(widgetOptions);
|
|
64
|
-
tvWidget.onChartReady(() => {
|
|
65
|
-
tvWidget.subscribe("onTick", event => onTick(event));
|
|
66
|
-
setWidgetState(tvWidget);
|
|
67
|
-
|
|
68
|
-
// get latest bar for last price
|
|
69
|
-
const prices = async () => {
|
|
70
|
-
const data = await tvWidget.activeChart().exportData({
|
|
71
|
-
includeTime: false,
|
|
72
|
-
includeSeries: true,
|
|
73
|
-
includedStudies: []
|
|
74
|
-
});
|
|
75
|
-
getLatestBar(data.data[data.data.length - 1]);
|
|
76
|
-
};
|
|
77
|
-
prices();
|
|
78
|
-
});
|
|
79
|
-
};
|
|
80
|
-
const updateOrderLines = orderLines => {
|
|
81
|
-
if (chartOrderLines && chartOrderLines.length > 0) {
|
|
82
|
-
chartOrderLines.forEach(item => {
|
|
83
|
-
orderLines.forEach(order => {
|
|
84
|
-
if (item.id == order.id) {
|
|
85
|
-
item.setText(order.text).setTooltip(order.tooltip).setQuantity(order.quantity).setPrice(order.price);
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
} else {
|
|
90
|
-
if (widgetState && orderLines && orderLines.length > 0) {
|
|
91
|
-
orderLines.forEach(order => {
|
|
92
|
-
const lineStyle = order.lineStyle || 0;
|
|
93
|
-
let chartOrderLine = widgetState.chart().createOrderLine().setText(order.text).setTooltip(order.tooltip).setQuantity(order.quantity).setQuantityFont("inherit 14px Arial").setQuantityBackgroundColor(order.color).setQuantityBorderColor(order.color).setLineStyle(lineStyle).setLineLength(25).setLineColor(order.color).setBodyFont("inherit 14px Arial").setBodyBorderColor(order.color).setBodyTextColor(order.color).setPrice(order.price);
|
|
94
|
-
|
|
95
|
-
// set custom id easier search
|
|
96
|
-
chartOrderLine.id = order.id;
|
|
97
|
-
setChartOrderLines(draft => {
|
|
98
|
-
draft.push(chartOrderLine);
|
|
99
|
-
return draft;
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
};
|
|
105
|
-
return /*#__PURE__*/_react.default.createElement("div", {
|
|
106
|
-
ref: containerRef,
|
|
107
|
-
style: {
|
|
108
|
-
height: height
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
TVChartContainer.propTypes = {
|
|
113
|
-
apiKey: _propTypes.default.string,
|
|
114
|
-
symbol: _propTypes.default.string,
|
|
115
|
-
interval: _propTypes.default.string,
|
|
116
|
-
libraryPath: _propTypes.default.string,
|
|
117
|
-
timescaleMarks: _propTypes.default.array,
|
|
118
|
-
orderLines: _propTypes.default.array,
|
|
119
|
-
height: _propTypes.default.string,
|
|
120
|
-
onTick: _propTypes.default.func,
|
|
121
|
-
getLatestBar: _propTypes.default.func
|
|
122
|
-
};
|