@mathieuc/tradingview 3.3.2 → 3.4.0

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/.gitattributes CHANGED
@@ -1,2 +1,2 @@
1
- # Auto detect text files and perform LF normalization
2
- * text=auto
1
+ # Auto detect text files and perform LF normalization
2
+ * text=auto
package/README.md CHANGED
@@ -1,66 +1,66 @@
1
- # TradingView API [![GitHub stars](https://img.shields.io/github/stars/Mathieu2301/TradingView-API.svg?style=social&label=Star&maxAge=2592000)](https://GitHub.com/Mathieu2301/TradingView-API/stargazers/)
2
-
3
- [![Tests](https://github.com/Mathieu2301/TradingView-API/actions/workflows/node.js.yml/badge.svg)](https://github.com/Mathieu2301/TradingView-API/actions/workflows/node.js.yml)
4
- [![CodeFactor](https://www.codefactor.io/repository/github/mathieu2301/tradingview-api/badge/main)](https://www.codefactor.io/repository/github/mathieu2301/tradingview-api/overview/main)
5
- [![GitHub latest commit](https://img.shields.io/github/last-commit/Mathieu2301/TradingView-API)](https://GitHub.com/Mathieu2301/TradingView-API/commit/)
6
- [![Npm package yearly downloads](https://badgen.net/npm/dt/@mathieuc/tradingview)](https://npmjs.com/@mathieuc/tradingview)
7
- [![Minimum node.js version](https://badgen.net/npm/node/@mathieuc/tradingview)](https://npmjs.com/@mathieuc/tradingview)
8
- [![Npm package version](https://badgen.net/npm/v/@mathieuc/tradingview)](https://npmjs.com/package/@mathieuc/tradingview)
9
-
10
- Get realtime market prices and indicator values from Tradingview !
11
-
12
- ## Features
13
-
14
- - [x] Premium features
15
- - [x] Automatically backtest many strategies and try many settings in a very little time
16
- - [x] Get drawings you made on your chart
17
- - [x] Works with invite-only indicators
18
- - [x] Unlimited simultaneous indicators
19
- - [x] Realtime
20
- - [x] Get TradingView's technical analysis
21
- - [x] Replay mode + Fake Replay mode (for free plan)
22
- - [x] Get values from a specific date range
23
- - [ ] TradingView socket server emulation
24
- - [ ] Interract with public chats
25
- - [ ] Get Screener top values
26
- - [ ] Get Hotlists
27
- - [ ] Get Calendar
28
- - IF YOU WANT A FEATURE, ASK ME !
29
-
30
- ## Possibilities
31
-
32
- - Trading bot
33
- - Discord alerts
34
- - Hard backtest
35
- - Machine Learning based indicator
36
- - Free replay mode for all timeframes
37
-
38
- ___
39
-
40
- ## Installation
41
-
42
- Stable version:
43
-
44
- ```ruby
45
- npm i @mathieuc/tradingview
46
- ```
47
-
48
- Last version:
49
-
50
- ```ruby
51
- npm i github:Mathieu2301/TradingView-API
52
- ```
53
-
54
- ## Examples
55
-
56
- You can find all the examples and snippets in `./examples` folder.
57
-
58
- ## Before opening an issue
59
-
60
- Please look at examples and previously resolved issues before opening a new one. I can't help everyone (especially for questions that are not library related but JavaScript related). Thank you for your understanding.
61
- ___
62
-
63
- ## Problems
64
-
65
- If you have errors in console or unwanted behavior,
66
- please create an issue [here](https://github.com/Mathieu2301/Tradingview-API/issues).
1
+ # TradingView API [![GitHub stars](https://img.shields.io/github/stars/Mathieu2301/TradingView-API.svg?style=social&label=Star&maxAge=2592000)](https://GitHub.com/Mathieu2301/TradingView-API/stargazers/)
2
+
3
+ [![Tests](https://github.com/Mathieu2301/TradingView-API/actions/workflows/node.js.yml/badge.svg)](https://github.com/Mathieu2301/TradingView-API/actions/workflows/node.js.yml)
4
+ [![CodeFactor](https://www.codefactor.io/repository/github/mathieu2301/tradingview-api/badge/main)](https://www.codefactor.io/repository/github/mathieu2301/tradingview-api/overview/main)
5
+ [![GitHub latest commit](https://img.shields.io/github/last-commit/Mathieu2301/TradingView-API)](https://GitHub.com/Mathieu2301/TradingView-API/commit/)
6
+ [![Npm package yearly downloads](https://badgen.net/npm/dt/@mathieuc/tradingview)](https://npmjs.com/@mathieuc/tradingview)
7
+ [![Minimum node.js version](https://badgen.net/npm/node/@mathieuc/tradingview)](https://npmjs.com/@mathieuc/tradingview)
8
+ [![Npm package version](https://badgen.net/npm/v/@mathieuc/tradingview)](https://npmjs.com/package/@mathieuc/tradingview)
9
+
10
+ Get realtime market prices and indicator values from Tradingview !
11
+
12
+ ## Features
13
+
14
+ - [x] Premium features
15
+ - [x] Automatically backtest many strategies and try many settings in a very little time
16
+ - [x] Get drawings you made on your chart
17
+ - [x] Works with invite-only indicators
18
+ - [x] Unlimited simultaneous indicators
19
+ - [x] Realtime
20
+ - [x] Get TradingView's technical analysis
21
+ - [x] Replay mode + Fake Replay mode (for free plan)
22
+ - [x] Get values from a specific date range
23
+ - [ ] TradingView socket server emulation
24
+ - [ ] Interract with public chats
25
+ - [ ] Get Screener top values
26
+ - [ ] Get Hotlists
27
+ - [ ] Get Calendar
28
+ - IF YOU WANT A FEATURE, ASK ME !
29
+
30
+ ## Possibilities
31
+
32
+ - Trading bot
33
+ - Discord alerts
34
+ - Hard backtest
35
+ - Machine Learning based indicator
36
+ - Free replay mode for all timeframes
37
+
38
+ ___
39
+
40
+ ## Installation
41
+
42
+ Stable version:
43
+
44
+ ```ruby
45
+ npm i @mathieuc/tradingview
46
+ ```
47
+
48
+ Last version:
49
+
50
+ ```ruby
51
+ npm i github:Mathieu2301/TradingView-API
52
+ ```
53
+
54
+ ## Examples
55
+
56
+ You can find all the examples and snippets in `./examples` folder.
57
+
58
+ ## Before opening an issue
59
+
60
+ Please look at examples and previously resolved issues before opening a new one. I can't help everyone (especially for questions that are not library related but JavaScript related). Thank you for your understanding.
61
+ ___
62
+
63
+ ## Problems
64
+
65
+ If you have errors in console or unwanted behavior,
66
+ please create an issue [here](https://github.com/Mathieu2301/Tradingview-API/issues).
@@ -1,147 +1,147 @@
1
- const TradingView = require('../main');
2
-
3
- /*
4
- This example tests many types of errors
5
- */
6
-
7
- const client = new TradingView.Client(); // Creates a websocket client
8
-
9
- const tests = [
10
- (next) => { /* Testing "Credentials error" */
11
- console.info('\nTesting "Credentials error" error:');
12
-
13
- const client2 = new TradingView.Client({
14
- token: 'FAKE_CREDENTIALS', // Set wrong credentials
15
- });
16
-
17
- client2.onError((...err) => {
18
- console.error(' => Client error:', err);
19
- client2.end();
20
- next();
21
- });
22
- },
23
-
24
- (next) => { /* Testing "Invalid symbol" */
25
- console.info('\nTesting "Invalid symbol" error:');
26
-
27
- const chart = new client.Session.Chart();
28
- chart.onError((...err) => { // Listen for errors
29
- console.error(' => Chart error:', err);
30
- chart.delete();
31
- next();
32
- });
33
-
34
- chart.setMarket('XXXXX'); // Set a wrong market
35
- },
36
-
37
- (next) => { /* Testing "Invalid timezone" */
38
- console.info('\nTesting "Invalid timezone" error:');
39
-
40
- const chart = new client.Session.Chart();
41
- chart.onError((...err) => { // Listen for errors
42
- console.error(' => Chart error:', err);
43
- next();
44
- });
45
-
46
- chart.setMarket('BINANCE:BTCEUR'); // Set a market
47
- chart.setTimezone('Nowhere/Nowhere'); // Set a fake timezone
48
- },
49
-
50
- (next) => { /* Testing "Custom timeframe" */
51
- console.info('\nTesting "Custom timeframe" error:');
52
-
53
- const chart = new client.Session.Chart();
54
- chart.onError((...err) => { // Listen for errors
55
- console.error(' => Chart error:', err);
56
- chart.delete();
57
- next();
58
- });
59
-
60
- chart.setMarket('BINANCE:BTCEUR', { // Set a market
61
- timeframe: '20', // Set a custom timeframe
62
- /*
63
- Timeframe '20' isn't available because we are
64
- not logged in as a premium TradingView account
65
- */
66
- });
67
- },
68
-
69
- (next) => { /* Testing "Invalid timeframe" */
70
- console.info('\nTesting "Invalid timeframe" error:');
71
-
72
- const chart = new client.Session.Chart();
73
- chart.onError((...err) => { // Listen for errors
74
- console.error(' => Chart error:', err);
75
- next();
76
- });
77
-
78
- chart.setMarket('BINANCE:BTCEUR', { // Set a market
79
- timeframe: 'XX', // Set a wrong timeframe
80
- });
81
- },
82
-
83
- (next) => { /* Testing "Study not auth" */
84
- console.info('\nTesting "Study not auth" error:');
85
-
86
- const chart = new client.Session.Chart();
87
- chart.onError((...err) => { // Listen for errors
88
- console.error(' => Chart error:', err);
89
- next();
90
- });
91
-
92
- chart.setMarket('BINANCE:BTCEUR', { // Set a market
93
- timeframe: '15',
94
- type: 'Renko',
95
- });
96
-
97
- chart.onUpdate(() => {
98
- console.log('DATA', chart.periods[0]);
99
- });
100
- },
101
-
102
- (next) => { /* Testing "Set the market before" */
103
- console.info('\nTesting "Set the market before..." error:');
104
-
105
- const chart = new client.Session.Chart();
106
- chart.onError((...err) => { // Listen for errors
107
- console.error(' => Chart error:', err);
108
- chart.delete();
109
- next();
110
- });
111
-
112
- chart.setSeries('15'); // Set series before setting the market
113
- },
114
-
115
- (next) => { /* Testing "Inexistent indicator" */
116
- console.info('\nTesting "Inexistent indicator" error:');
117
-
118
- TradingView.getIndicator('STD;XXXXXXX')
119
- .catch((err) => {
120
- console.error(' => API error:', [err.message]);
121
- next();
122
- });
123
- },
124
-
125
- async (next) => { /* Testing "Invalid value" */
126
- console.info('\nTesting "Invalid value" error:');
127
-
128
- const chart = new client.Session.Chart();
129
- chart.setMarket('BINANCE:BTCEUR'); // Set a market
130
-
131
- const ST = await TradingView.getIndicator('STD;Supertrend');
132
- ST.setOption('Factor', -1); // This will cause an error
133
-
134
- const Supertrend = new chart.Study(ST);
135
- Supertrend.onError((...err) => {
136
- console.error(' => Study error:', err);
137
- chart.delete();
138
- next();
139
- });
140
- },
141
- ];
142
-
143
- (async () => {
144
- // eslint-disable-next-line no-restricted-syntax, no-await-in-loop
145
- for (const t of tests) await new Promise(t);
146
- console.log(`\nTests ${tests.length}/${tests.length} done !`);
147
- })();
1
+ const TradingView = require('../main');
2
+
3
+ /*
4
+ This example tests many types of errors
5
+ */
6
+
7
+ const client = new TradingView.Client(); // Creates a websocket client
8
+
9
+ const tests = [
10
+ (next) => { /* Testing "Credentials error" */
11
+ console.info('\nTesting "Credentials error" error:');
12
+
13
+ const client2 = new TradingView.Client({
14
+ token: 'FAKE_CREDENTIALS', // Set wrong credentials
15
+ });
16
+
17
+ client2.onError((...err) => {
18
+ console.error(' => Client error:', err);
19
+ client2.end();
20
+ next();
21
+ });
22
+ },
23
+
24
+ (next) => { /* Testing "Invalid symbol" */
25
+ console.info('\nTesting "Invalid symbol" error:');
26
+
27
+ const chart = new client.Session.Chart();
28
+ chart.onError((...err) => { // Listen for errors
29
+ console.error(' => Chart error:', err);
30
+ chart.delete();
31
+ next();
32
+ });
33
+
34
+ chart.setMarket('XXXXX'); // Set a wrong market
35
+ },
36
+
37
+ (next) => { /* Testing "Invalid timezone" */
38
+ console.info('\nTesting "Invalid timezone" error:');
39
+
40
+ const chart = new client.Session.Chart();
41
+ chart.onError((...err) => { // Listen for errors
42
+ console.error(' => Chart error:', err);
43
+ next();
44
+ });
45
+
46
+ chart.setMarket('BINANCE:BTCEUR'); // Set a market
47
+ chart.setTimezone('Nowhere/Nowhere'); // Set a fake timezone
48
+ },
49
+
50
+ (next) => { /* Testing "Custom timeframe" */
51
+ console.info('\nTesting "Custom timeframe" error:');
52
+
53
+ const chart = new client.Session.Chart();
54
+ chart.onError((...err) => { // Listen for errors
55
+ console.error(' => Chart error:', err);
56
+ chart.delete();
57
+ next();
58
+ });
59
+
60
+ chart.setMarket('BINANCE:BTCEUR', { // Set a market
61
+ timeframe: '20', // Set a custom timeframe
62
+ /*
63
+ Timeframe '20' isn't available because we are
64
+ not logged in as a premium TradingView account
65
+ */
66
+ });
67
+ },
68
+
69
+ (next) => { /* Testing "Invalid timeframe" */
70
+ console.info('\nTesting "Invalid timeframe" error:');
71
+
72
+ const chart = new client.Session.Chart();
73
+ chart.onError((...err) => { // Listen for errors
74
+ console.error(' => Chart error:', err);
75
+ next();
76
+ });
77
+
78
+ chart.setMarket('BINANCE:BTCEUR', { // Set a market
79
+ timeframe: 'XX', // Set a wrong timeframe
80
+ });
81
+ },
82
+
83
+ (next) => { /* Testing "Study not auth" */
84
+ console.info('\nTesting "Study not auth" error:');
85
+
86
+ const chart = new client.Session.Chart();
87
+ chart.onError((...err) => { // Listen for errors
88
+ console.error(' => Chart error:', err);
89
+ next();
90
+ });
91
+
92
+ chart.setMarket('BINANCE:BTCEUR', { // Set a market
93
+ timeframe: '15',
94
+ type: 'Renko',
95
+ });
96
+
97
+ chart.onUpdate(() => {
98
+ console.log('DATA', chart.periods[0]);
99
+ });
100
+ },
101
+
102
+ (next) => { /* Testing "Set the market before" */
103
+ console.info('\nTesting "Set the market before..." error:');
104
+
105
+ const chart = new client.Session.Chart();
106
+ chart.onError((...err) => { // Listen for errors
107
+ console.error(' => Chart error:', err);
108
+ chart.delete();
109
+ next();
110
+ });
111
+
112
+ chart.setSeries('15'); // Set series before setting the market
113
+ },
114
+
115
+ (next) => { /* Testing "Inexistent indicator" */
116
+ console.info('\nTesting "Inexistent indicator" error:');
117
+
118
+ TradingView.getIndicator('STD;XXXXXXX')
119
+ .catch((err) => {
120
+ console.error(' => API error:', [err.message]);
121
+ next();
122
+ });
123
+ },
124
+
125
+ async (next) => { /* Testing "Invalid value" */
126
+ console.info('\nTesting "Invalid value" error:');
127
+
128
+ const chart = new client.Session.Chart();
129
+ chart.setMarket('BINANCE:BTCEUR'); // Set a market
130
+
131
+ const ST = await TradingView.getIndicator('STD;Supertrend');
132
+ ST.setOption('Factor', -1); // This will cause an error
133
+
134
+ const Supertrend = new chart.Study(ST);
135
+ Supertrend.onError((...err) => {
136
+ console.error(' => Study error:', err);
137
+ chart.delete();
138
+ next();
139
+ });
140
+ },
141
+ ];
142
+
143
+ (async () => {
144
+ // eslint-disable-next-line no-restricted-syntax, no-await-in-loop
145
+ for (const t of tests) await new Promise(t);
146
+ console.log(`\nTests ${tests.length}/${tests.length} done !`);
147
+ })();
package/package.json CHANGED
@@ -1,39 +1,39 @@
1
- {
2
- "name": "@mathieuc/tradingview",
3
- "version": "3.3.2",
4
- "description": "Tradingview instant stocks API, indicator alerts, trading bot, and more !",
5
- "main": "main.js",
6
- "scripts": {
7
- "test": "node test"
8
- },
9
- "repository": {
10
- "type": "git",
11
- "url": "git+https://github.com/Mathieu2301/TradingView-API.git"
12
- },
13
- "engines": {
14
- "node": ">=14.0.0"
15
- },
16
- "keywords": [
17
- "tradingwiew",
18
- "market",
19
- "stocks",
20
- "crypto",
21
- "forex",
22
- "indicator",
23
- "bitcoin",
24
- "api"
25
- ],
26
- "author": "Mathieu Colmon",
27
- "license": "ISC",
28
- "dependencies": {
29
- "jszip": "^3.7.1",
30
- "ws": "^7.4.3"
31
- },
32
- "devDependencies": {
33
- "@babel/eslint-parser": "^7.15.7",
34
- "@mathieuc/console": "^1.0.1",
35
- "eslint": "^7.25.0",
36
- "eslint-config-airbnb-base": "^14.2.1",
37
- "eslint-plugin-import": "^2.22.1"
38
- }
39
- }
1
+ {
2
+ "name": "@mathieuc/tradingview",
3
+ "version": "3.4.0",
4
+ "description": "Tradingview instant stocks API, indicator alerts, trading bot, and more !",
5
+ "main": "main.js",
6
+ "scripts": {
7
+ "test": "node test"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/Mathieu2301/TradingView-API.git"
12
+ },
13
+ "engines": {
14
+ "node": ">=14.0.0"
15
+ },
16
+ "keywords": [
17
+ "tradingwiew",
18
+ "market",
19
+ "stocks",
20
+ "crypto",
21
+ "forex",
22
+ "indicator",
23
+ "bitcoin",
24
+ "api"
25
+ ],
26
+ "author": "Mathieu Colmon",
27
+ "license": "ISC",
28
+ "dependencies": {
29
+ "jszip": "^3.7.1",
30
+ "ws": "^7.4.3"
31
+ },
32
+ "devDependencies": {
33
+ "@babel/eslint-parser": "^7.15.7",
34
+ "@mathieuc/console": "^1.0.1",
35
+ "eslint": "^7.25.0",
36
+ "eslint-config-airbnb-base": "^14.2.1",
37
+ "eslint-plugin-import": "^2.22.1"
38
+ }
39
+ }
@@ -32,6 +32,27 @@ function getInputs(options) {
32
32
  return options.options;
33
33
  }
34
34
 
35
+ const parseTrades = (trades) => trades.reverse().map((t) => ({
36
+ entry: {
37
+ name: t.e.c,
38
+ type: (t.e.tp[0] === 's' ? 'short' : 'long'),
39
+ value: t.e.p,
40
+ time: t.e.tm,
41
+ },
42
+ exit: {
43
+ name: t.x.c,
44
+ value: t.x.p,
45
+ time: t.x.tm,
46
+ },
47
+ quantity: t.q,
48
+ profit: t.tp,
49
+ cumulative: t.cp,
50
+ runup: t.rn,
51
+ drawdown: t.dd,
52
+ }));
53
+
54
+ // const historyParser = (history) => history.reverse().map((h) => ({
55
+
35
56
  /**
36
57
  * @typedef {Object} TradeReport Trade report
37
58
 
@@ -86,9 +107,19 @@ function getInputs(options) {
86
107
  * @prop {number} totalTrades Total trades
87
108
  */
88
109
 
110
+ /**
111
+ * @typedef {Object} FromTo
112
+ * @prop {number} from From timestamp
113
+ * @prop {number} to To timestamp
114
+ */
115
+
89
116
  /**
90
117
  * @typedef {Object} StrategyReport
91
118
  * @prop {'EUR' | 'USD' | 'JPY' | '' | 'CHF'} [currency] Selected currency
119
+ * @prop {Object} [settings] Backtester settings
120
+ * @prop {Object} [settings.dateRange] Backtester date range
121
+ * @prop {FromTo} [settings.dateRange.backtest] Date range for backtest
122
+ * @prop {FromTo} [settings.dateRange.trade] Date range for trade
92
123
  * @prop {TradeReport[]} trades Trade list starting by the last
93
124
  * @prop {Object} history History Chart value
94
125
  * @prop {number[]} [history.buyHold] Buy hold values
@@ -275,60 +306,45 @@ module.exports = (chartSession) => class ChartStudy {
275
306
  changes.push('graphic');
276
307
  }
277
308
 
278
- if (parsed.data && parsed.data.report && parsed.data.report.performance) {
279
- this.#strategyReport.performance = parsed.data.report.performance;
280
- changes.push('perfReport');
281
- }
309
+ const updateStrategyReport = (report) => {
310
+ if (report.currency) {
311
+ this.#strategyReport.currency = report.currency;
312
+ changes.push('report.currency');
313
+ }
282
314
 
283
- if (parsed.data && parsed.data.report && parsed.data.report.trades) {
284
- this.#strategyReport.trades = parsed.data.report.trades;
285
- changes.push('tradesReport');
286
- }
315
+ if (report.settings) {
316
+ this.#strategyReport.settings = report.settings;
317
+ changes.push('report.settings');
318
+ }
287
319
 
288
- if (parsed.data && parsed.data.report && parsed.data.report.history) {
289
- this.#strategyReport.history = parsed.data.report.history;
290
- changes.push('historyReport');
291
- }
320
+ if (report.performance) {
321
+ this.#strategyReport.performance = report.performance;
322
+ changes.push('report.perf');
323
+ }
324
+
325
+ if (report.trades) {
326
+ this.#strategyReport.trades = parseTrades(report.trades);
327
+ changes.push('report.trades');
328
+ }
329
+
330
+ if (report.equity) {
331
+ this.#strategyReport.history = {
332
+ buyHold: report.buyHold,
333
+ buyHoldPercent: report.buyHoldPercent,
334
+ drawDown: report.drawDown,
335
+ drawDownPercent: report.drawDownPercent,
336
+ equity: report.equity,
337
+ equityPercent: report.equityPercent,
338
+ };
339
+ changes.push('report.history');
340
+ }
341
+ };
292
342
 
293
343
  if (parsed.dataCompressed) {
294
- const parsedC = await parseCompressed(parsed.dataCompressed);
295
-
296
- this.#strategyReport = {
297
- currency: parsedC.report.currency,
298
-
299
- trades: parsedC.report.trades.reverse().map((t) => ({
300
- entry: {
301
- name: t.e.c,
302
- type: (t.e.tp[0] === 's' ? 'short' : 'long'),
303
- value: t.e.p,
304
- time: t.e.tm,
305
- },
306
- exit: {
307
- name: t.x.c,
308
- value: t.x.p,
309
- time: t.x.tm,
310
- },
311
- quantity: t.q,
312
- profit: t.tp,
313
- cumulative: t.cp,
314
- runup: t.rn,
315
- drawdown: t.dd,
316
- })),
317
-
318
- history: {
319
- buyHold: parsedC.report.buyHold,
320
- buyHoldPercent: parsedC.report.buyHoldPercent,
321
- drawDown: parsedC.report.drawDown,
322
- drawDownPercent: parsedC.report.drawDownPercent,
323
- equity: parsedC.report.equity,
324
- equityPercent: parsedC.report.equityPercent,
325
- },
326
-
327
- performance: parsedC.report.performance,
328
- };
329
-
330
- changes.push('fullReport');
344
+ updateStrategyReport((await parseCompressed(parsed.dataCompressed)).report);
331
345
  }
346
+
347
+ if (parsed.data && parsed.data.report) updateStrategyReport(parsed.data.report);
332
348
  }
333
349
 
334
350
  if (data.ns.indexes && typeof data.ns.indexes === 'object') {
@@ -383,8 +399,10 @@ module.exports = (chartSession) => class ChartStudy {
383
399
  }
384
400
 
385
401
  /**
386
- * @typedef {
387
- * 'plots' | 'perfReport' | 'tradesReport' | 'historyReport' | 'fullReport'
402
+ * @typedef {'plots' | 'report.currency'
403
+ * | 'report.settings' | 'report.perf'
404
+ * | 'report.trades' | 'report.history'
405
+ * | 'graphic'
388
406
  * } UpdateChangeType
389
407
  */
390
408
 
package/src/client.js CHANGED
@@ -225,7 +225,7 @@ module.exports = class Client {
225
225
  if (clientOptions.DEBUG) global.TW_DEBUG = clientOptions.DEBUG;
226
226
 
227
227
  const server = clientOptions.server || 'data';
228
- this.#ws = new WebSocket(`wss://${server}.tradingview.com/socket.io/websocket`, {
228
+ this.#ws = new WebSocket(`wss://${server}.tradingview.com/socket.io/websocket?&type=chart`, {
229
229
  origin: 'https://s.tradingview.com',
230
230
  });
231
231
 
@@ -5,11 +5,16 @@
5
5
  /**
6
6
  * @param {import('./session').QuoteSessionBridge} quoteSession
7
7
  */
8
+
8
9
  module.exports = (quoteSession) => class QuoteMarket {
9
10
  #symbolListeners = quoteSession.symbolListeners;
10
11
 
11
12
  #symbol;
12
13
 
14
+ #session;
15
+
16
+ #symbolKey;
17
+
13
18
  #symbolListenerID = 0;
14
19
 
15
20
  #lastData = {};
@@ -38,20 +43,24 @@ module.exports = (quoteSession) => class QuoteMarket {
38
43
 
39
44
  /**
40
45
  * @param {string} symbol Market symbol (like: 'BTCEUR' or 'KRAKEN:BTCEUR')
46
+ * @param {string} session Market session (like: 'regular' or 'extended')
41
47
  */
42
- constructor(symbol) {
48
+ constructor(symbol, session = 'regular') {
43
49
  this.#symbol = symbol;
50
+ this.#session = session;
51
+ this.#symbolKey = `=${JSON.stringify({ session, symbol })}`;
44
52
 
45
- if (!this.#symbolListeners[symbol]) {
46
- this.#symbolListeners[symbol] = [];
53
+ if (!this.#symbolListeners[this.#symbolKey]) {
54
+ this.#symbolListeners[this.#symbolKey] = [];
47
55
  quoteSession.send('quote_add_symbols', [
48
56
  quoteSession.sessionID,
49
- symbol,
57
+ this.#symbolKey,
50
58
  ]);
51
59
  }
52
- this.#symbolListenerID = this.#symbolListeners[symbol].length;
53
60
 
54
- this.#symbolListeners[symbol][this.#symbolListenerID] = (packet) => {
61
+ this.#symbolListenerID = this.#symbolListeners[this.#symbolKey].length;
62
+
63
+ this.#symbolListeners[this.#symbolKey][this.#symbolListenerID] = (packet) => {
55
64
  if (global.TW_DEBUG) console.log('§90§30§105 MARKET §0 DATA', packet);
56
65
 
57
66
  if (packet.type === 'qsd' && packet.data[1].s === 'ok') {
@@ -112,12 +121,12 @@ module.exports = (quoteSession) => class QuoteMarket {
112
121
 
113
122
  /** Close this listener */
114
123
  close() {
115
- if (this.#symbolListeners[this.#symbol].length <= 1) {
124
+ if (this.#symbolListeners[this.#symbolKey].length <= 1) {
116
125
  quoteSession.send('quote_remove_symbols', [
117
126
  quoteSession.sessionID,
118
- this.#symbol,
127
+ this.#symbolKey,
119
128
  ]);
120
129
  }
121
- delete this.#symbolListeners[this.#symbol][this.#symbolListenerID];
130
+ delete this.#symbolListeners[this.#symbolKey][this.#symbolListenerID];
122
131
  }
123
132
  };
@@ -79,21 +79,21 @@ module.exports = (client) => class QuoteSession {
79
79
  if (global.TW_DEBUG) console.log('§90§30§102 QUOTE SESSION §0 DATA', packet);
80
80
 
81
81
  if (packet.type === 'quote_completed') {
82
- const symbol = packet.data[1];
83
- if (!this.#symbolListeners[symbol]) {
84
- this.#client.send('quote_remove_symbols', [this.#sessionID, symbol]);
82
+ const symbolKey = packet.data[1];
83
+ if (!this.#symbolListeners[symbolKey]) {
84
+ this.#client.send('quote_remove_symbols', [this.#sessionID, symbolKey]);
85
85
  return;
86
86
  }
87
- this.#symbolListeners[symbol].forEach((h) => h(packet));
87
+ this.#symbolListeners[symbolKey].forEach((h) => h(packet));
88
88
  }
89
89
 
90
90
  if (packet.type === 'qsd') {
91
- const symbol = packet.data[1].n;
92
- if (!this.#symbolListeners[symbol]) {
93
- this.#client.send('quote_remove_symbols', [this.#sessionID, symbol]);
91
+ const symbolKey = packet.data[1].n;
92
+ if (!this.#symbolListeners[symbolKey]) {
93
+ this.#client.send('quote_remove_symbols', [this.#sessionID, symbolKey]);
94
94
  return;
95
95
  }
96
- this.#symbolListeners[symbol].forEach((h) => h(packet));
96
+ this.#symbolListeners[symbolKey].forEach((h) => h(packet));
97
97
  }
98
98
  },
99
99
  };
package/src/request.js CHANGED
@@ -1,37 +1,37 @@
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;
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 CHANGED
@@ -47,17 +47,21 @@ function success() {
47
47
  }
48
48
 
49
49
  console.info('§90§30§104 ==== ALL TESTS DONE ==== §0\n');
50
+
51
+ let errored = false;
52
+
50
53
  Object.keys(TESTS).forEach((t) => {
51
54
  let color = '2';
52
55
  if (TESTS[t].warnings) color = '3';
53
- if (TESTS[t].errors) color = '1';
56
+ if (TESTS[t].errors) {
57
+ color = '1';
58
+ errored = true;
59
+ }
54
60
 
55
61
  console.info(`§90§30§10${color} ${t} §0 §91E§0: ${TESTS[t].errors}§0 §93W§0: ${TESTS[t].warnings}`);
56
- // console.info(` - §91 Errors:§0 ${TESTS[t].errors}`);
57
- // console.info(` - §93 Warnings:§0 ${TESTS[t].warnings}`);
58
62
  });
59
63
 
60
- process.exit(0);
64
+ process.exit(errored ? 1 : 0);
61
65
  })();
62
66
 
63
67
  setTimeout(() => {
@@ -11,7 +11,6 @@ module.exports = async (log, success, warn, err, cb) => {
11
11
 
12
12
  if (markets.length < 10 || indicators.length < 10) {
13
13
  err('Not enough results');
14
- throw new Error('Not enough results');
15
14
  }
16
15
 
17
16
  cb();
@@ -5,7 +5,6 @@ module.exports = (log, success, warn, err, cb) => {
5
5
 
6
6
  client.onError((...error) => {
7
7
  err('Client error', ...error);
8
- throw new Error('Client error');
9
8
  });
10
9
 
11
10
  const quoteSession = new client.Session.Quote({
@@ -38,7 +37,6 @@ module.exports = (log, success, warn, err, cb) => {
38
37
  keys.forEach((k) => {
39
38
  if (!rsKeys.includes(k)) {
40
39
  err(`Missing '${k}' key in`, rsKeys);
41
- throw new Error('Missing key');
42
40
  }
43
41
  });
44
42
 
@@ -49,6 +47,5 @@ module.exports = (log, success, warn, err, cb) => {
49
47
 
50
48
  BTC.onError((...error) => {
51
49
  err('BTCEUR ERROR:', error);
52
- throw new Error('Missing key');
53
50
  });
54
51
  };
@@ -5,7 +5,6 @@ module.exports = async (log, success, warn, err, cb) => {
5
5
 
6
6
  client.onError((...error) => {
7
7
  err('Client error', error);
8
- throw new Error('Client error');
9
8
  });
10
9
 
11
10
  const chart = new client.Session.Chart();
@@ -16,7 +15,6 @@ module.exports = async (log, success, warn, err, cb) => {
16
15
 
17
16
  chart.onError((...error) => {
18
17
  err('Chart error', error);
19
- throw new Error('Chart error');
20
18
  });
21
19
 
22
20
  chart.onSymbolLoaded(() => { // When the symbol is successfully loaded
@@ -5,7 +5,6 @@ module.exports = async (log, success, warn, err, cb) => {
5
5
 
6
6
  client.onError((...error) => {
7
7
  err('Client error', error);
8
- throw new Error('Client error');
9
8
  });
10
9
 
11
10
  const chart = new client.Session.Chart();
@@ -15,7 +14,6 @@ module.exports = async (log, success, warn, err, cb) => {
15
14
 
16
15
  chart.onError((...error) => {
17
16
  err('Chart error', error);
18
- throw new Error('Chart error');
19
17
  });
20
18
 
21
19
  TradingView.getIndicator('STD;Supertrend%Strategy').then((indicator) => {
@@ -31,7 +29,6 @@ module.exports = async (log, success, warn, err, cb) => {
31
29
 
32
30
  SuperTrend.onError((...error) => {
33
31
  err('SuperTrend error', error[0]);
34
- throw new Error('SuperTrend error');
35
32
  });
36
33
 
37
34
  let QTY = 10;
@@ -81,7 +78,6 @@ module.exports = async (log, success, warn, err, cb) => {
81
78
 
82
79
  CipherB.onError((...error) => {
83
80
  err('MarketCipher B error:', error[0]);
84
- throw new Error('MarketCipher B error');
85
81
  });
86
82
 
87
83
  CipherB.onUpdate(() => {
@@ -5,7 +5,6 @@ module.exports = async (log, success, warn, err, cb) => {
5
5
 
6
6
  client.onError((...error) => {
7
7
  err('Client error', error);
8
- throw new Error('Client error');
9
8
  });
10
9
 
11
10
  const chart = new client.Session.Chart();
@@ -15,7 +14,6 @@ module.exports = async (log, success, warn, err, cb) => {
15
14
 
16
15
  chart.onError((...error) => {
17
16
  err('Chart error', error);
18
- throw new Error('Chart error');
19
17
  });
20
18
 
21
19
  const volumeProfile = new TradingView.BuiltInIndicator('VbPFixed@tv-basicstudies-139!');
@@ -5,7 +5,6 @@ module.exports = async (log, success, warn, err, cb) => {
5
5
 
6
6
  client.onError((...error) => {
7
7
  err('Client error', error);
8
- throw new Error('Client error');
9
8
  });
10
9
 
11
10
  const chart = new client.Session.Chart();
@@ -17,7 +16,6 @@ module.exports = async (log, success, warn, err, cb) => {
17
16
 
18
17
  chart.onError((...error) => {
19
18
  err('Chart error', error);
20
- throw new Error('Chart error');
21
19
  });
22
20
 
23
21
  let interval = NaN;
@@ -43,7 +41,7 @@ module.exports = async (log, success, warn, err, cb) => {
43
41
  if (Number.isNaN(interval) && times.length >= 2) interval = intrval;
44
42
 
45
43
  if (!Number.isNaN(interval) && interval !== intrval) {
46
- throw new Error(`Wrong interval: ${intrval} (should be ${interval})`);
44
+ err(`Wrong interval: ${intrval} (should be ${interval})`);
47
45
  }
48
46
 
49
47
  log('Next ->', times[0]);
@@ -5,7 +5,6 @@ module.exports = async (log, success, warn, err, cb) => {
5
5
 
6
6
  client.onError((...error) => {
7
7
  err('Client error', error);
8
- throw new Error('Client error');
9
8
  });
10
9
 
11
10
  const chart = new client.Session.Chart();
@@ -17,7 +16,6 @@ module.exports = async (log, success, warn, err, cb) => {
17
16
 
18
17
  chart.onError((...error) => {
19
18
  err('Chart error', error);
20
- throw new Error('Chart error');
21
19
  });
22
20
 
23
21
  let finished = false;
@@ -5,14 +5,12 @@ module.exports = async (log, success, warn, err, cb) => {
5
5
 
6
6
  client.onError((...error) => {
7
7
  err('Client error', error);
8
- throw new Error('Client error');
9
8
  });
10
9
 
11
10
  const chart = new client.Session.Chart();
12
11
 
13
12
  chart.onError((...error) => {
14
13
  err('Chart error', error);
15
- throw new Error('Chart error');
16
14
  });
17
15
 
18
16
  /* (0s) Heikin Ashi chart */
@@ -0,0 +1,95 @@
1
+ /* eslint-disable no-restricted-syntax */
2
+ /* eslint-disable no-await-in-loop */
3
+ const TradingView = require('../main');
4
+
5
+ const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
6
+
7
+ module.exports = async (log, success, warn, err, cb) => {
8
+ log('Getting user info');
9
+
10
+ const userInfos = await TradingView.getUser(process.env.SESSION);
11
+ if (userInfos && userInfos.id) {
12
+ success('User info:', {
13
+ id: userInfos.id,
14
+ username: userInfos.username,
15
+ firstName: userInfos.firstName,
16
+ lastName: userInfos.lastName,
17
+ following: userInfos.following,
18
+ followers: userInfos.followers,
19
+ notifications: userInfos.notifications,
20
+ joinDate: userInfos.joinDate,
21
+ });
22
+ } else err('Get user error. Result:', userInfos);
23
+
24
+ await wait(1000);
25
+
26
+ log('Getting user indicators');
27
+
28
+ const userIndicators = await TradingView.getPrivateIndicators(process.env.SESSION);
29
+ if (userIndicators) {
30
+ if (userIndicators.length === 0) warn('No private indicator found');
31
+ else success('User indicators:', userIndicators.map((i) => i.name));
32
+ } else err('Get indicators error. Result:', userIndicators);
33
+
34
+ await wait(1000);
35
+
36
+ log('Creating logged client');
37
+ const client = new TradingView.Client({
38
+ token: process.env.SESSION,
39
+ });
40
+ client.onError((...error) => {
41
+ err('Client error', error);
42
+ });
43
+
44
+ const chart = new client.Session.Chart();
45
+ chart.onError((...error) => {
46
+ err('Chart error', error);
47
+ });
48
+
49
+ log('Setting market to BINANCE:BTCEUR...');
50
+ chart.setMarket('BINANCE:BTCEUR', { timeframe: 'D' });
51
+
52
+ const checked = new Set();
53
+ async function check(item) {
54
+ checked.add(item);
55
+ log('Checked:', [...checked], `(${checked.size}/${userIndicators.length + 1})`);
56
+ if (checked.size < userIndicators.length + 1) return;
57
+
58
+ log('Closing client...');
59
+ chart.delete();
60
+ await client.end();
61
+ success('Client closed');
62
+ cb();
63
+ }
64
+
65
+ chart.onUpdate(async () => {
66
+ success('Market data:', {
67
+ name: chart.infos.pro_name,
68
+ description: chart.infos.short_description,
69
+ exchange: chart.infos.exchange,
70
+ price: chart.periods[0].close,
71
+ });
72
+
73
+ await check(Symbol.for('PRICE'));
74
+ });
75
+
76
+ await wait(1000);
77
+
78
+ log('Loading indicators...');
79
+
80
+ for (const indic of userIndicators) {
81
+ const privateIndic = await indic.get();
82
+ log(`[${indic.name}] Loading indicator...`);
83
+
84
+ const indicator = new chart.Study(privateIndic);
85
+
86
+ indicator.onReady(() => {
87
+ success(`[${indic.name}] Indicator loaded !`);
88
+ });
89
+
90
+ indicator.onUpdate(async () => {
91
+ success(`[${indic.name}] Last plot:`, indicator.periods[0]);
92
+ await check(indic.id);
93
+ });
94
+ }
95
+ };
File without changes