@mathieuc/tradingview 3.4.1 → 3.4.2

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.
@@ -1,31 +1,18 @@
1
1
  const os = require('os');
2
- const https = require('https');
3
- const request = require('./request');
4
- const FormData = require('./FormData');
2
+ const axios = require('axios');
5
3
 
6
- const PinePermManager = require('./classes/PinePermManager');
7
4
  const PineIndicator = require('./classes/PineIndicator');
8
5
 
6
+ const validateStatus = (status) => status < 500;
7
+
9
8
  const indicators = ['Recommend.Other', 'Recommend.All', 'Recommend.MA'];
10
9
  const builtInIndicList = [];
11
10
 
12
11
  async function fetchScanData(tickers = [], type = '', columns = []) {
13
- let { data } = await request({
14
- method: 'POST',
15
- hostname: 'scanner.tradingview.com',
16
- path: `/${type}/scan`,
17
- headers: {
18
- 'Content-Type': 'application/json',
19
- },
20
- }, true, JSON.stringify({ symbols: { tickers }, columns }));
21
-
22
- if (!data.startsWith('{')) throw new Error('Wrong screener or symbol');
23
-
24
- try {
25
- data = JSON.parse(data);
26
- } catch (e) {
27
- throw new Error('Can\'t parse server response');
28
- }
12
+ const { data } = await axios.post(`https://scanner.tradingview.com/${type}/scan`, {
13
+ symbols: { tickers },
14
+ columns,
15
+ }, { validateStatus });
29
16
 
30
17
  return data;
31
18
  }
@@ -53,40 +40,40 @@ async function fetchScanData(tickers = [], type = '', columns = []) {
53
40
  * }} Periods
54
41
  */
55
42
 
56
- /**
57
- * @typedef {string | 'forex' | 'crypto'
58
- * | 'america' | 'australia' | 'canada' | 'egypt'
59
- * | 'germany' | 'india' | 'israel' | 'italy'
60
- * | 'luxembourg' | 'poland' | 'sweden' | 'turkey'
61
- * | 'uk' | 'vietnam'} Screener
62
- * You can use `getScreener(exchange)` function for non-forex and non-crypto markets.
63
- */
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
+ // */
64
51
 
