binbot-charts 0.0.21 → 0.1.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.
Files changed (30) hide show
  1. package/dist/components/TVChartContainer.js +41 -77
  2. package/dist/components/datafeed.js +131 -100
  3. package/dist/components/streaming.js +1 -1
  4. package/package.json +5 -4
  5. package/dist/datafeeds/README.md +0 -3
  6. package/dist/datafeeds/udf/README.md +0 -46
  7. package/dist/datafeeds/udf/dist/bundle.js +0 -1
  8. package/dist/datafeeds/udf/lib/data-pulse-provider.js +0 -104
  9. package/dist/datafeeds/udf/lib/helpers.js +0 -20
  10. package/dist/datafeeds/udf/lib/history-provider.js +0 -73
  11. package/dist/datafeeds/udf/lib/iquotes-provider.js +0 -1
  12. package/dist/datafeeds/udf/lib/quotes-provider.js +0 -25
  13. package/dist/datafeeds/udf/lib/quotes-pulse-provider.js +0 -44
  14. package/dist/datafeeds/udf/lib/requester.js +0 -28
  15. package/dist/datafeeds/udf/lib/symbols-storage.js +0 -180
  16. package/dist/datafeeds/udf/lib/udf-compatible-datafeed-base.js +0 -252
  17. package/dist/datafeeds/udf/lib/udf-compatible-datafeed.js +0 -10
  18. package/dist/datafeeds/udf/package.json +0 -17
  19. package/dist/datafeeds/udf/rollup.config.js +0 -25
  20. package/dist/datafeeds/udf/src/data-pulse-provider.ts +0 -152
  21. package/dist/datafeeds/udf/src/helpers.ts +0 -38
  22. package/dist/datafeeds/udf/src/history-provider.ts +0 -134
  23. package/dist/datafeeds/udf/src/iquotes-provider.ts +0 -14
  24. package/dist/datafeeds/udf/src/quotes-provider.ts +0 -37
  25. package/dist/datafeeds/udf/src/quotes-pulse-provider.ts +0 -85
  26. package/dist/datafeeds/udf/src/requester.ts +0 -39
  27. package/dist/datafeeds/udf/src/symbols-storage.ts +0 -298
  28. package/dist/datafeeds/udf/src/udf-compatible-datafeed-base.ts +0 -369
  29. package/dist/datafeeds/udf/src/udf-compatible-datafeed.ts +0 -11
  30. package/dist/datafeeds/udf/tsconfig.json +0 -25
@@ -5,95 +5,59 @@ require("core-js/modules/web.dom-collections.iterator.js");
5
5
  Object.defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
- exports.default = void 0;
8
+ exports.default = TVChartContainer;
9
9
 
10
- var React = _interopRequireWildcard(require("react"));
10
+ var _react = _interopRequireWildcard(require("react"));
11
11
 
12
12
  var _charting_library = require("../charting_library");
13
13
 
14
- function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
15
-
16
- 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; }
17
-
18
- function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
14
+ var _datafeed = _interopRequireDefault(require("./datafeed"));
19
15
 
20
- class TVChartContainer extends React.PureComponent {
21
- constructor(props) {
22
- super(props);
16
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
23
17
 
24
- _defineProperty(this, "tvWidget", null);
18
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
25
19
 
26
- this.ref = /*#__PURE__*/React.createRef();
27
- }
20
+ 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; }
28
21
 
