binbot-charts 0.0.20 → 0.0.21

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 (31) hide show
  1. package/README.md +2 -0
  2. package/dist/components/TVChartContainer.js +45 -24
  3. package/dist/components/datafeed.js +2 -13
  4. package/dist/{index.js → components/index.js} +1 -1
  5. package/dist/datafeeds/README.md +3 -0
  6. package/dist/datafeeds/udf/README.md +46 -0
  7. package/dist/datafeeds/udf/dist/bundle.js +1 -0
  8. package/dist/datafeeds/udf/lib/data-pulse-provider.js +104 -0
  9. package/dist/datafeeds/udf/lib/helpers.js +20 -0
  10. package/dist/datafeeds/udf/lib/history-provider.js +73 -0
  11. package/dist/datafeeds/udf/lib/iquotes-provider.js +1 -0
  12. package/dist/datafeeds/udf/lib/quotes-provider.js +25 -0
  13. package/dist/datafeeds/udf/lib/quotes-pulse-provider.js +44 -0
  14. package/dist/datafeeds/udf/lib/requester.js +28 -0
  15. package/dist/datafeeds/udf/lib/symbols-storage.js +180 -0
  16. package/dist/datafeeds/udf/lib/udf-compatible-datafeed-base.js +252 -0
  17. package/dist/datafeeds/udf/lib/udf-compatible-datafeed.js +10 -0
  18. package/dist/datafeeds/udf/package.json +17 -0
  19. package/dist/datafeeds/udf/rollup.config.js +25 -0
  20. package/dist/datafeeds/udf/src/data-pulse-provider.ts +152 -0
  21. package/dist/datafeeds/udf/src/helpers.ts +38 -0
  22. package/dist/datafeeds/udf/src/history-provider.ts +134 -0
  23. package/dist/datafeeds/udf/src/iquotes-provider.ts +14 -0
  24. package/dist/datafeeds/udf/src/quotes-provider.ts +37 -0
  25. package/dist/datafeeds/udf/src/quotes-pulse-provider.ts +85 -0
  26. package/dist/datafeeds/udf/src/requester.ts +39 -0
  27. package/dist/datafeeds/udf/src/symbols-storage.ts +298 -0
  28. package/dist/datafeeds/udf/src/udf-compatible-datafeed-base.ts +369 -0
  29. package/dist/datafeeds/udf/src/udf-compatible-datafeed.ts +11 -0
  30. package/dist/datafeeds/udf/tsconfig.json +25 -0
  31. package/package.json +9 -6
package/README.md CHANGED
@@ -7,6 +7,8 @@ Import it in your project as a React component
7
7
  ## How to start
8
8
 
9
9
  1. Run `yarn install && yarn start`. It will build the project and open a default browser with the Charting Library.
