@mathieuc/tradingview 3.4.3 → 3.4.5

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/.eslintrc.js CHANGED
@@ -23,5 +23,6 @@ module.exports = {
23
23
  'no-restricted-syntax': 'off',
24
24
  'no-await-in-loop': 'off',
25
25
  'no-continue': 'off',
26
+ 'guard-for-in': 'off',
26
27
  },
27
28
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mathieuc/tradingview",
3
- "version": "3.4.3",
3
+ "version": "3.4.5",
4
4
  "description": "Tradingview instant stocks API, indicator alerts, trading bot, and more !",
5
5
  "main": "main.js",
6
6
  "scripts": {
@@ -1,4 +1,5 @@
1
1
  const axios = require('axios');
2
+ const { genAuthCookies } = require('../utils');
2
3
 
3
4
  /**
4
5
  * @typedef {Object} AuthorizationUser
@@ -50,7 +51,7 @@ class PinePermManager {
50
51
  headers: {
51
52
  origin: 'https://www.tradingview.com',
52
53
  'Content-Type': 'application/x-www-form-urlencoded',
53
- cookie: `sessionid=${this.sessionId};sessionid_sign=${this.signature};`,
54
+ cookie: genAuthCookies(this.sessionId, this.signature),
54
55
  },
55
56
  },
56
57
  );
@@ -84,7 +85,7 @@ class PinePermManager {
84
85
  headers: {
85
86
  origin: 'https://www.tradingview.com',
86
87
  'Content-Type': 'application/x-www-form-urlencoded',
87
- cookie: `sessionid=${this.sessionId};sessionid_sign=${this.signature};`,
88
+ cookie: genAuthCookies(this.sessionId, this.signature),
88
89
  },
89
90
  },
90
91
  );
@@ -118,7 +119,7 @@ class PinePermManager {
118
119
  headers: {
119
120
  origin: 'https://www.tradingview.com',
120
121
  'Content-Type': 'application/x-www-form-urlencoded',
121
- cookie: `sessionid=${this.sessionId};sessionid_sign=${this.signature};`,
122
+ cookie: genAuthCookies(this.sessionId, this.signature),
122
123
  },
123
124
  },
124
125
  );
@@ -143,7 +144,7 @@ class PinePermManager {
143
144
  headers: {
144
145
  origin: 'https://www.tradingview.com',
145
146
  'Content-Type': 'application/x-www-form-urlencoded',
146
- cookie: `sessionid=${this.sessionId};sessionid_sign=${this.signature};`,
147
+ cookie: genAuthCookies(this.sessionId, this.signature),
147
148
  },
148
149
  },
149
150
  );
@@ -2,17 +2,22 @@ const os = require('os');
2
2
  const axios = require('axios');
3
3
 
4
4
  const PineIndicator = require('./classes/PineIndicator');
5
+ const { genAuthCookies } = require('./utils');
5
6
 
6
7
  const validateStatus = (status) => status < 500;
7
8
 
8
9
  const indicators = ['Recommend.Other', 'Recommend.All', 'Recommend.MA'];
9
10
  const builtInIndicList = [];
10
11
 
11
- async function fetchScanData(tickers = [], type = '', columns = []) {
12
- const { data } = await axios.post(`https://scanner.tradingview.com/${type}/scan`, {
13
- symbols: { tickers },
14
- columns,
15
- }, { validateStatus });
12
+ async function fetchScanData(tickers = [], columns = []) {
13
+ const { data } = await axios.post(
14
+ 'https://scanner.tradingview.com/global/scan',
15
+ {
16
+ symbols: { tickers },
17
+ columns,
18
+ },
19
+ { validateStatus },
20
+ );
16
21
 
17
22
  return data;
18
23
  }
@@ -40,56 +45,21 @@ async function fetchScanData(tickers = [], type = '', columns = []) {
40
45
  * }} Periods
41
46
  */
42
47
 
43
- // /**
44
- // * @typedef {string | 'forex' | 'crypto'
45
- // * | 'america' | 'australia' | 'canada' | 'egypt'
46
- // * | 'germany' | 'india' | 'israel' | 'italy'
47
- // * | 'luxembourg' | 'poland' | 'sweden' | 'turkey'
48
- // * | 'uk' | 'vietnam'} Screener
49
- // * You can use `getScreener(exchange)` function for non-forex and non-crypto markets.
50
- // */
51
-
52
48
  module.exports = {
53
- // /**
54
- // * Get a screener from an exchange
55
- // * @function getScreener
56
- // * @param {string} exchange Example: BINANCE, EURONEXT, NASDAQ
57
- // * @returns {Screener}
58
- // */
59
- // getScreener(exchange) {
60
- // const e = exchange.toUpperCase();
61
- // if (['NASDAQ', 'NYSE', 'NYSE ARCA', 'OTC'].includes(e)) return 'america';
62
- // if (['ASX'].includes(e)) return 'australia';
63
- // if (['TSX', 'TSXV', 'CSE', 'NEO'].includes(e)) return 'canada';
64
- // if (['EGX'].includes(e)) return 'egypt';
65
- // if (['FWB', 'SWB', 'XETR'].includes(e)) return 'germany';
66
- // if (['BSE', 'NSE'].includes(e)) return 'india';
67
- // if (['TASE'].includes(e)) return 'israel';
68
- // if (['MIL', 'MILSEDEX'].includes(e)) return 'italy';
69
- // if (['LUXSE'].includes(e)) return 'luxembourg';
70
- // if (['NEWCONNECT'].includes(e)) return 'poland';
71
- // if (['NGM'].includes(e)) return 'sweden';
72
- // if (['BIST'].includes(e)) return 'turkey';
73
- // if (['LSE', 'LSIN'].includes(e)) return 'uk';
74
- // if (['HNX'].includes(e)) return 'vietnam';
75
- // return 'global';
76
- // },
77
-
78
49
  /**
79
50
  * Get technical analysis
80
51
  * @function getTA
81
- * @param {Screener} screener Market screener
82
52
  * @param {string} id Full market id (Example: COINBASE:BTCEUR)
83
53
  * @returns {Promise<Periods>} results
84
54
  */
85
- async getTA(screener, id) {
55
+ async getTA(id) {
86
56
  const advice = {};
87
57
 
88
58
  const cols = ['1', '5', '15', '60', '240', '1D', '1W', '1M']
89
59
  .map((t) => indicators.map((i) => (t !== '1D' ? `${i}|${t}` : i)))
90
60
  .flat();
91
61
 
92
- const rs = await fetchScanData([id], screener, cols);
62
+ const rs = await fetchScanData([id], cols);
93
63
  if (!rs.data || !rs.data[0]) return false;
94
64
 
95
65
  rs.data[0].d.forEach((val, i) => {
@@ -107,7 +77,6 @@ module.exports = {
107
77
  * @prop {string} id Market full symbol
108
78
  * @prop {string} exchange Market exchange name
109
79
  * @prop {string} fullExchange Market exchange full name
110
- * @prop {Screener | 'forex' | 'crypto'} screener Market screener
111
80
  * @prop {string} symbol Market symbol
112
81
  * @prop {string} description Market name
113
82
  * @prop {string} type Market type
@@ -115,7 +84,7 @@ module.exports = {
115
84
  */
116
85
 
117
86
  /**
118
- * Find a symbol
87
+ * Find a symbol (deprecated)
119
88
  * @function searchMarket
120
89
  * @param {string} search Keywords
121
90
  * @param {'stock'
@@ -123,15 +92,20 @@ module.exports = {
123
92
  * | 'crypto' | 'index' | 'economic'
124
93
  * } [filter] Caterogy filter
125
94
  * @returns {Promise<SearchMarketResult[]>} Search results
95
+ * @deprecated Use searchMarketV3 instead
126
96
  */
127
97
  async searchMarket(search, filter = '') {
128
98
  const { data } = await axios.get(
129
- `https://symbol-search.tradingview.com/symbol_search/?text=${search.replace(/ /g, '%20')}&type=${filter}`,
99
+ 'https://symbol-search.tradingview.com/symbol_search',
130
100
  {
131
- validateStatus,
101
+ params: {
102
+ text: search.replace(/ /g, '%20'),
103
+ type: filter,
104
+ },
132
105
  headers: {
133
106
  origin: 'https://www.tradingview.com',
134
107
  },
108
+ validateStatus,
135
109
  },
136
110
  );
137
111
 
@@ -139,21 +113,63 @@ module.exports = {
139
113
  const exchange = s.exchange.split(' ')[0];
140
114
  const id = `${exchange}:${s.symbol}`;
141
115
 
142
- // const screener = (['forex', 'crypto'].includes(s.type)
143
- // ? s.type
144
- // : this.getScreener(exchange)
145
- // );
146
- const screener = 'global';
116
+ return {
117
+ id,
118
+ exchange,
119
+ fullExchange: s.exchange,
120
+ symbol: s.symbol,
121
+ description: s.description,
122
+ type: s.type,
123
+ getTA: () => this.getTA(id),
124
+ };
125
+ });
126
+ },
127
+
128
+ /**
129
+ * Find a symbol
130
+ * @function searchMarketV3
131
+ * @param {string} search Keywords
132
+ * @param {'stock'
133
+ * | 'futures' | 'forex' | 'cfd'
134
+ * | 'crypto' | 'index' | 'economic'
135
+ * } [filter] Caterogy filter
136
+ * @returns {Promise<SearchMarketResult[]>} Search results
137
+ */
138
+ async searchMarketV3(search, filter = '') {
139
+ const splittedSearch = search.toUpperCase().replace(/ /g, '+').split(':');
140
+
141
+ const request = await axios.get(
142
+ 'https://symbol-search.tradingview.com/symbol_search/v3',
143
+ {
144
+ params: {
145
+ exchange: (splittedSearch.length === 2
146
+ ? splittedSearch[0]
147
+ : undefined
148
+ ),
149
+ text: splittedSearch.pop(),
150
+ search_type: filter,
151
+ },
152
+ headers: {
153
+ origin: 'https://www.tradingview.com',
154
+ },
155
+ validateStatus,
156
+ },
157
+ );
158
+
159
+ const { data } = request;
160
+
161
+ return data.symbols.map((s) => {
162
+ const exchange = s.exchange.split(' ')[0];
163
+ const id = `${exchange.toUpperCase()}:${s.symbol}`;
147
164
 
148
165
  return {
149
166
  id,
150
167
  exchange,
151
168
  fullExchange: s.exchange,
152
- screener,
153
169
  symbol: s.symbol,
154
170
  description: s.description,
155
171
  type: s.type,
156
- getTA: () => this.getTA(screener, id),
172
+ getTA: () => this.getTA(id),
157
173
  };
158
174
  });
159
175
  },
@@ -182,16 +198,26 @@ module.exports = {
182
198
  if (!builtInIndicList.length) {
183
199
  await Promise.all(['standard', 'candlestick', 'fundamental'].map(async (type) => {
184
200
  const { data } = await axios.get(
185
- `https://pine-facade.tradingview.com/pine-facade/list/?filter=${type}`,
186
- { validateStatus },
201
+ 'https://pine-facade.tradingview.com/pine-facade/list',
202
+ {
203
+ params: {
204
+ filter: type,
205
+ },
206
+ validateStatus,
207
+ },
187
208
  );
188
209
  builtInIndicList.push(...data);
189
210
  }));
190
211
  }
191
212
 
192
213
  const { data } = await axios.get(
193
- `https://www.tradingview.com/pubscripts-suggest-json/?search=${search.replace(/ /g, '%20')}`,
194
- { validateStatus },
214
+ 'https://www.tradingview.com/pubscripts-suggest-json',
215
+ {
216
+ params: {
217
+ search: search.replace(/ /g, '%20'),
218
+ },
219
+ validateStatus,
220
+ },
195
221
  );
196
222
 
197
223
  function norm(str = '') {
@@ -243,14 +269,21 @@ module.exports = {
243
269
  * @function getIndicator
244
270
  * @param {string} id Indicator ID (Like: PUB;XXXXXXXXXXXXXXXXXXXXX)
245
271
  * @param {'last' | string} [version] Wanted version of the indicator
272
+ * @param {string} [session] User 'sessionid' cookie
273
+ * @param {string} [signature] User 'sessionid_sign' cookie
246
274
  * @returns {Promise<PineIndicator>} Indicator
247
275
  */
248
- async getIndicator(id, version = 'last') {
276
+ async getIndicator(id, version = 'last', session = '', signature = '') {
249
277
  const indicID = id.replace(/ |%/g, '%25');
250
278
 
251
279
  const { data } = await axios.get(
252
280
  `https://pine-facade.tradingview.com/pine-facade/translate/${indicID}/${version}`,
253
- { validateStatus },
281
+ {
282
+ headers: {
283
+ cookie: genAuthCookies(session, signature),
284
+ },
285
+ validateStatus,
286
+ },
254
287
  );
255
288
 
256
289
  if (!data.success || !data.result.metaInfo || !data.result.metaInfo.inputs) {
@@ -349,12 +382,12 @@ module.exports = {
349
382
  'https://www.tradingview.com/accounts/signin/',
350
383
  `username=${username}&password=${password}${remember ? '&remember=on' : ''}`,
351
384
  {
352
- validateStatus,
353
385
  headers: {
354
386
  referer: 'https://www.tradingview.com',
355
387
  'Content-Type': 'application/x-www-form-urlencoded',
356
388
  'User-agent': `${UA} (${os.version()}; ${os.platform()}; ${os.arch()})`,
357
389
  },
390
+ validateStatus,
358
391
  },
359
392
  );
360
393
 
@@ -396,10 +429,10 @@ module.exports = {
396
429
  */
397
430
  async getUser(session, signature = '', location = 'https://www.tradingview.com/') {
398
431
  const { data } = await axios.get(location, {
399
- validateStatus,
400
432
  headers: {
401
- cookie: `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}`,
433
+ cookie: genAuthCookies(session, signature),
402
434
  },
435
+ validateStatus,
403
436
  });
404
437
 
405
438
  if (data.includes('auth_token')) {
@@ -435,12 +468,18 @@ module.exports = {
435
468
  * @returns {Promise<SearchIndicatorResult[]>} Search results
436
469
  */
437
470
  async getPrivateIndicators(session, signature = '') {
438
- const { data } = await axios.get('https://pine-facade.tradingview.com/pine-facade/list?filter=saved', {
439
- validateStatus,
440
- headers: {
441
- cookie: `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}`,
471
+ const { data } = await axios.get(
472
+ 'https://pine-facade.tradingview.com/pine-facade/list',
473
+ {
474
+ headers: {
475
+ cookie: genAuthCookies(session, signature),
476
+ },
477
+ params: {
478
+ filter: 'saved',
479
+ },
480
+ validateStatus,
442
481
  },
443
- });
482
+ );
444
483
 
445
484
  return data.map((ind) => ({
446
485
  id: ind.scriptIdPart,
@@ -455,7 +494,12 @@ module.exports = {
455
494
  source: ind.scriptSource,
456
495
  type: (ind.extra && ind.extra.kind) ? ind.extra.kind : 'study',
457
496
  get() {
458
- return module.exports.getIndicator(ind.scriptIdPart, ind.version);
497
+ return module.exports.getIndicator(
498
+ ind.scriptIdPart,
499
+ ind.version,
500
+ session,
501
+ signature,
502
+ );
459
503
  },
460
504
  }));
461
505
  },
@@ -483,14 +527,16 @@ module.exports = {
483
527
  );
484
528
 
485
529
  const { data } = await axios.get(
486
- `https://www.tradingview.com/chart-token/?image_url=${layout}&user_id=${id}`,
530
+ 'https://www.tradingview.com/chart-token',
487
531
  {
488
- validateStatus,
489
532
  headers: {
490
- cookie: session
491
- ? `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}`
492
- : '',
533
+ cookie: genAuthCookies(session, signature),
493
534
  },
535
+ params: {
536
+ image_url: layout,
537
+ user_id: id,
538
+ },
539
+ validateStatus,
494
540
  },
495
541
  );
496
542
 
@@ -536,14 +582,15 @@ module.exports = {
536
582
  const { data } = await axios.get(
537
583
  `https://charts-storage.tradingview.com/charts-storage/get/layout/${
538
584
  layout
539
- }/sources?chart_id=${
540
- chartID
541
- }&jwt=${
542
- chartToken
543
- }${
544
- (symbol ? `&symbol=${symbol}` : '')
545
- }`,
546
- { validateStatus },
585
+ }/sources`,
586
+ {
587
+ params: {
588
+ chart_id: chartID,
589
+ jwt: chartToken,
590
+ symbol,
591
+ },
592
+ validateStatus,
593
+ },
547
594
  );
548
595
 
549
596
  if (!data.payload) throw new Error('Wrong layout, user credentials, or chart id.');
package/src/utils.js CHANGED
@@ -11,4 +11,10 @@ module.exports = {
11
11
  for (let i = 0; i < 12; i += 1) r += c.charAt(Math.floor(Math.random() * c.length));
12
12
  return `${type}_${r}`;
13
13
  },
14
+
15
+ genAuthCookies(sessionId = '', signature = '') {
16
+ if (!sessionId) return '';
17
+ if (!signature) return `sessionid=${sessionId}`;
18
+ return `sessionid=${sessionId};sessionid_sign=${signature}`;
19
+ },
14
20
  };