29
- componentDidMount() {
22
+ function TVChartContainer(_ref) {
23
+ let {
24
+ symbol = "AAPL",
25
+ interval = "D",
26
+ libraryPath = "/charting_library/",
27
+ timescaleMarks = [],
28
+ orderLines = []
29
+ } = _ref;
30
+ const containerRef = (0, _react.useRef)(null);
31
+ (0, _react.useEffect)(() => {
30
32
  const widgetOptions = {
31
- symbol: this.props.symbol,
32
- // BEWARE: no trailing slash is expected in feed URL
33
- datafeed: new window.Datafeeds.UDFCompatibleDatafeed(this.props.datafeedUrl),
34
- interval: this.props.interval,
35
- container: this.ref.current,
36
- library_path: this.props.libraryPath,
37
- locale: 'en',
38
- disabled_features: ['use_localstorage_for_settings'],
39
- enabled_features: ['study_templates'],
40
- charts_storage_url: this.props.chartsStorageUrl,
41
- charts_storage_api_version: this.props.chartsStorageApiVersion,
42
- client_id: this.props.clientId,
43
- user_id: this.props.userId,
44
- fullscreen: this.props.fullscreen,
45
- autosize: this.props.autosize,
46
- studies_overrides: this.props.studiesOverrides
33
+ symbol: symbol,
34
+ datafeed: new _datafeed.default(timescaleMarks),
35
+ interval: interval,
36
+ container: containerRef.current,
37
+ library_path: libraryPath,
38
+ locale: "en",
39
+ fullscreen: false,
40
+ autosize: true,
41
+ studies_overrides: {}
47
42
  };
48
43
  const tvWidget = new _charting_library.widget(widgetOptions);
49
- this.tvWidget = tvWidget;
50
44
  tvWidget.onChartReady(() => {
51
- tvWidget.headerReady().then(() => {
52
- const button = tvWidget.createButton();
53
- button.setAttribute('title', 'Click to show a notification popup');
54
- button.classList.add('apply-common-tooltip');
55
- button.addEventListener('click', () => tvWidget.showNoticeDialog({
56
- title: 'Notification',
57
- body: 'TradingView Charting Library API works correctly',
58
- callback: () => {
59
- console.log('Noticed!');
60
- }
61
- }));
62
- button.innerHTML = 'Check API';
45
+ orderLines.forEach(element => {
46
+ tvWidget.chart().createPositionLine().setText(element.text).setTooltip(element.tooltip).setQuantity(element.quantity).setQuantityBackgroundColor(element.color).setQuantityBorderColor(element.color).setLineStyle(0).setLineLength(25).setLineColor(element.color).setBodyBorderColor(element.color).setBodyTextColor(element.color).setPrice(element.price);
63
47
  });
64
- });
65
- }
48
+ }); // returned function will be called on component unmount
66
49
 
67
- componentWillUnmount() {
68
- if (this.tvWidget !== null) {
69
- this.tvWidget.remove();
70
- this.tvWidget = null;
71
- }
72
- }
73
-
74
- render() {
75
- return /*#__PURE__*/React.createElement("div", {
76
- ref: this.ref,
77
- style: {
78
- height: "calc(100vh - 80px)"
50
+ return () => {
51
+ if (this.tvWidget !== null) {
52
+ this.tvWidget.remove();
53
+ this.tvWidget = null;
79
54
  }
80
- });
81
- }
82
-
83
- }
84
-
85
- exports.default = TVChartContainer;
86
-
87
- _defineProperty(TVChartContainer, "defaultProps", {
88
- symbol: 'AAPL',
89
- interval: 'D',
90
- datafeedUrl: 'https://demo_feed.tradingview.com',
91
- libraryPath: '/charting_library/',
92
- chartsStorageUrl: 'https://saveload.tradingview.com',
93
- chartsStorageApiVersion: '1.1',
94
- clientId: 'tradingview.com',
95
- userId: 'public_user_id',
96
- fullscreen: false,
97
- autosize: true,
98
- studiesOverrides: {}
99
- });
55
+ };
56
+ }, []);
57
+ return /*#__PURE__*/_react.default.createElement("div", {
58
+ ref: containerRef,
59
+ style: {
60
+ height: "calc(100vh)"
61
+ }
62
+ });
63
+ }
@@ -24,6 +24,10 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
24
24
  const lastBarsCache = new Map();
25
25
  const exchange = "Binance";