10
+ 2. `library_path` should be `node_modules/dist/charting_library`
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`
10
12
 
11
13
  ## About This Project
12
14
 
@@ -7,53 +7,60 @@ Object.defineProperty(exports, "__esModule", {
7
7
  });
8
8
  exports.default = void 0;
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
- var _datafeed = _interopRequireDefault(require("./datafeed"));
15
-
16
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17
-
18
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); }
19
15
 
20
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; }
21
17
 
22
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; }
23
19
 
24
- class TVChartContainer extends _react.Component {
20
+ class TVChartContainer extends React.PureComponent {
25
21
  constructor(props) {
26
22
  super(props);
27
23
 
28
24
  _defineProperty(this, "tvWidget", null);
29
25
 
30
- this.ref = /*#__PURE__*/_react.default.createRef();
26
+ this.ref = /*#__PURE__*/React.createRef();
31
27
  }
32
28
 
33
29
  componentDidMount() {
34
30
  const widgetOptions = {
35
- symbol: "AAPL",
31
+ symbol: this.props.symbol,
36
32
  // BEWARE: no trailing slash is expected in feed URL
37
- datafeed: _datafeed.default,
38
- // interval: this.props.interval,
33
+ datafeed: new window.Datafeeds.UDFCompatibleDatafeed(this.props.datafeedUrl),
34
+ interval: this.props.interval,
39
35
  container: this.ref.current,
40
36
  library_path: this.props.libraryPath,
41
- locale: "en",
42
- // disabled_features: ["use_localstorage_for_settings"],
43
- // enabled_features: ["study_templates"],
44
- // charts_storage_url: this.props.chartsStorageUrl,
45
- // charts_storage_api_version: this.props.chartsStorageApiVersion,
46
- // client_id: this.props.clientId,
47
- // user_id: this.props.userId,
48
- fullscreen: false,
49
- autosize: false,
50
- studies_overrides: {}
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
51
47
  };
52
48
  const tvWidget = new _charting_library.widget(widgetOptions);
53
49
  this.tvWidget = tvWidget;
54
50
  tvWidget.onChartReady(() => {
55
- const takeProfit = tvWidget.chart().createOrderLine().setText("Take profit").setLineLength(3).setLineStyle(1).setLineColor("green").setBodyBorderColor("green").setBodyTextColor("green").setQuantityBackgroundColor("green").setQuantityBorderColor("green").setQuantityTextColor("green").setQuantity("225 USDT");
56
- takeProfit.setPrice(182);
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';
63
+ });
57
64
  });
58
65
  }
59
66
 
@@ -65,7 +72,7 @@ class TVChartContainer extends _react.Component {
65
72
  }
66
73
 
67
74
  render() {
68
- return /*#__PURE__*/_react.default.createElement("div", {
75
+ return /*#__PURE__*/React.createElement("div", {
69
76
  ref: this.ref,
70
77
  style: {
71
78
  height: "calc(100vh - 80px)"
@@ -75,4 +82,18 @@ class TVChartContainer extends _react.Component {
75
82
 
76
83
  }
77
84
 
78
- exports.default = TVChartContainer;
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
+ });
@@ -32,10 +32,8 @@ const configurationData = {
32
32
  }],
33
33
  symbols_types: [{
34
34
  name: "crypto",
35
- // `symbolType` argument for the `searchSymbols` method, if a user selects this symbol type
36
35
  value: "crypto"
37
- } // ...
38
- ]
36
+ }]
39
37
  };
40
38
 
41
39
  async function getAllSymbols() {
@@ -65,6 +63,7 @@ class Datafeed {}
65
63
  exports.default = Datafeed;
66
64
 
67
65
  _defineProperty(Datafeed, "onReady", callback => {
66
+ debugger;
68
67
  console.log("[onReady]: Method call");
69
68
  setTimeout(() => callback(configurationData));
70
69
  });
@@ -170,14 +169,4 @@ _defineProperty(Datafeed, "getBars", async (symbolInfo, resolution, periodParams
170
169
  console.log("[getBars]: Get error", error);
171
170
  onErrorCallback(error);
172
171
  }
173
- });
174
-
175
- _defineProperty(Datafeed, "subscribeBars", (symbolInfo, resolution, onRealtimeCallback, subscribeUID, onResetCacheNeededCallback) => {
176
- console.log("[subscribeBars]: Method call with subscribeUID:", subscribeUID);
177
- (0, _streaming.subscribeOnStream)(symbolInfo, resolution, onRealtimeCallback, subscribeUID, onResetCacheNeededCallback, lastBarsCache.get(symbolInfo.full_name));
178
- });
179
-
180
- _defineProperty(Datafeed, "unsubscribeBars", subscriberUID => {
181
- console.log("[unsubscribeBars]: Method call with subscriberUID:", subscriberUID);
182
- (0, _streaming.unsubscribeFromStream)(subscriberUID);
183
172
  });
@@ -10,6 +10,6 @@ Object.defineProperty(exports, "TVChartContainer", {
10
10
  }
11
11
  });
12
12
 
13
- var _TVChartContainer = _interopRequireDefault(require("./components/TVChartContainer"));
13
+ var _TVChartContainer = _interopRequireDefault(require("./TVChartContainer"));
14
14
 
15
15
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@@ -0,0 +1,3 @@
1
+ # Charting Library Datafeeds
2
+
3
+ This folder contains implementation of Charting Library Datafeeds.
@@ -0,0 +1,46 @@
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
+ ```
@@ -0,0 +1 @@
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})}));
@@ -0,0 +1,104 @@
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
+ }
@@ -0,0 +1,20 @@
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
+ }
@@ -0,0 +1,73 @@
1
+ import { getErrorMessage, } from './helpers';
2
+ export class HistoryProvider {
3
+ constructor(datafeedUrl, requester) {
4
+ this._datafeedUrl = datafeedUrl;
5
+ this._requester = requester;
6
+ }
7
+ getBars(symbolInfo, resolution, periodParams) {
8
+ const requestParams = {
9
+ symbol: symbolInfo.ticker || '',
10
+ resolution: resolution,
11
+ from: periodParams.from,
12
+ to: periodParams.to,
13
+ };
14
+ if (periodParams.countBack !== undefined) {
15
+ requestParams.countback = periodParams.countBack;
16
+ }
17
+ if (symbolInfo.currency_code !== undefined) {
18
+ requestParams.currencyCode = symbolInfo.currency_code;
19
+ }
20
+ if (symbolInfo.unit_id !== undefined) {
21
+ requestParams.unitId = symbolInfo.unit_id;
22
+ }
23
+ return new Promise((resolve, reject) => {
24
+ this._requester.sendRequest(this._datafeedUrl, 'history', requestParams)
25
+ .then((response) => {
26
+ if (response.s !== 'ok' && response.s !== 'no_data') {
27
+ reject(response.errmsg);
28
+ return;
29
+ }
30
+ const bars = [];
31
+ const meta = {
32
+ noData: false,
33
+ };
34
+ if (response.s === 'no_data') {
35
+ meta.noData = true;
36
+ meta.nextTime = response.nextTime;
37
+ }
38
+ else {
39
+ const volumePresent = response.v !== undefined;
40
+ const ohlPresent = response.o !== undefined;
41
+ for (let i = 0; i < response.t.length; ++i) {
42
+ const barValue = {
43
+ time: response.t[i] * 1000,
44
+ close: parseFloat(response.c[i]),
45
+ open: parseFloat(response.c[i]),
46
+ high: parseFloat(response.c[i]),
47
+ low: parseFloat(response.c[i]),
48
+ };
49
+ if (ohlPresent) {
50
+ barValue.open = parseFloat(response.o[i]);
51
+ barValue.high = parseFloat(response.h[i]);
52
+ barValue.low = parseFloat(response.l[i]);
53
+ }
54
+ if (volumePresent) {
55
+ barValue.volume = parseFloat(response.v[i]);
56
+ }
57
+ bars.push(barValue);
58
+ }
59
+ }
60
+ resolve({
61
+ bars: bars,
62
+ meta: meta,
63
+ });
64
+ })
65
+ .catch((reason) => {
66
+ const reasonString = getErrorMessage(reason);
67
+ // tslint:disable-next-line:no-console
68
+ console.warn(`HistoryProvider: getBars() failed, error=${reasonString}`);
69
+ reject(reasonString);
70
+ });
71
+ });
72
+ }
73
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,25 @@
1
+ import { getErrorMessage, logMessage, } from './helpers';
2
+ export class QuotesProvider {
3
+ constructor(datafeedUrl, requester) {
4
+ this._datafeedUrl = datafeedUrl;
5
+ this._requester = requester;
6
+ }
7
+ getQuotes(symbols) {
8
+ return new Promise((resolve, reject) => {
9
+ this._requester.sendRequest(this._datafeedUrl, 'quotes', { symbols: symbols })
10
+ .then((response) => {
11
+ if (response.s === 'ok') {
12
+ resolve(response.d);
13
+ }
14
+ else {
15
+ reject(response.errmsg);
16
+ }
17
+ })
18
+ .catch((error) => {
19
+ const errorMessage = getErrorMessage(error);
20
+ logMessage(`QuotesProvider: getQuotes failed, error=${errorMessage}`);
21
+ reject(`network error: ${errorMessage}`);
22
+ });
23
+ });
24
+ }
25
+ }
@@ -0,0 +1,44 @@
1
+ import { getErrorMessage, logMessage, } from './helpers';
2
+ export class QuotesPulseProvider {
3
+ constructor(quotesProvider) {
4
+ this._subscribers = {};
5
+ this._requestsPending = 0;
6
+ this._quotesProvider = quotesProvider;
7
+ setInterval(this._updateQuotes.bind(this, 1 /* Fast */), 10000 /* Fast */);
8
+ setInterval(this._updateQuotes.bind(this, 0 /* General */), 60000 /* General */);
9
+ }
10
+ subscribeQuotes(symbols, fastSymbols, onRealtimeCallback, listenerGuid) {
11
+ this._subscribers[listenerGuid] = {
12
+ symbols: symbols,
13
+ fastSymbols: fastSymbols,
14
+ listener: onRealtimeCallback,
15
+ };
16
+ logMessage(`QuotesPulseProvider: subscribed quotes with #${listenerGuid}`);
17
+ }
18
+ unsubscribeQuotes(listenerGuid) {
19
+ delete this._subscribers[listenerGuid];
20
+ logMessage(`QuotesPulseProvider: unsubscribed quotes with #${listenerGuid}`);
21
+ }
22
+ _updateQuotes(updateType) {
23
+ if (this._requestsPending > 0) {
24
+ return;
25
+ }
26
+ for (const listenerGuid in this._subscribers) { // tslint:disable-line:forin
27
+ this._requestsPending++;
28
+ const subscriptionRecord = this._subscribers[listenerGuid];
29
+ this._quotesProvider.getQuotes(updateType === 1 /* Fast */ ? subscriptionRecord.fastSymbols : subscriptionRecord.symbols)
30
+ .then((data) => {
31
+ this._requestsPending--;
32
+ if (!this._subscribers.hasOwnProperty(listenerGuid)) {
33
+ return;
34
+ }
35
+ subscriptionRecord.listener(data);
36
+ logMessage(`QuotesPulseProvider: data for #${listenerGuid} (${updateType}) updated successfully, pending=${this._requestsPending}`);
37
+ })
38
+ .catch((reason) => {
39
+ this._requestsPending--;
40
+ logMessage(`QuotesPulseProvider: data for #${listenerGuid} (${updateType}) updated with error=${getErrorMessage(reason)}, pending=${this._requestsPending}`);
41
+ });
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,28 @@
1
+ import { logMessage } from './helpers';
2
+ export class Requester {
3
+ constructor(headers) {
4
+ if (headers) {
5
+ this._headers = headers;
6
+ }
7
+ }
8
+ sendRequest(datafeedUrl, urlPath, params) {
9
+ if (params !== undefined) {
10
+ const paramKeys = Object.keys(params);
11
+ if (paramKeys.length !== 0) {
12
+ urlPath += '?';
13
+ }
14
+ urlPath += paramKeys.map((key) => {
15
+ return `${encodeURIComponent(key)}=${encodeURIComponent(params[key].toString())}`;
16
+ }).join('&');
17
+ }
18
+ logMessage('New request: ' + urlPath);
19
+ // Send user cookies if the URL is on the same origin as the calling script.
20
+ const options = { credentials: 'same-origin' };
21
+ if (this._headers !== undefined) {
22
+ options.headers = this._headers;
23
+ }
24
+ return fetch(`${datafeedUrl}/${urlPath}`, options)
25
+ .then((response) => response.text())
26
+ .then((responseTest) => JSON.parse(responseTest));
27
+ }
28
+ }