65
52
  module.exports = {
66
- /**
67
- * Get a screener from an exchange
68
- * @function getScreener
69
- * @param {string} exchange Example: BINANCE, EURONEXT, NASDAQ
70
- * @returns {Screener}
71
- */
72
- getScreener(exchange) {
73
- const e = exchange.toUpperCase();
74
- if (['NASDAQ', 'NYSE', 'NYSE ARCA', 'OTC'].includes(e)) return 'america';
75
- if (['ASX'].includes(e)) return 'australia';
76
- if (['TSX', 'TSXV', 'CSE', 'NEO'].includes(e)) return 'canada';
77
- if (['EGX'].includes(e)) return 'egypt';
78
- if (['FWB', 'SWB', 'XETR'].includes(e)) return 'germany';
79
- if (['BSE', 'NSE'].includes(e)) return 'india';
80
- if (['TASE'].includes(e)) return 'israel';
81
- if (['MIL', 'MILSEDEX'].includes(e)) return 'italy';
82
- if (['LUXSE'].includes(e)) return 'luxembourg';
83
- if (['NEWCONNECT'].includes(e)) return 'poland';
84
- if (['NGM'].includes(e)) return 'sweden';
85
- if (['BIST'].includes(e)) return 'turkey';
86
- if (['LSE', 'LSIN'].includes(e)) return 'uk';
87
- if (['HNX'].includes(e)) return 'vietnam';
88
- return exchange.toLowerCase();
89
- },
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
+ // },
90
77
 
91
78
  /**
92
79
  * Get technical analysis
@@ -138,20 +125,25 @@ module.exports = {
138
125
  * @returns {Promise<SearchMarketResult[]>} Search results
139
126
  */
140
127
  async searchMarket(search, filter = '') {
141
- const { data } = await request({
142
- host: 'symbol-search.tradingview.com',
143
- path: `/symbol_search/?text=${search.replace(/ /g, '%20')}&type=${filter}`,
144
- origin: 'https://www.tradingview.com',
145
- });
128
+ const { data } = await axios.get(
129
+ `https://symbol-search.tradingview.com/symbol_search/?text=${search.replace(/ /g, '%20')}&type=${filter}`,
130
+ {
131
+ validateStatus,
132
+ headers: {
133
+ origin: 'https://www.tradingview.com',
134
+ },
135
+ },
136
+ );
146
137
 
147
138
  return data.map((s) => {
148
139
  const exchange = s.exchange.split(' ')[0];
149
140
  const id = `${exchange}:${s.symbol}`;
150
141
 
151
- const screener = (['forex', 'crypto'].includes(s.type)
152
- ? s.type
153
- : this.getScreener(exchange)
154
- );
142
+ // const screener = (['forex', 'crypto'].includes(s.type)
143
+ // ? s.type
144
+ // : this.getScreener(exchange)
145
+ // );
146
+ const screener = 'global';
155
147
 
156
148
  return {
157
149
  id,
@@ -189,17 +181,18 @@ module.exports = {
189
181
  async searchIndicator(search = '') {
190
182
  if (!builtInIndicList.length) {
191
183
  await Promise.all(['standard', 'candlestick', 'fundamental'].map(async (type) => {
192
- builtInIndicList.push(...(await request({
193
- host: 'pine-facade.tradingview.com',
194
- path: `/pine-facade/list/?filter=${type}`,
195
- })).data);
184
+ const { data } = await axios.get(
185
+ `https://pine-facade.tradingview.com/pine-facade/list/?filter=${type}`,
186
+ { validateStatus },
187
+ );
188
+ builtInIndicList.push(...data);
196
189
  }));
197
190
  }
198
191
 
199
- const { data } = await request({
200
- host: 'www.tradingview.com',
201
- path: `/pubscripts-suggest-json/?search=${search.replace(/ /g, '%20')}`,
202
- });
192
+ const { data } = await axios.get(
193
+ `https://www.tradingview.com/pubscripts-suggest-json/?search=${search.replace(/ /g, '%20')}`,
194
+ { validateStatus },
195
+ );
203
196
 
204
197
  function norm(str = '') {
205
198
  return str.toUpperCase().replace(/[^A-Z]/g, '');
@@ -255,16 +248,10 @@ module.exports = {
255
248
  async getIndicator(id, version = 'last') {
256
249
  const indicID = id.replace(/ |%/g, '%25');
257
250
 
258
- let { data } = await request({
259
- host: 'pine-facade.tradingview.com',
260
- path: `/pine-facade/translate/${indicID}/${version}`,
261
- }, true);
262
-
263
- try {
264
- data = JSON.parse(data);
265
- } catch (e) {
266
- throw new Error(`Inexistent or unsupported indicator: '${id}'`);
267
- }
251
+ const { data } = await axios.get(
252
+ `https://pine-facade.tradingview.com/pine-facade/translate/${indicID}/${version}`,
253
+ { validateStatus },
254
+ );
268
255
 
269
256
  if (!data.success || !data.result.metaInfo || !data.result.metaInfo.inputs) {
270
257
  throw new Error(`Inexistent or unsupported indicator: "${data.reason}"`);
@@ -342,6 +329,7 @@ module.exports = {
342
329
  * @prop {number} notifications.following Notification from following accounts
343
330
  * @prop {string} session User session
344
331
  * @prop {string} sessionHash User session hash
332
+ * @prop {string} signature User session signature
345
333
  * @prop {string} privateChannel User private channel
346
334
  * @prop {string} authToken User auth token
347
335
  * @prop {Date} joinDate Account creation date
@@ -357,21 +345,20 @@ module.exports = {
357
345
  * @returns {Promise<User>} Token
358
346
  */
359
347
  async loginUser(username, password, remember = true, UA = 'TWAPI/3.0') {
360
- const formData = new FormData();
361
- formData.append('username', username);
362
- formData.append('password', password);
363
- if (remember) formData.append('remember', 'on');
364
-
365
- const { data, cookies } = await request({
366
- method: 'POST',
367
- host: 'www.tradingview.com',
368
- path: '/accounts/signin/',
369
- headers: {
370
- referer: 'https://www.tradingview.com',
371
- 'Content-Type': `multipart/form-data; boundary=${formData.boundary}`,
372
- 'User-agent': `${UA} (${os.version()}; ${os.platform()}; ${os.arch()})`,
348
+ const { data, headers } = await axios.post(
349
+ 'https://www.tradingview.com/accounts/signin/',
350
+ `username=${username}&password=${password}${remember ? '&remember=on' : ''}`,
351
+ {
352
+ validateStatus,
353
+ headers: {
354
+ referer: 'https://www.tradingview.com',
355
+ 'Content-Type': 'application/x-www-form-urlencoded',
356
+ 'User-agent': `${UA} (${os.version()}; ${os.platform()}; ${os.arch()})`,
357
+ },
373
358
  },
374
- }, false, formData.toString());
359
+ );
360
+
361
+ const cookies = headers['set-cookie'];
375
362
 
376
363
  if (data.error) throw new Error(data.error);
377
364
 
@@ -408,43 +395,36 @@ module.exports = {
408
395
  * @returns {Promise<User>} Token
409
396
  */
410
397
  async getUser(session, signature = '', location = 'https://www.tradingview.com/') {
411
- return new Promise((cb, err) => {
412
- https.get(location, {
413
- headers: { cookie: `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}` },
414
- }, (res) => {
415
- let rs = '';
416
- res.on('data', (d) => { rs += d; });
417
- res.on('end', async () => {
418
- if (res.headers.location && location !== res.headers.location) {
419
- cb(await module.exports.getUser(session, signature, res.headers.location));
420
- return;
421
- }
422
- if (rs.includes('auth_token')) {
423
- cb({
424
- id: /"id":([0-9]{1,10}),/.exec(rs)[1],
425
- username: /"username":"(.*?)"/.exec(rs)[1],
426
- firstName: /"first_name":"(.*?)"/.exec(rs)[1],
427
- lastName: /"last_name":"(.*?)"/.exec(rs)[1],
428
- reputation: parseFloat(/"reputation":(.*?),/.exec(rs)[1] || 0),
429
- following: parseFloat(/,"following":([0-9]*?),/.exec(rs)[1] || 0),
430
- followers: parseFloat(/,"followers":([0-9]*?),/.exec(rs)[1] || 0),
431
- notifications: {
432
- following: parseFloat(/"notification_count":\{"following":([0-9]*),/.exec(rs)[1] || 0),
433
- user: parseFloat(/"notification_count":\{"following":[0-9]*,"user":([0-9]*)/.exec(rs)[1] || 0),
434
- },
435
- session,
436
- signature,
437
- sessionHash: /"session_hash":"(.*?)"/.exec(rs)[1],
438
- privateChannel: /"private_channel":"(.*?)"/.exec(rs)[1],
439
- authToken: /"auth_token":"(.*?)"/.exec(rs)[1],
440
- joinDate: new Date(/"date_joined":"(.*?)"/.exec(rs)[1] || 0),
441
- });
442
- } else err(new Error('Wrong or expired sessionid/signature'));
443
- });
444
-
445
- res.on('error', err);
446
- }).end();
398
+ const { data } = await axios.get(location, {
399
+ validateStatus,
400
+ headers: {
401
+ cookie: `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}`,
402
+ },
447
403
  });
404
+
405
+ if (data.includes('auth_token')) {
406
+ return {
407
+ id: /"id":([0-9]{1,10}),/.exec(data)?.[1],
408
+ username: /"username":"(.*?)"/.exec(data)?.[1],
409
+ firstName: /"first_name":"(.*?)"/.exec(data)?.[1],
410
+ lastName: /"last_name":"(.*?)"/.exec(data)?.[1],
411
+ reputation: parseFloat(/"reputation":(.*?),/.exec(data)?.[1] || 0),
412
+ following: parseFloat(/,"following":([0-9]*?),/.exec(data)?.[1] || 0),
413
+ followers: parseFloat(/,"followers":([0-9]*?),/.exec(data)?.[1] || 0),
414
+ notifications: {
415
+ following: parseFloat(/"notification_count":\{"following":([0-9]*),/.exec(data)?.[1] || 0),
416
+ user: parseFloat(/"notification_count":\{"following":[0-9]*,"user":([0-9]*)/.exec(data)?.[1] || 0),
417
+ },
418
+ session,
419
+ signature,
420
+ sessionHash: /"session_hash":"(.*?)"/.exec(data)?.[1],
421
+ privateChannel: /"private_channel":"(.*?)"/.exec(data)?.[1],
422
+ authToken: /"auth_token":"(.*?)"/.exec(data)?.[1],
423
+ joinDate: new Date(/"date_joined":"(.*?)"/.exec(data)?.[1] || 0),
424
+ };
425
+ }
426
+
427
+ throw new Error('Wrong or expired sessionid/signature');
448
428
  },
449
429
 
450
430
  /**
@@ -455,44 +435,29 @@ module.exports = {
455
435
  * @returns {Promise<SearchIndicatorResult[]>} Search results
456
436
  */
457
437
  async getPrivateIndicators(session, signature = '') {
458
- return new Promise((cb, err) => {
459
- https.get('https://pine-facade.tradingview.com/pine-facade/list?filter=saved', {
460
- headers: { cookie: `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}` },
461
- }, (res) => {
462
- let rs = '';
463
- res.on('data', (d) => { rs += d; });
464
- res.on('end', async () => {
465
- try {
466
- rs = JSON.parse(rs);
467
- } catch (error) {
468
- err(new Error('Can\'t parse private indicator list'));
469
- return;
470
- }
471
-
472
- cb(rs.map((ind) => ({
473
- id: ind.scriptIdPart,
474
- version: ind.version,
475
- name: ind.scriptName,
476
- author: {
477
- id: -1,
478
- username: '@ME@',
479
- },
480
- image: ind.imageUrl,
481
- access: 'private',
482
- source: ind.scriptSource,
483
- type: (ind.extra && ind.extra.kind) ? ind.extra.kind : 'study',
484
- get() {
485
- return module.exports.getIndicator(ind.scriptIdPart, ind.version);
486
- },
487
- getManager() {
488
- return new PinePermManager(ind.scriptIdPart);
489
- },
490
- })));
491
- });
492
-
493
- res.on('error', err);
494
- }).end();
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};` : ''}`,
442
+ },
495
443
  });
444
+
445
+ return data.map((ind) => ({
446
+ id: ind.scriptIdPart,
447
+ version: ind.version,
448
+ name: ind.scriptName,
449
+ author: {
450
+ id: -1,
451
+ username: '@ME@',
452
+ },
453
+ image: ind.imageUrl,
454
+ access: 'private',
455
+ source: ind.scriptSource,
456
+ type: (ind.extra && ind.extra.kind) ? ind.extra.kind : 'study',
457
+ get() {
458
+ return module.exports.getIndicator(ind.scriptIdPart, ind.version);
459
+ },
460
+ }));
496
461
  },
497
462
 
498
463
  /**
@@ -500,6 +465,7 @@ module.exports = {
500
465
  * @typedef {Object} UserCredentials
501
466
  * @prop {number} id User ID
502
467
  * @prop {string} session User session ('sessionid' cookie)
468
+ * @prop {string} [signature] User session signature ('sessionid_sign' cookie)
503
469
  */
504
470
 
505
471
  /**
@@ -510,16 +476,23 @@ module.exports = {
510
476
  * @returns {Promise<string>} Token
511
477
  */
512
478
  async getChartToken(layout, credentials = {}) {
513
- const creds = credentials.id && credentials.session;
514
- const userID = creds ? credentials.id : -1;
515
- const session = creds ? credentials.session : null;
516
- const signature = creds ? credentials.signature : null;
517
-
518
- const { data } = await request({
519
- host: 'www.tradingview.com',
520
- path: `/chart-token/?image_url=${layout}&user_id=${userID}`,
521
- headers: { cookie: session ? `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}` : '' },
522
- });
479
+ const { id, session, signature } = (
480
+ credentials.id && credentials.session
481
+ ? credentials
482
+ : { id: -1, session: null, signature: null }
483
+ );
484
+
485
+ const { data } = await axios.get(
486
+ `https://www.tradingview.com/chart-token/?image_url=${layout}&user_id=${id}`,
487
+ {
488
+ validateStatus,
489
+ headers: {
490
+ cookie: session
491
+ ? `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}`
492
+ : '',
493
+ },
494
+ },
495
+ );
523
496
 
524
497
  if (!data.token) throw new Error('Wrong layout or credentials');
525
498
 
@@ -557,18 +530,21 @@ module.exports = {
557
530
  * @param {number} [chartID] Chart ID
558
531
  * @returns {Promise<Drawing[]>} Drawings
559
532
  */
560
- async getDrawings(layout, symbol = '', credentials = {}, chartID = 1) {
533
+ async getDrawings(layout, symbol = '', credentials = {}, chartID = '_shared') {
561
534
  const chartToken = await module.exports.getChartToken(layout, credentials);
562
- const creds = credentials.id && credentials.session;
563
- const session = creds ? credentials.session : null;
564
- const signature = creds ? credentials.signature : null;
565
-
566
- const { data } = await request({
567
- host: 'charts-storage.tradingview.com',
568
- path: `/charts-storage/layout/${layout}/sources?chart_id=${chartID
569
- }&jwt=${chartToken}${symbol ? `&symbol=${symbol}` : ''}`,
570
- headers: { cookie: session ? `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}` : '' },
571
- });
535
+
536
+ const { data } = await axios.get(
537
+ `https://charts-storage.tradingview.com/charts-storage/get/layout/${
538
+ layout
539
+ }/sources?chart_id=${
540
+ chartID
541
+ }&jwt=${
542
+ chartToken
543
+ }${
544
+ (symbol ? `&symbol=${symbol}` : '')
545
+ }`,
546
+ { validateStatus },
547
+ );
572
548
 
573
549
  if (!data.payload) throw new Error('Wrong layout, user credentials, or chart id.');
574
550
 
package/vite.config.js ADDED
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ test: {
3
+ testTimeout: 10000,
4
+ setupFiles: 'dotenv/config',
5
+ },
6
+ };
package/src/FormData.js DELETED
@@ -1,36 +0,0 @@
1
- /** @class */
2
- class FormData {
3
- #string = '';
4
-
5
- #boundary = '';
6
-
7
- get boundary() {
8
- return this.#boundary;
9
- }
10
-
11
- constructor() {
12
- const random = (Math.random() * 10 ** 20).toString(36);
13
- this.#boundary = `${random}`;
14
- this.#string = `--${this.boundary}`;
15
- }
16
-
17
- /**
18
- * Adds a property to the FormData object
19
- * @param {string} key Property key
20
- * @param {string} value Property value
21
- */
22
- append(key, value) {
23
- this.#string += `\r\nContent-Disposition: form-data; name="${key}"`;
24
- this.#string += `\r\n\r\n${value}`;
25
- this.#string += `\r\n--${this.boundary}`;
26
- }
27
-
28
- /**
29
- * @returns {string}
30
- */
31
- toString() {
32
- return `${this.#string}--`;
33
- }
34
- }
35
-
36
- module.exports = FormData;
package/src/request.js DELETED
@@ -1,37 +0,0 @@
1
- const https = require('https');
2
-
3
- /**
4
- * @param {https.RequestOptions} options HTTPS Request options
5
- * @param {boolean} [raw] Get raw or JSON data
6
- * @param {string} [content] Request body content
7
- * @returns {Promise<{ data: (string | object | array), cookies: string[] }>} Result
8
- */
9
- function request(options = {}, raw = false, content = '') {
10
- return new Promise((cb, err) => {
11
- const req = https.request(options, (res) => {
12
- let data = '';
13
- res.on('data', (c) => { data += c; });
14
- res.on('end', () => {
15
- if (raw) {
16
- cb({ data, cookies: res.headers['set-cookie'] });
17
- return;
18
- }
19
-
20
- try {
21
- data = JSON.parse(data);
22
- } catch (error) {
23
- console.log(data);
24
- err(new Error('Can\'t parse server response'));
25
- return;
26
- }
27
-
28
- cb({ data, cookies: res.headers['set-cookie'] });
29
- });
30
- });
31
-
32
- req.on('error', err);
33
- req.end(content);
34
- });
35
- }
36
-
37
- module.exports = request;
package/test.js DELETED
@@ -1,70 +0,0 @@
1
- /* eslint-disable no-continue */
2
- /* eslint-disable no-await-in-loop */
3
- /* eslint-disable import/no-dynamic-require */
4
- /* eslint-disable no-restricted-syntax */
5
- /* eslint-disable global-require */
6
-
7
- const fs = require('fs');
8
- require('@mathieuc/console')(
9
- '§', // Character you want to use (defaut: '§')
10
- true, // Active timestamp (defaut: false)
11
- );
12
-
13
- const TESTS = {};
14
-
15
- function err(name) {
16
- return (...msg) => {
17
- TESTS[name].errors += 1;
18
- console.error('§30§101 > §0', ...msg);
19
- };
20
- }
21
-
22
- function warn(name) {
23
- return (...msg) => {
24
- TESTS[name].warnings += 1;
25
- console.warn('§30§103 > §0', ...msg);
26
- };
27
- }
28
-
29
- function log() {
30
- return (...msg) => console.log('§30§107 > §0', ...msg);
31
- }
32
-
33
- function success() {
34
- return (...msg) => console.info('§30§102 > §0', ...msg);
35
- }
36
-
37
- (async () => {
38
- console.info('§90§30§104 ==== Starting tests ==== §0');
39
-
40
- for (const file of fs.readdirSync('./tests').filter((f) => f.endsWith('.js'))) {
41
- if (process.argv[2] && !file.startsWith(process.argv[2])) continue;
42
- const test = require(`./tests/${file}`);
43
- const name = file.replace(/\.js/g, '');
44
- TESTS[name] = { errors: 0, warnings: 0 };
45
- console.log(`§90§30§107 ${name}`);
46
- await new Promise((cb) => test(log(), success(), warn(name), err(name), cb));
47
- }
48
-
49
- console.info('§90§30§104 ==== ALL TESTS DONE ==== §0\n');
50
-
51
- let errored = false;
52
-
53
- Object.keys(TESTS).forEach((t) => {
54
- let color = '2';
55
- if (TESTS[t].warnings) color = '3';
56
- if (TESTS[t].errors) {
57
- color = '1';
58
- errored = true;
59
- }
60
-
61
- console.info(`§90§30§10${color} ${t} §0 §91E§0: ${TESTS[t].errors}§0 §93W§0: ${TESTS[t].warnings}`);
62
- });
63
-
64
- process.exit(errored ? 1 : 0);
65
- })();
66
-
67
- setTimeout(() => {
68
- console.log('§30§101 TIMEOUT §0');
69
- throw new Error('Timeout');
70
- }, 60000);
@@ -1,17 +0,0 @@
1
- const TradingView = require('../main');
2
-
3
- module.exports = async (log, success, warn, err, cb) => {
4
- log('Searching market: \'BINANCE:\'');
5
- const markets = await TradingView.searchMarket('BINANCE:');
6
- success('Found', markets.length, 'markets');
7
-
8
- log('Searching indicator: \'RSI\'');
9
- const indicators = await TradingView.searchIndicator('RSI');
10
- success('Found', indicators.length, 'indicators');
11
-
12
- if (markets.length < 10 || indicators.length < 10) {
13
- err('Not enough results');
14
- }
15
-
16
- cb();
17
- };
@@ -1,51 +0,0 @@
1
- const TradingView = require('../main');
2
-
3
- module.exports = (log, success, warn, err, cb) => {
4
- const client = new TradingView.Client();
5
-
6
- client.onError((...error) => {
7
- err('Client error', ...error);
8
- });
9
-
10
- const quoteSession = new client.Session.Quote({
11
- fields: 'all',
12
- });
13
-
14
- const BTC = new quoteSession.Market('BTCEUR');
15
-
16
- BTC.onLoaded(() => {
17
- success('BTCEUR LOADED');
18
- });
19
-
20
- const keys = [
21
- 'volume', 'update_mode', 'type', 'timezone',
22
- 'short_name', 'rtc_time', 'rtc', 'rchp', 'ch',
23
- 'rch', 'provider_id', 'pro_name', 'pricescale',
24
- 'prev_close_price', 'original_name', 'lp',
25
- 'open_price', 'minmove2', 'minmov', 'lp_time',
26
- 'low_price', 'is_tradable', 'high_price',
27
- 'fractional', 'exchange', 'description',
28
- 'current_session', 'currency_code', 'chp',
29
- 'currency-logoid', 'base-currency-logoid',
30
- ];
31
-
32
- BTC.onData(async (data) => {
33
- const rsKeys = Object.keys(data);
34
- success('BTCEUR DATA');
35
- if (rsKeys.length <= 2) return;
36
-
37
- keys.forEach((k) => {
38
- if (!rsKeys.includes(k)) {
39
- err(`Missing '${k}' key in`, rsKeys);
40
- }
41
- });
42
-
43
- quoteSession.delete();
44
- await client.end();
45
- cb();
46
- });
47
-
48
- BTC.onError((...error) => {
49
- err('BTCEUR ERROR:', error);
50
- });
51
- };