26
26
  const configurationData = {
27
+ // trading_options: true,
28
+ supports_marks: true,
29
+ supports_time: true,
30
+ supports_timescale_marks: true,
27
31
  supported_resolutions: ["1D", "1W", "1M"],
28
32
  exchanges: [{
29
33
  value: "Binance",
@@ -58,115 +62,142 @@ async function getAllSymbols() {
58
62
  return allSymbols;
59
63
  }
60
64
 
61
- class Datafeed {}
65
+ class Datafeed {
66
+ constructor() {
67
+ let timescaleMarks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
68
+ let lineOrders = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
62
69
 
63
- exports.default = Datafeed;
64
-
65
- _defineProperty(Datafeed, "onReady", callback => {
66
- debugger;
67
- console.log("[onReady]: Method call");
68
- setTimeout(() => callback(configurationData));
69
- });
70
+ _defineProperty(this, "onReady", callback => {
71
+ console.log("[onReady]: Method call");
72
+ setTimeout(() => callback(configurationData));
73
+ });
70
74
 
71
- _defineProperty(Datafeed, "searchSymbols", async (userInput, exchange, symbolType, onResultReadyCallback) => {
72
- console.log("[searchSymbols]: Method call");
73
- const symbols = await getAllSymbols();
74
- const newSymbols = symbols.filter(symbol => {
75
- const isExchangeValid = exchange === "" || symbol.exchange === exchange;
76
- const isFullSymbolContainsInput = symbol.full_name.toLowerCase().indexOf(userInput.toLowerCase()) !== -1;
77
- return isExchangeValid && isFullSymbolContainsInput;
78
- });
79
- onResultReadyCallback(newSymbols);
80
- });
75
+ _defineProperty(this, "searchSymbols", async (userInput, exchange, symbolType, onResultReadyCallback) => {
76
+ console.log("[searchSymbols]: Method call");
77
+ const symbols = await getAllSymbols();
78
+ const newSymbols = symbols.filter(symbol => {
79
+ const isExchangeValid = exchange === "" || symbol.exchange === exchange;
80
+ const isFullSymbolContainsInput = symbol.full_name.toLowerCase().indexOf(userInput.toLowerCase()) !== -1;
81
+ return isExchangeValid && isFullSymbolContainsInput;
82
+ });
83
+ onResultReadyCallback(newSymbols);
84
+ });
81
85
 
82
- _defineProperty(Datafeed, "resolveSymbol", async (symbolName, onSymbolResolvedCallback, onResolveErrorCallback) => {
83
- console.log("[resolveSymbol]: Method call", symbolName);
84
- const symbols = await getAllSymbols();
85
- const symbolItem = symbols.find(_ref => {
86
- let {
87
- full_name
88
- } = _ref;
89
- return full_name === symbolName;
90
- });
91
-
92
- if (!symbolItem) {
93
- console.log("[resolveSymbol]: Cannot resolve symbol", symbolName);
94
- onResolveErrorCallback("cannot resolve symbol");
95
- return;
96
- }
86
+ _defineProperty(this, "resolveSymbol", async (symbolName, onSymbolResolvedCallback, onResolveErrorCallback) => {
87
+ console.log("[resolveSymbol]: Method call", symbolName);
88
+ const symbols = await getAllSymbols();
89
+ const symbolItem = symbols.find(_ref => {
90
+ let {
91
+ full_name
92
+ } = _ref;
93
+ return full_name === symbolName;
94
+ });
97
95
 
98
- const symbolInfo = {
99
- ticker: symbolItem.full_name,
100
- name: symbolItem.symbol,
101
- description: symbolItem.description,
102
- type: symbolItem.type,
103
- session: "24x7",
104
- timezone: "Etc/UTC",
105
- exchange: symbolItem.exchange,
106
- minmov: 1,
107
- pricescale: 100,
108
- has_intraday: false,
109
- has_no_volume: true,
110
- has_weekly_and_monthly: false,
111
- supported_resolutions: ["1D", "1W", "1M"],
112
- volume_precision: 2,
113
- data_status: "streaming"
114
- };
115
- console.log("[resolveSymbol]: Symbol resolved", symbolName);
116
- onSymbolResolvedCallback(symbolInfo);
117
- });
96
+ if (!symbolItem) {
97
+ console.log("[resolveSymbol]: Cannot resolve symbol", symbolName);
98
+ onResolveErrorCallback("cannot resolve symbol");
99
+ return;
100
+ }
118
101
 
119
- _defineProperty(Datafeed, "getBars", async (symbolInfo, resolution, periodParams, onHistoryCallback, onErrorCallback) => {
120
- const {
121
- from,
122
- to,
123
- firstDataRequest
124
- } = periodParams;
125
- console.log("[getBars]: Method call", symbolInfo, resolution, from, to);
126
- const parsedSymbol = (0, _helpers.parseFullSymbol)(symbolInfo.full_name);
127
- const urlParameters = {
128
- e: parsedSymbol.exchange,
129
- fsym: parsedSymbol.fromSymbol,
130
- tsym: parsedSymbol.toSymbol,
131
- toTs: to,
132
- limit: 2000
133
- };
134
- const query = Object.keys(urlParameters).map(name => "".concat(name, "=").concat(encodeURIComponent(urlParameters[name]))).join("&");
135
-
136
- try {
137
- const data = await (0, _helpers.makeApiRequest)("data/histoday?".concat(query));
138
-
139
- if (data.Response && data.Response === "Error" || data.Data.length === 0) {
140
- // "noData" should be set if there is no data in the requested period.
141
- onHistoryCallback([], {
142
- noData: true
143
- });
144
- return;
145
- }
102
+ const symbolInfo = {
103
+ ticker: symbolItem.full_name,
104
+ name: symbolItem.symbol,
105
+ description: symbolItem.description,
106
+ type: symbolItem.type,
107
+ session: "24x7",
108
+ timezone: "Etc/UTC",
109
+ exchange: symbolItem.exchange,
110
+ minmov: 1,
111
+ pricescale: 100,
112
+ has_intraday: true,
113
+ has_no_volume: false,
114
+ has_weekly_and_monthly: false,
115
+ supported_resolutions: ["1D", "1W", "1M"],
116
+ volume_precision: 4,
117
+ data_status: "streaming"
118
+ };
119
+ console.log("[resolveSymbol]: Symbol resolved", symbolName);
120
+ onSymbolResolvedCallback(symbolInfo);
121
+ });
146
122
 
147
- let bars = [];
148
- data.Data.forEach(bar => {
149
- if (bar.time >= from && bar.time < to) {
150
- bars = [...bars, {
151
- time: bar.time * 1000,
152
- low: bar.low,
153
- high: bar.high,
154
- open: bar.open,
155
- close: bar.close
156
- }];
123
+ _defineProperty(this, "getBars", async (symbolInfo, resolution, periodParams, onHistoryCallback, onErrorCallback) => {
124
+ const {
125
+ from,
126
+ to,
127
+ firstDataRequest
128
+ } = periodParams;
129
+ console.log("[getBars]: Method call");
130
+ const parsedSymbol = (0, _helpers.parseFullSymbol)(symbolInfo.full_name);
131
+ const urlParameters = {
132
+ e: parsedSymbol.exchange,
133
+ fsym: parsedSymbol.fromSymbol,
134
+ tsym: parsedSymbol.toSymbol,
135
+ toTs: to,
136
+ limit: 2000
137
+ };
138
+ const query = Object.keys(urlParameters).map(name => "".concat(name, "=").concat(encodeURIComponent(urlParameters[name]))).join("&");
139
+
140
+ try {
141
+ const data = await (0, _helpers.makeApiRequest)("data/histoday?".concat(query));
142
+
143
+ if (data.Response && data.Response === "Error" || data.Data.length === 0) {
144
+ // "noData" should be set if there is no data in the requested period.
145
+ onHistoryCallback([], {
146
+ noData: true
147
+ });
148
+ return;
149
+ }
150
+
151
+ let bars = [];
152
+ data.Data.forEach(bar => {
153
+ if (bar.time >= from && bar.time < to) {
154
+ bars = [...bars, {
155
+ time: bar.time * 1000,
156
+ low: bar.low,
157
+ high: bar.high,
158
+ open: bar.open,
159
+ close: bar.close
160
+ }];
161
+ }
162
+ });
163
+
164
+ if (firstDataRequest) {
165
+ lastBarsCache.set(symbolInfo.full_name, _objectSpread({}, bars[bars.length - 1]));
166
+ }
167
+
168
+ console.log("[getBars]: returned ".concat(bars.length, " bar(s)"));
169
+ onHistoryCallback(bars, {
170
+ noData: false
171
+ });
172
+ } catch (error) {
173
+ console.log("[getBars]: Get error", error);
174
+ onErrorCallback(error);
157
175
  }
158
176
  });
159
177
 
160
- if (firstDataRequest) {
161
- lastBarsCache.set(symbolInfo.full_name, _objectSpread({}, bars[bars.length - 1]));
162
- }
178
+ _defineProperty(this, "subscribeBars", (symbolInfo, resolution, onRealtimeCallback, subscribeUID, onResetCacheNeededCallback) => {
179
+ console.log("[subscribeBars]: Method call with subscribeUID:", subscribeUID);
180
+ (0, _streaming.subscribeOnStream)(symbolInfo, resolution, onRealtimeCallback, subscribeUID, onResetCacheNeededCallback, lastBarsCache.get(symbolInfo.full_name));
181
+ });
163
182
 
164
- console.log("[getBars]: returned ".concat(bars.length, " bar(s)"));
165
- onHistoryCallback(bars, {
166
- noData: false
183
+ _defineProperty(this, "unsubscribeBars", subscriberUID => {
184
+ console.log("[unsubscribeBars]: Method call with subscriberUID:", subscriberUID);
185
+ (0, _streaming.unsubscribeFromStream)(subscriberUID);
167
186
  });
168
- } catch (error) {
169
- console.log("[getBars]: Get error", error);
170
- onErrorCallback(error);
187
+
188
+ this.timescaleMarks = timescaleMarks;
189
+ this.lineOrders = lineOrders;
190
+ }
191
+
192
+ getTimescaleMarks(symbolInfo, from, to, onDataCallback, resolution) {
193
+ let timescaleMarks = [];
194
+
195
+ if (this.timescaleMarks.length > 0) {
196
+ timescaleMarks = this.timescaleMarks;
197
+ onDataCallback(timescaleMarks);
198
+ }
171
199
  }
172
- });
200
+
201
+ }
202
+
203
+ exports.default = Datafeed;
@@ -41,7 +41,7 @@ socket.on("error", error => {
41
41
  });
42
42
  socket.on("m", data => {
43
43
  console.log("[socket] Message:", data);
44
- const [eventTypeStr, exchange, fromSymbol, toSymbol,,, tradeTimeStr,, tradePriceStr] = data.split("~");
44
+ const [eventTypeStr, exchange, fromSymbol, toSymbol, tradeTimeStr, tradePriceStr] = data.split("~");
45
45
 
46
46
  if (parseInt(eventTypeStr) !== 0) {
47
47
  // skip all non-TRADE events
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.0.21",
2
+ "version": "0.1.1",
3
3
  "name": "binbot-charts",
4
4
  "dependencies": {
5
5
  "@babel/cli": "^7.18.10",
@@ -9,12 +9,13 @@
9
9
  "react": "^17.0.1",
10
10
  "react-dom": "^17.0.1",
11
11
  "react-scripts": "5.0.1",
12
- "socket.io-client": "^4.5.2"
12
+ "socket.io-client": "^4.5.2",
13
+ "use-immer": "^0.7.0"
13
14
  },
14
15
  "scripts": {
15
16
  "start": "react-scripts start",
16
- "build": "rm -rf dist && NODE_ENV=production babel src/components/ -d dist/components && cp -r src/charting_library dist/charting_library && cp -r public/datafeeds/ dist/datafeeds/",
17
- "publish": "yarn build && npm publish"
17
+ "build": "rm -rf dist && NODE_ENV=production babel src/components/ -d dist/components && cp -r src/charting_library dist/charting_library",
18
+ "release": "yarn build && npm publish"
18
19
  },
19
20
  "description": "Binbot charts is the default candlestick bars chart used in terminal.binbot.com to render bots graphically.",
20
21
  "repository": {
@@ -1,3 +0,0 @@
1
- # Charting Library Datafeeds
2
-
3
- This folder contains implementation of Charting Library Datafeeds.
@@ -1,46 +0,0 @@
1
- # UDF Compatible Datafeed
2
-
3
- This folder contains [UDF](https://github.com/tradingview/charting_library/wiki/UDF) datafeed adapter. It implements [JS API](https://github.com/tradingview/charting_library/wiki/JS%20API) and makes HTTP requests using [UDF](https://github.com/tradingview/charting_library/wiki/UDF) protocol.
4
-
5
- You can use this datafeed adapter to plug your data if you implement [UDF](https://github.com/tradingview/charting_library/wiki/UDF) on your server. You can also scrutinize how it works before writing your own adapter.
6
-
7
- This datafeed is implemented in [TypeScript](https://github.com/Microsoft/TypeScript/).
8
-
9
- ## Folders content
10
-
11
- - `./src` folder contains the source code in TypeScript.
12
-
13
- - `./lib` folder contains transpiled in es5 code. So, if you do not know how to use TypeScript - you can modify these files to change the result bundle later.
14
-
15
- - `./dist` folder contains bundled JavaScript files which can be inlined into a page and used in the Widget Constructor.
16
-
17
- ## Build & bundle
18
-
19
- Before building or bundling your code you need to run `npm install` to install dependencies.
20
-
21
- `package.json` contains some handy scripts to build or generate the bundle:
22
-
23
- - `npm run compile` to compile TypeScript source code into JavaScript files (output will be in `./lib` folder)
24
- - `npm run bundle-js` to bundle multiple JavaScript files into one bundle (it also bundle polyfills)
25
- - `npm run build` to compile and bundle (it is a combination of all above commands)
26
-
27
- NOTE: if you want to minify the bundle code, you need to set `ENV` environment variable to a value different from `development`.
28
-
29
- For example:
30
-
31
- ```bash
32
- export ENV=prod
33
- npm run bundle-js # or npm run build
34
- ```
35
-
36
- or
37
-
38
- ```bash
39
- ENV=prod npm run bundle-js
40
- ```
41
-
42
- or
43
-
44
- ```bash
45
- ENV=prod npm run build
46
- ```
@@ -1 +0,0 @@
1
- !function(e,s){"object"==typeof exports&&"undefined"!=typeof module?s(exports):"function"==typeof define&&define.amd?define(["exports"],s):s((e="undefined"!=typeof globalThis?globalThis:e||self).Datafeeds={})}(this,(function(e){"use strict";function s(e){return void 0===e?"":"string"==typeof e?e:e.message}class t{constructor(e,s){this._datafeedUrl=e,this._requester=s}getBars(e,t,r){const o={symbol:e.ticker||"",resolution:t,from:r.from,to:r.to};return void 0!==r.countBack&&(o.countback=r.countBack),void 0!==e.currency_code&&(o.currencyCode=e.currency_code),void 0!==e.unit_id&&(o.unitId=e.unit_id),new Promise(((e,t)=>{this._requester.sendRequest(this._datafeedUrl,"history",o).then((s=>{if("ok"!==s.s&&"no_data"!==s.s)return void t(s.errmsg);const r=[],o={noData:!1};if("no_data"===s.s)o.noData=!0,o.nextTime=s.nextTime;else{const e=void 0!==s.v,t=void 0!==s.o;for(let o=0;o<s.t.length;++o){const i={time:1e3*s.t[o],close:parseFloat(s.c[o]),open:parseFloat(s.c[o]),high:parseFloat(s.c[o]),low:parseFloat(s.c[o])};t&&(i.open=parseFloat(s.o[o]),i.high=parseFloat(s.h[o]),i.low=parseFloat(s.l[o])),e&&(i.volume=parseFloat(s.v[o])),r.push(i)}}e({bars:r,meta:o})})).catch((e=>{const r=s(e);console.warn(`HistoryProvider: getBars() failed, error=${r}`),t(r)}))}))}}class r{constructor(e,s){this._subscribers={},this._requestsPending=0,this._historyProvider=e,setInterval(this._updateData.bind(this),s)}subscribeBars(e,s,t,r){this._subscribers.hasOwnProperty(r)||(this._subscribers[r]={lastBarTime:null,listener:t,resolution:s,symbolInfo:e},e.name)}unsubscribeBars(e){delete this._subscribers[e]}_updateData(){if(!(this._requestsPending>0)){this._requestsPending=0;for(const e in this._subscribers)this._requestsPending+=1,this._updateDataForSubscriber(e).then((()=>{this._requestsPending-=1,this._requestsPending})).catch((e=>{this._requestsPending-=1,s(e),this._requestsPending}))}}_updateDataForSubscriber(e){const s=this._subscribers[e],t=parseInt((Date.now()/1e3).toString()),r=t-function(e,s){let t=0;t="D"===e||"1D"===e?s:"M"===e||"1M"===e?31*s:"W"===e||"1W"===e?7*s:s*parseInt(e)/1440;return 24*t*60*60}(s.resolution,10);return this._historyProvider.getBars(s.symbolInfo,s.resolution,{from:r,to:t,countBack:2,firstDataRequest:!1}).then((s=>{this._onSubscriberDataReceived(e,s)}))}_onSubscriberDataReceived(e,s){if(!this._subscribers.hasOwnProperty(e))return;const t=s.bars;if(0===t.length)return;const r=t[t.length-1],o=this._subscribers[e];if(null!==o.lastBarTime&&r.time<o.lastBarTime)return;if(null!==o.lastBarTime&&r.time>o.lastBarTime){if(t.length<2)throw new Error("Not enough bars in history for proper pulse update. Need at least 2.");const e=t[t.length-2];o.listener(e)}o.lastBarTime=r.time,o.listener(r)}}class o{constructor(e){this._subscribers={},this._requestsPending=0,this._quotesProvider=e,setInterval(this._updateQuotes.bind(this,1),1e4),setInterval(this._updateQuotes.bind(this,0),6e4)}subscribeQuotes(e,s,t,r){this._subscribers[r]={symbols:e,fastSymbols:s,listener:t}}unsubscribeQuotes(e){delete this._subscribers[e]}_updateQuotes(e){if(!(this._requestsPending>0))for(const t in this._subscribers){this._requestsPending++;const r=this._subscribers[t];this._quotesProvider.getQuotes(1===e?r.fastSymbols:r.symbols).then((e=>{this._requestsPending--,this._subscribers.hasOwnProperty(t)&&(r.listener(e),this._requestsPending)})).catch((e=>{this._requestsPending--,s(e),this._requestsPending}))}}}function i(e,s,t,r){const o=e[s];return!Array.isArray(o)||r&&!Array.isArray(o[0])?o:o[t]}function n(e,s,t){return e+(void 0!==s?"_%|#|%_"+s:"")+(void 0!==t?"_%|#|%_"+t:"")}class a{constructor(e,s,t){this._exchangesList=["NYSE","FOREX","AMEX"],this._symbolsInfo={},this._symbolsList=[],this._datafeedUrl=e,this._datafeedSupportedResolutions=s,this._requester=t,this._readyPromise=this._init(),this._readyPromise.catch((e=>{console.error(`SymbolsStorage: Cannot init, error=${e.toString()}`)}))}resolveSymbol(e,s,t){return this._readyPromise.then((()=>{const r=this._symbolsInfo[n(e,s,t)];return void 0===r?Promise.reject("invalid symbol"):Promise.resolve(r)}))}searchSymbols(e,s,t,r){return this._readyPromise.then((()=>{const o=[],i=0===e.length;e=e.toUpperCase();for(const r of this._symbolsList){const n=this._symbolsInfo[r];if(void 0===n)continue;if(t.length>0&&n.type!==t)continue;if(s&&s.length>0&&n.exchange!==s)continue;const a=n.name.toUpperCase().indexOf(e),u=n.description.toUpperCase().indexOf(e);if(i||a>=0||u>=0){if(!o.some((e=>e.symbolInfo===n))){const e=a>=0?a:8e3+u;o.push({symbolInfo:n,weight:e})}}}const n=o.sort(((e,s)=>e.weight-s.weight)).slice(0,r).map((e=>{const s=e.symbolInfo;return{symbol:s.name,full_name:s.full_name,description:s.description,exchange:s.exchange,params:[],type:s.type,ticker:s.name}}));return Promise.resolve(n)}))}_init(){const e=[],s={};for(const t of this._exchangesList)s[t]||(s[t]=!0,e.push(this._requestExchangeData(t)));return Promise.all(e).then((()=>{this._symbolsList.sort()}))}_requestExchangeData(e){return new Promise(((t,r)=>{this._requester.sendRequest(this._datafeedUrl,"symbol_info",{group:e}).then((s=>{try{this._onExchangeDataReceived(e,s)}catch(e){return void r(e)}t()})).catch((e=>{s(e),t()}))}))}_onExchangeDataReceived(e,s){let t=0;try{const e=s.symbol.length,r=void 0!==s.ticker;for(;t<e;++t){const e=s.symbol[t],o=i(s,"exchange-listed",t),a=i(s,"exchange-traded",t),c=a+":"+e,h=i(s,"currency-code",t),l=i(s,"unit-id",t),d=r?i(s,"ticker",t):e,_={ticker:d,name:e,base_name:[o+":"+e],full_name:c,listed_exchange:o,exchange:a,currency_code:h,original_currency_code:i(s,"original-currency-code",t),unit_id:l,original_unit_id:i(s,"original-unit-id",t),unit_conversion_types:i(s,"unit-conversion-types",t,!0),description:i(s,"description",t),has_intraday:u(i(s,"has-intraday",t),!1),has_no_volume:u(i(s,"has-no-volume",t),!1),minmov:i(s,"minmovement",t)||i(s,"minmov",t)||0,minmove2:i(s,"minmove2",t)||i(s,"minmov2",t),fractional:i(s,"fractional",t),pricescale:i(s,"pricescale",t),type:i(s,"type",t),session:i(s,"session-regular",t),session_holidays:i(s,"session-holidays",t),corrections:i(s,"corrections",t),timezone:i(s,"timezone",t),supported_resolutions:u(i(s,"supported-resolutions",t,!0),this._datafeedSupportedResolutions),has_daily:u(i(s,"has-daily",t),!0),intraday_multipliers:u(i(s,"intraday-multipliers",t,!0),["1","5","15","30","60"]),has_weekly_and_monthly:i(s,"has-weekly-and-monthly",t),has_empty_bars:i(s,"has-empty-bars",t),volume_precision:u(i(s,"volume-precision",t),0),format:"price"};this._symbolsInfo[d]=_,this._symbolsInfo[e]=_,this._symbolsInfo[c]=_,void 0===h&&void 0===l||(this._symbolsInfo[n(d,h,l)]=_,this._symbolsInfo[n(e,h,l)]=_,this._symbolsInfo[n(c,h,l)]=_),this._symbolsList.push(e)}}catch(r){throw new Error(`SymbolsStorage: API error when processing exchange ${e} symbol #${t} (${s.symbol[t]}): ${r.message}`)}}}function u(e,s){return void 0!==e?e:s}function c(e,s,t){const r=e[s];return Array.isArray(r)?r[t]:r}class h{constructor(e,s){this._datafeedUrl=e,this._requester=s}getQuotes(e){return new Promise(((t,r)=>{this._requester.sendRequest(this._datafeedUrl,"quotes",{symbols:e}).then((e=>{"ok"===e.s?t(e.d):r(e.errmsg)})).catch((e=>{const t=s(e);r(`network error: ${t}`)}))}))}}class l{constructor(e){e&&(this._headers=e)}sendRequest(e,s,t){if(void 0!==t){const e=Object.keys(t);0!==e.length&&(s+="?"),s+=e.map((e=>`${encodeURIComponent(e)}=${encodeURIComponent(t[e].toString())}`)).join("&")}const r={credentials:"same-origin"};return void 0!==this._headers&&(r.headers=this._headers),fetch(`${e}/${s}`,r).then((e=>e.text())).then((e=>JSON.parse(e)))}}e.UDFCompatibleDatafeed=class extends class{constructor(e,s,i,n=1e4){this._configuration={supports_search:!1,supports_group_request:!0,supported_resolutions:["1","5","15","30","60","1D","1W","1M"],supports_marks:!1,supports_timescale_marks:!1},this._symbolsStorage=null,this._datafeedURL=e,this._requester=i,this._historyProvider=new t(e,this._requester),this._quotesProvider=s,this._dataPulseProvider=new r(this._historyProvider,n),this._quotesPulseProvider=new o(this._quotesProvider),this._configurationReadyPromise=this._requestConfiguration().then((e=>{null===e&&(e={supports_search:!1,supports_group_request:!0,supported_resolutions:["1","5","15","30","60","1D","1W","1M"],supports_marks:!1,supports_timescale_marks:!1}),this._setupWithConfiguration(e)}))}onReady(e){this._configurationReadyPromise.then((()=>{e(this._configuration)}))}getQuotes(e,s,t){this._quotesProvider.getQuotes(e).then(s).catch(t)}subscribeQuotes(e,s,t,r){this._quotesPulseProvider.subscribeQuotes(e,s,t,r)}unsubscribeQuotes(e){this._quotesPulseProvider.unsubscribeQuotes(e)}getMarks(e,t,r,o,i){if(!this._configuration.supports_marks)return;const n={symbol:e.ticker||"",from:t,to:r,resolution:i};this._send("marks",n).then((e=>{if(!Array.isArray(e)){const s=[];for(let t=0;t<e.id.length;++t)s.push({id:c(e,"id",t),time:c(e,"time",t),color:c(e,"color",t),text:c(e,"text",t),label:c(e,"label",t),labelFontColor:c(e,"labelFontColor",t),minSize:c(e,"minSize",t)});e=s}o(e)})).catch((e=>{s(e),o([])}))}getTimescaleMarks(e,t,r,o,i){if(!this._configuration.supports_timescale_marks)return;const n={symbol:e.ticker||"",from:t,to:r,resolution:i};this._send("timescale_marks",n).then((e=>{if(!Array.isArray(e)){const s=[];for(let t=0;t<e.id.length;++t)s.push({id:c(e,"id",t),time:c(e,"time",t),color:c(e,"color",t),label:c(e,"label",t),tooltip:c(e,"tooltip",t)});e=s}o(e)})).catch((e=>{s(e),o([])}))}getServerTime(e){this._configuration.supports_time&&this._send("time").then((s=>{const t=parseInt(s);isNaN(t)||e(t)})).catch((e=>{s(e)}))}searchSymbols(e,t,r,o){if(this._configuration.supports_search){const i={limit:30,query:e.toUpperCase(),type:r,exchange:t};this._send("search",i).then((e=>{if(void 0!==e.s)return e.errmsg,void o([]);o(e)})).catch((e=>{s(e),o([])}))}else{if(null===this._symbolsStorage)throw new Error("UdfCompatibleDatafeed: inconsistent configuration (symbols storage)");this._symbolsStorage.searchSymbols(e,t,r,30).then(o).catch(o.bind(null,[]))}}resolveSymbol(e,t,r,o){const i=o&&o.currencyCode,n=o&&o.unitId;function a(e){t(e)}if(this._configuration.supports_group_request){if(null===this._symbolsStorage)throw new Error("UdfCompatibleDatafeed: inconsistent configuration (symbols storage)");this._symbolsStorage.resolveSymbol(e,i,n).then(a).catch(r)}else{const t={symbol:e};void 0!==i&&(t.currencyCode=i),void 0!==n&&(t.unitId=n),this._send("symbols",t).then((e=>{void 0!==e.s?r("unknown_symbol"):a(e)})).catch((e=>{s(e),r("unknown_symbol")}))}}getBars(e,s,t,r,o){this._historyProvider.getBars(e,s,t).then((e=>{r(e.bars,e.meta)})).catch(o)}subscribeBars(e,s,t,r,o){this._dataPulseProvider.subscribeBars(e,s,t,r)}unsubscribeBars(e){this._dataPulseProvider.unsubscribeBars(e)}_requestConfiguration(){return this._send("config").catch((e=>(s(e),null)))}_send(e,s){return this._requester.sendRequest(this._datafeedURL,e,s)}_setupWithConfiguration(e){if(this._configuration=e,void 0===e.exchanges&&(e.exchanges=[]),!e.supports_search&&!e.supports_group_request)throw new Error("Unsupported datafeed configuration. Must either support search, or support group request");!e.supports_group_request&&e.supports_search||(this._symbolsStorage=new a(this._datafeedURL,e.supported_resolutions||[],this._requester)),JSON.stringify(e)}}{constructor(e,s=1e4){const t=new l;super(e,new h(e,t),t,s)}},Object.defineProperty(e,"__esModule",{value:!0})}));
@@ -1,104 +0,0 @@
1
- import { getErrorMessage, logMessage, } from './helpers';
2
- export class DataPulseProvider {
3
- constructor(historyProvider, updateFrequency) {
4
- this._subscribers = {};
5
- this._requestsPending = 0;
6
- this._historyProvider = historyProvider;
7
- setInterval(this._updateData.bind(this), updateFrequency);
8
- }
9
- subscribeBars(symbolInfo, resolution, newDataCallback, listenerGuid) {
10
- if (this._subscribers.hasOwnProperty(listenerGuid)) {
11
- logMessage(`DataPulseProvider: already has subscriber with id=${listenerGuid}`);
12
- return;
13
- }
14
- this._subscribers[listenerGuid] = {
15
- lastBarTime: null,
16
- listener: newDataCallback,
17
- resolution: resolution,
18
- symbolInfo: symbolInfo,
19
- };
20
- logMessage(`DataPulseProvider: subscribed for #${listenerGuid} - {${symbolInfo.name}, ${resolution}}`);
21
- }
22
- unsubscribeBars(listenerGuid) {
23
- delete this._subscribers[listenerGuid];
24
- logMessage(`DataPulseProvider: unsubscribed for #${listenerGuid}`);
25
- }
26
- _updateData() {
27
- if (this._requestsPending > 0) {
28
- return;
29
- }
30
- this._requestsPending = 0;
31
- for (const listenerGuid in this._subscribers) { // tslint:disable-line:forin
32
- this._requestsPending += 1;
33
- this._updateDataForSubscriber(listenerGuid)
34
- .then(() => {
35
- this._requestsPending -= 1;
36
- logMessage(`DataPulseProvider: data for #${listenerGuid} updated successfully, pending=${this._requestsPending}`);
37
- })
38
- .catch((reason) => {
39
- this._requestsPending -= 1;
40
- logMessage(`DataPulseProvider: data for #${listenerGuid} updated with error=${getErrorMessage(reason)}, pending=${this._requestsPending}`);
41
- });
42
- }
43
- }
44
- _updateDataForSubscriber(listenerGuid) {
45
- const subscriptionRecord = this._subscribers[listenerGuid];
46
- const rangeEndTime = parseInt((Date.now() / 1000).toString());
47
- // BEWARE: please note we really need 2 bars, not the only last one
48
- // see the explanation below. `10` is the `large enough` value to work around holidays
49
- const rangeStartTime = rangeEndTime - periodLengthSeconds(subscriptionRecord.resolution, 10);
50
- return this._historyProvider.getBars(subscriptionRecord.symbolInfo, subscriptionRecord.resolution, {
51
- from: rangeStartTime,
52
- to: rangeEndTime,
53
- countBack: 2,
54
- firstDataRequest: false,
55
- })
56
- .then((result) => {
57
- this._onSubscriberDataReceived(listenerGuid, result);
58
- });
59
- }
60
- _onSubscriberDataReceived(listenerGuid, result) {
61
- // means the subscription was cancelled while waiting for data
62
- if (!this._subscribers.hasOwnProperty(listenerGuid)) {
63
- logMessage(`DataPulseProvider: Data comes for already unsubscribed subscription #${listenerGuid}`);
64
- return;
65
- }
66
- const bars = result.bars;
67
- if (bars.length === 0) {
68
- return;
69
- }
70
- const lastBar = bars[bars.length - 1];
71
- const subscriptionRecord = this._subscribers[listenerGuid];
72
- if (subscriptionRecord.lastBarTime !== null && lastBar.time < subscriptionRecord.lastBarTime) {
73
- return;
74
- }
75
- const isNewBar = subscriptionRecord.lastBarTime !== null && lastBar.time > subscriptionRecord.lastBarTime;
76
- // Pulse updating may miss some trades data (ie, if pulse period = 10 secods and new bar is started 5 seconds later after the last update, the
77
- // old bar's last 5 seconds trades will be lost). Thus, at fist we should broadcast old bar updates when it's ready.
78
- if (isNewBar) {
79
- if (bars.length < 2) {
80
- throw new Error('Not enough bars in history for proper pulse update. Need at least 2.');
81
- }
82
- const previousBar = bars[bars.length - 2];
83
- subscriptionRecord.listener(previousBar);
84
- }
85
- subscriptionRecord.lastBarTime = lastBar.time;
86
- subscriptionRecord.listener(lastBar);
87
- }
88
- }
89
- function periodLengthSeconds(resolution, requiredPeriodsCount) {
90
- let daysCount = 0;
91
- if (resolution === 'D' || resolution === '1D') {
92
- daysCount = requiredPeriodsCount;
93
- }
94
- else if (resolution === 'M' || resolution === '1M') {
95
- daysCount = 31 * requiredPeriodsCount;
96
- }
97
- else if (resolution === 'W' || resolution === '1W') {
98
- daysCount = 7 * requiredPeriodsCount;
99
- }
100
- else {
101
- daysCount = requiredPeriodsCount * parseInt(resolution) / (24 * 60);
102
- }
103
- return daysCount * 24 * 60 * 60;
104
- }
@@ -1,20 +0,0 @@
1
- /**
2
- * If you want to enable logs from datafeed set it to `true`
3
- */
4
- const isLoggingEnabled = false;
5
- export function logMessage(message) {
6
- if (isLoggingEnabled) {
7
- const now = new Date();
8
- // tslint:disable-next-line:no-console
9
- console.log(`${now.toLocaleTimeString()}.${now.getMilliseconds()}> ${message}`);
10
- }
11
- }
12
- export function getErrorMessage(error) {
13
- if (error === undefined) {
14
- return '';
15
- }
16
- else if (typeof error === 'string') {
17
- return error;
18
- }
19
- return error.message;
20
- }