@mathieuc/tradingview 3.4.4 → 3.4.6

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
  };
@@ -4,9 +4,15 @@ const TradingView = require('../main');
4
4
  * This example tests built-in indicators like volume-based indicators
5
5
  */
6
6
 
7
- const volumeProfile = new TradingView.BuiltInIndicator('VbPFixed@tv-basicstudies-139!');
7
+ const volumeProfile = new TradingView.BuiltInIndicator('VbPFixed@tv-basicstudies-241!');
8
8
 
9
- if (!process.argv[2] && !['VbPFixed@tv-basicstudies-139!', 'Volume@tv-basicstudies-144'].includes(volumeProfile.type)) {
9
+ const AUTHENTICATED_INDICATORS = [
10
+ 'VbPFixed@tv-basicstudies-241',
11
+ 'VbPFixed@tv-basicstudies-241!',
12
+ 'Volume@tv-basicstudies-241',
13
+ ];
14
+
15
+ if (!process.argv[2] && !AUTHENTICATED_INDICATORS.includes(volumeProfile.type)) {
10
16
  throw Error('Please specify your \'sessionid\' cookie');
11
17
  }
12
18
 
@@ -91,6 +91,6 @@ async function addIndicator(name, pineId, options = {}) {
91
91
  });
92
92
  }
93
93
 
94
- addIndicator('Volume', 'Volume@tv-basicstudies-144');
94
+ addIndicator('Volume', 'Volume@tv-basicstudies-241');
95
95
  addIndicator('EMA_50', 'STD;EMA', { Length: 50 });
96
96
  addIndicator('EMA_200', 'STD;EMA', { Length: 200 });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mathieuc/tradingview",
3
- "version": "3.4.4",
3
+ "version": "3.4.6",
4
4
  "description": "Tradingview instant stocks API, indicator alerts, trading bot, and more !",
5
5
  "main": "main.js",
6
6
  "scripts": {
@@ -1,6 +1,7 @@
1
1
  /**
2
- * @typedef {'Volume@tv-basicstudies-144'
3
- * | 'VbPFixed@tv-basicstudies-139!'
2
+ * @typedef {'Volume@tv-basicstudies-241'
3
+ * | 'VbPFixed@tv-basicstudies-241'
4
+ * | 'VbPFixed@tv-basicstudies-241!'
4
5
  * | 'VbPFixed@tv-volumebyprice-53!'
5
6
  * | 'VbPSessions@tv-volumebyprice-53'
6
7
  * | 'VbPSessionsRough@tv-volumebyprice-53!'
@@ -17,11 +18,22 @@
17
18
  */
18
19
 
19
20
  const defaultValues = {
20
- 'Volume@tv-basicstudies-144': {
21
+ 'Volume@tv-basicstudies-241': {
21
22
  length: 20,
22
23
  col_prev_close: false,
23
24
  },
24
- 'VbPFixed@tv-basicstudies-139!': {
25
+ 'VbPFixed@tv-basicstudies-241': {
26
+ rowsLayout: 'Number Of Rows',
27
+ rows: 24,
28
+ volume: 'Up/Down',
29
+ vaVolume: 70,
30
+ subscribeRealtime: false,
31
+ first_bar_time: NaN,
32
+ last_bar_time: Date.now(),
33
+ extendToRight: false,
34
+ mapRightBoundaryToBarStartTime: true,
35
+ },
36
+ 'VbPFixed@tv-basicstudies-241!': {
25
37
  rowsLayout: 'Number Of Rows',
26
38
  rows: 24,
27
39
  volume: 'Up/Down',
@@ -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 = '') {
@@ -253,10 +279,10 @@ module.exports = {
253
279
  const { data } = await axios.get(
254
280
  `https://pine-facade.tradingview.com/pine-facade/translate/${indicID}/${version}`,
255
281
  {
256
- validateStatus,
257
282
  headers: {
258
- cookie: `${session ? `sessionid=${session};` : ''}${signature ? `sessionid_sign=${signature};` : ''}`,
283
+ cookie: genAuthCookies(session, signature),
259
284
  },
285
+ validateStatus,
260
286
  },
261
287
  );
262
288
 
@@ -356,12 +382,12 @@ module.exports = {
356
382
  'https://www.tradingview.com/accounts/signin/',
357
383
  `username=${username}&password=${password}${remember ? '&remember=on' : ''}`,
358
384
  {
359
- validateStatus,
360
385
  headers: {
361
386
  referer: 'https://www.tradingview.com',
362
387
  'Content-Type': 'application/x-www-form-urlencoded',
363
388
  'User-agent': `${UA} (${os.version()}; ${os.platform()}; ${os.arch()})`,
364
389
  },
390
+ validateStatus,
365
391
  },
366
392
  );
367
393
 
@@ -403,10 +429,10 @@ module.exports = {
403
429
  */
404
430
  async getUser(session, signature = '', location = 'https://www.tradingview.com/') {
405
431
  const { data } = await axios.get(location, {
406
- validateStatus,
407
432
  headers: {
408
- cookie: `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}`,
433
+ cookie: genAuthCookies(session, signature),
409
434
  },
435
+ validateStatus,
410
436
  });
411
437
 
412
438
  if (data.includes('auth_token')) {
@@ -442,12 +468,18 @@ module.exports = {
442
468
  * @returns {Promise<SearchIndicatorResult[]>} Search results
443
469
  */
444
470
  async getPrivateIndicators(session, signature = '') {
445
- const { data } = await axios.get('https://pine-facade.tradingview.com/pine-facade/list?filter=saved', {
446
- validateStatus,
447
- headers: {
448
- 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,
449
481
  },
450
- });
482
+ );
451
483
 
452
484
  return data.map((ind) => ({
453
485
  id: ind.scriptIdPart,
@@ -495,14 +527,16 @@ module.exports = {
495
527
  );
496
528
 
497
529
  const { data } = await axios.get(
498
- `https://www.tradingview.com/chart-token/?image_url=${layout}&user_id=${id}`,
530
+ 'https://www.tradingview.com/chart-token',
499
531
  {
500
- validateStatus,
501
532
  headers: {
502
- cookie: session
503
- ? `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}`
504
- : '',
533
+ cookie: genAuthCookies(session, signature),
505
534
  },
535
+ params: {
536
+ image_url: layout,
537
+ user_id: id,
538
+ },
539
+ validateStatus,
506
540
  },
507
541
  );
508
542
 
@@ -548,14 +582,15 @@ module.exports = {
548
582
  const { data } = await axios.get(
549
583
  `https://charts-storage.tradingview.com/charts-storage/get/layout/${
550
584
  layout
551
- }/sources?chart_id=${
552
- chartID
553
- }&jwt=${
554
- chartToken
555
- }${
556
- (symbol ? `&symbol=${symbol}` : '')
557
- }`,
558
- { validateStatus },
585
+ }/sources`,
586
+ {
587
+ params: {
588
+ chart_id: chartID,
589
+ jwt: chartToken,
590
+ symbol,
591
+ },
592
+ validateStatus,
593
+ },
559
594
  );
560
595
 
561
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
  };