@mathieuc/tradingview 2.0.5 → 3.0.0-pre

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.
@@ -0,0 +1,63 @@
1
+ const TradingView = require('../main');
2
+
3
+ /*
4
+ This example creates a BTCEUR daily chart
5
+ */
6
+
7
+ const client = new TradingView.Client(); // Creates a websocket client
8
+
9
+ const chart = new client.Session.Chart(); // Init a Chart session
10
+
11
+ chart.setMarket('BINANCE:BTCEUR', { // Set the market
12
+ timeframe: 'D',
13
+ });
14
+
15
+ chart.onError((...err) => { // Listen for errors (can avoid crash)
16
+ console.error('Chart error:', ...err);
17
+ // Do something...
18
+ });
19
+
20
+ chart.onSymbolLoaded(() => { // When the symbol is successfully loaded
21
+ console.log(`Market "${chart.infos.description}" loaded !`);
22
+ });
23
+
24
+ chart.onUpdate(() => { // When price changes
25
+ if (!chart.periods[0]) return;
26
+ console.log(`[${chart.infos.description}]: ${chart.periods[0].close} ${chart.infos.currency_id}`);
27
+ // Do something...
28
+ });
29
+
30
+ // Wait 5 seconds and set the market to BINANCE:ETHEUR
31
+ setTimeout(() => {
32
+ console.log('\nSetting market to BINANCE:ETHEUR...');
33
+ chart.setMarket('BINANCE:ETHEUR', {
34
+ timeframe: 'D',
35
+ });
36
+ }, 5000);
37
+
38
+ // Wait 10 seconds and set the timeframe to 15 minutes
39
+ setTimeout(() => {
40
+ console.log('\nSetting timeframe to 15 minutes...');
41
+ chart.setSeries('15');
42
+ }, 10000);
43
+
44
+ // Wait 15 seconds and set the chart type to "Heikin Ashi"
45
+ setTimeout(() => {
46
+ console.log('\nSetting the chart type to "Heikin Ashi"s...');
47
+ chart.setMarket('BINANCE:ETHEUR', {
48
+ timeframe: 'D',
49
+ type: 'HeikinAshi',
50
+ });
51
+ }, 15000);
52
+
53
+ // Wait 20 seconds and close the chart
54
+ setTimeout(() => {
55
+ console.log('\nClosing the chart...');
56
+ chart.delete();
57
+ }, 20000);
58
+
59
+ // Wait 25 seconds and close the client
60
+ setTimeout(() => {
61
+ console.log('\nClosing the client...');
62
+ client.end();
63
+ }, 25000);
@@ -0,0 +1,41 @@
1
+ const TradingView = require('../main');
2
+
3
+ if (!process.argv[2]) throw Error('Please specify your \'sessionid\' cookie');
4
+
5
+ const client = new TradingView.Client({
6
+ token: process.argv[2],
7
+ });
8
+
9
+ const chart = new client.Session.Chart();
10
+ chart.setMarket('NASDAQ:AAPL', {
11
+ type: 'Renko',
12
+ timeframe: '1',
13
+ inputs: {
14
+ source: 'close',
15
+ sources: 'Close',
16
+ boxSize: 3,
17
+ style: 'Traditional',
18
+ atrLength: 14,
19
+ wicks: true,
20
+ },
21
+ });
22
+
23
+ TradingView.searchIndicator('CM_Ultimate_MA_MTF_V2').then(async (indicator) => {
24
+ console.log(
25
+ '*** Indicator information\n',
26
+ indicator[0].id,
27
+ );
28
+ const pineIndicator = await indicator[0].get();
29
+ const study = new chart.Study(pineIndicator);
30
+ study.onUpdate(() => {
31
+ console.log(
32
+ '\n*** Chart time and close:\n',
33
+ chart.periods[0].time,
34
+ chart.periods[0].close,
35
+ );
36
+ console.log(
37
+ '\n*** Indicator values:\n',
38
+ study.periods[0].MultiTimeframe_Moving_Avg,
39
+ );
40
+ });
41
+ });
package/main.js CHANGED
@@ -1,337 +1,9 @@
1
- const WebSocket = require('ws');
2
- const {
3
- search, getScreener, getTA, getIndicator, getUserToken,
4
- } = require('./miscRequests');
5
-
6
- let onPacket = () => null;
7
-
8
- function parse(str) {
9
- const packets = str.replace(/~h~/g, '').split(/~m~[0-9]{1,}~m~/g).map((p) => {
10
- if (!p) return false;
11
- try {
12
- return JSON.parse(p);
13
- } catch (error) {
14
- console.warn('Cant parse', p);
15
- return false;
16
- }
17
- }).filter((p) => p);
18
-
19
- packets.forEach((packet) => {
20
- if (packet.m === 'protocol_error') {
21
- return onPacket({
22
- type: 'error',
23
- syntax: packet.p[0],
24
- });
25
- }
26
-
27
- if (packet.m && packet.p) {
28
- return onPacket({
29
- type: packet.m,
30
- session: packet.p[0],
31
- data: packet.p[1],
32
- });
33
- }
34
-
35
- if (typeof packet === 'number') return onPacket({ type: 'ping', ping: packet });
36
-
37
- return onPacket({ type: 'info', ...packet });
38
- });
39
- }
40
-
41
- function genSession() {
42
- let r = '';
43
- const c = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
44
- for (let i = 0; i < 12; i += 1) r += c.charAt(Math.floor(Math.random() * c.length));
45
- return `qs_${r}`;
46
- }
47
-
48
- module.exports = (autoInit = true) => {
49
- const callbacks = {
50
- connected: [],
51
- disconnected: [],
52
- logged: [],
53
- subscribed: [],
54
- ping: [],
55
- price: [],
56
- data: [],
57
-
58
- error: [],
59
- event: [],
60
- };
61
-
62
- const chartEventNames = [
63
- 'du', 'timescale_update',
64
- 'series_loading', 'series_completed', 'series_error',
65
- 'symbol_resolved', 'symbol_error',
66
- 'study_loading', 'study_error',
67
- ];
68
- const chartCBs = {};
69
-
70
- function handleEvent(ev, ...data) {
71
- callbacks[ev].forEach((e) => e(...data));
72
- callbacks.event.forEach((e) => e(ev, ...data));
73
- }
74
-
75
- function handleError(...msgs) {
76
- if (callbacks.error.length === 0) console.error(...msgs);
77
- else handleEvent('error', ...msgs);
78
- }
79
-
80
- const ws = new WebSocket('wss://widgetdata.tradingview.com/socket.io/websocket', {
81
- origin: 'https://s.tradingview.com',
82
- });
83
-
84
- let logged = false;
85
- /** ID of the quote session */
86
- let sessionId = '';
87
-
88
- /** List of subscribed symbols */
89
- let subscribed = [];
90
-
91
- /**
92
- * Send a custom packet
93
- * @param {string} t Packet type
94
- * @param {string[]} p Packet data
95
- * @example
96
- * // Subscribe manualy to BTCEUR
97
- * send('quote_add_symbols', [sessionId, 'BTCEUR']);
98
- */
99
- function send(t, p = []) {
100
- if (!sessionId) return;
101
-
102
- const msg = JSON.stringify({ m: t, p });
103
- ws.send(`~m~${msg.length}~m~${msg}`);
104
- }
105
-
106
- ws.on('open', () => {
107
- sessionId = genSession();
108
- handleEvent('connected');
109
- });
110
-
111
- ws.on('close', () => {
112
- logged = false;
113
- sessionId = '';
114
- handleEvent('disconnected');
115
- });
116
-
117
- ws.on('message', parse);
118
-
119
- onPacket = (packet) => {
120
- if (packet.type === 'ping') {
121
- const pingStr = `~h~${packet.ping}`;
122
- ws.send(`~m~${pingStr.length}~m~${pingStr}`);
123
- handleEvent('ping', packet.ping);
124
- return;
125
- }
126
-
127
- if (packet.type === 'quote_completed' && packet.data) {
128
- handleEvent('subscribed', packet.data);
129
- return;
130
- }
131
-
132
- if (packet.type === 'qsd' && packet.data.n && packet.data.v.lp) {
133
- handleEvent('price', {
134
- symbol: packet.data.n,
135
- price: packet.data.v.lp,
136
- });
137
-
138
- return;
139
- }
140
-
141
- if (chartEventNames.includes(packet.type) && chartCBs[packet.session]) {
142
- chartCBs[packet.session](packet);
143
- return;
144
- }
145
-
146
- if (!logged && packet.type === 'info') {
147
- if (autoInit) {
148
- send('set_auth_token', ['unauthorized_user_token']);
149
- send('quote_create_session', [sessionId]);
150
- send('quote_set_fields', [sessionId, 'lp']);
151
-
152
- subscribed.forEach((symbol) => send('quote_add_symbols', [sessionId, symbol]));
153
- }
154
-
155
- handleEvent('logged', packet);
156
- return;
157
- }
158
-
159
- if (packet.type === 'error') {
160
- handleError(`Market API critical error: ${packet.syntax}`);
161
- ws.close();
162
- return;
163
- }
164
-
165
- handleEvent('data', packet);
166
- };
167
-
168
- return {
169
- /** Event listener
170
- * @param { 'connected' | 'disconnected' | 'logged'
171
- * | 'subscribed' | 'price' | 'data' | 'error' | 'ping' } event Event
172
- * @param {(...data: object) => null} cb Callback
173
- */
174
- on(event, cb) {
175
- if (!callbacks[event]) {
176
- console.log('Wrong event:', event);
177
- console.log('Available events:', Object.keys(callbacks));
178
- return;
179
- }
180
-
181
- callbacks[event].push(cb);
182
- },
183
-
184
- /**
185
- * Close the websocket connection
186
- * @param {string} symbol Market symbol (Example: BTCEUR or COINBASE:BTCEUR)
187
- */
188
- end() { ws.close(); },
189
-
190
- search,
191
- getScreener,
192
- getTA,
193
- subscribed,
194
-
195
- /**
196
- * Unsubscribe to a market
197
- * @param {string} symbol Market symbol (Example: BTCEUR or COINBASE:BTCEUR)
198
- */
199
- subscribe(symbol) {
200
- if (subscribed.includes(symbol)) return;
201
- send('quote_add_symbols', [sessionId, symbol]);
202
- subscribed.push(symbol);
203
- },
204
-
205
- /**
206
- * Unsubscribe from a market
207
- * @param {string} symbol Market symbol (Example: BTCEUR or COINBASE:BTCEUR)
208
- */
209
- unsubscribe(symbol) {
210
- if (!subscribed.includes(symbol)) return;
211
- send('quote_remove_symbols', [sessionId, symbol]);
212
- subscribed = subscribed.filter((s) => s !== symbol);
213
- },
214
-
215
- /**
216
- * @typedef {Object} IndicatorInfos Indicator infos
217
- * @property {string} id ID of the indicator (Like: XXX;XXXXXXXXXXXXXXXXXXXXX)
218
- * @property {string} version Wanted version of the indicator
219
- * @property {(string | number | boolean | null)[]} [settings] Indicator settings value
220
- *
221
- * @typedef {Object} ChartInfos
222
- * @property {string} [session] User 'sessionid' cookie
223
- * @property {string} symbol Market symbol (Example: BTCEUR or COINBASE:BTCEUR)
224
- * @property { '1' | '3' | '5' | '15' | '30' | '45'
225
- * | '60' | '120' | '180' | '240'
226
- * | '1D' | '1W' | '1M'
227
- * } [period] Period
228
- * @property {number} [range] Number of loaded periods
229
- * @property {string} [timezone] Timezone in 'Europe/Paris' format
230
- * @property {IndicatorInfos[]} [indicators] List of indicators
231
- */
232
-
233
- /**
234
- * @typedef {Object} Period List of prices / indicator values
235
- * @property {number} $time
236
- * @property {{
237
- * time: number, open: number, close: number,
238
- * max: number, min: number, change: number,
239
- * }} $prices
240
- */
241
-
242
- /**
243
- * Init a chart instance
244
- * @param {ChartInfos} chart
245
- * @param {{(prices: Period[]): null}} onUpdate List of periods starting from the lastest
246
- */
247
- async initChart(chart, onUpdate) {
248
- const chartSession = genSession();
249
- const periods = [];
250
- const indicators = await Promise.all(
251
- (chart.indicators || []).map((i) => getIndicator(i.id, i.version, i.settings)),
252
- );
253
-
254
- function updatePeriods(packet) {
255
- const newData = packet.data;
256
-
257
- Object.keys(newData).forEach((type) => {
258
- (newData[type].s || newData[type].st || []).forEach((p) => {
259
- if (!periods[p.i]) periods[p.i] = {};
260
-
261
- if (newData[type].s) {
262
- [periods[p.i].$time] = p.v;
263
-
264
- periods[p.i][type] = {
265
- open: p.v[1],
266
- close: p.v[4],
267
- max: p.v[2],
268
- min: p.v[3],
269
- change: Math.round(p.v[5] * 100) / 100,
270
- };
271
- }
272
-
273
- if (newData[type].st) {
274
- const period = {};
275
- const indicator = indicators[parseInt(type, 10)];
276
-
277
- p.v.forEach((val, i) => {
278
- if (i === 0) return;
279
- if (indicator.plots[`plot_${i - 1}`]) period[indicator.plots[`plot_${i - 1}`]] = val;
280
- else period[`_plot_${i - 1}`] = val;
281
- });
282
- periods[p.i][chart.indicators[parseInt(type, 10)].name || `st${type}`] = period;
283
- }
284
- });
285
- });
286
- }
287
-
288
- chartCBs[chartSession] = (packet) => {
289
- if (['timescale_update', 'du'].includes(packet.type)) {
290
- updatePeriods(packet);
291
- onUpdate([...periods].reverse());
292
- return;
293
- }
294
-
295
- if (packet.type.endsWith('_error')) {
296
- handleError(`Error on '${chart.symbol}' (${chartSession}) chart: "${packet.type}"`);
297
- }
298
- };
299
-
300
- if (chart.session) send('set_auth_token', [await getUserToken(chart.session)]);
301
- send('chart_create_session', [chartSession, '']);
302
- if (chart.timezone) send('switch_timezone', [chartSession, chart.timezone]);
303
- send('resolve_symbol', [chartSession, 'sds_sym_1', `={"symbol":"${chart.symbol || 'BTCEUR'}","adjustment":"splits"}`]);
304
- send('create_series', [chartSession, '$prices', 's1', 'sds_sym_1', (chart.period || '240'), (chart.range || 100), '']);
305
-
306
- indicators.forEach(async (indicator, i) => {
307
- const pineInfos = {
308
- pineId: indicator.pineId,
309
- pineVersion: indicator.pineVersion,
310
- text: indicator.script,
311
- };
312
-
313
- Object.keys(indicator.inputs).forEach((inputID, inp) => {
314
- const input = indicator.inputs[inputID];
315
- if (input.type === 'bool' && typeof input.value !== 'boolean') handleError(`Input '${input.name}' (${inp}) must be a boolean !`);
316
- if (input.type === 'integer' && typeof input.value !== 'number') handleError(`Input '${input.name}' (${inp}) must be a number !`);
317
- if (input.type === 'float' && typeof input.value !== 'number') handleError(`Input '${input.name}' (${inp}) must be a number !`);
318
- if (input.type === 'text' && typeof input.value !== 'string') handleError(`Input '${input.name}' (${inp}) must be a string !`);
319
- if (input.options && !input.options.includes(input.value)) {
320
- handleError(`Input '${input.name}' (${inp}) must be one of these values:`, input.options);
321
- }
322
-
323
- pineInfos[inputID] = {
324
- v: input.value,
325
- f: input.isFake,
326
- t: input.type,
327
- };
328
- });
329
-
330
- send('create_study', [chartSession, `${i}`, 'st1', '$prices', 'Script@tv-scripting-101!', pineInfos]);
331
- });
332
- },
333
-
334
- send,
335
- sessionId,
336
- };
337
- };
1
+ const miscRequests = require('./src/miscRequests');
2
+ const Client = require('./src/client');
3
+ const BuiltInIndicator = require('./src/classes/BuiltInIndicator');
4
+ const PineIndicator = require('./src/classes/PineIndicator');
5
+
6
+ module.exports = { ...miscRequests };
7
+ module.exports.Client = Client;
8
+ module.exports.BuiltInIndicator = BuiltInIndicator;
9
+ module.exports.PineIndicator = PineIndicator;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mathieuc/tradingview",
3
- "version": "2.0.5",
3
+ "version": "3.0.0-pre",
4
4
  "description": "Tradingview instant stocks API, indicator alerts, trading bot, and more !",
5
5
  "main": "main.js",
6
6
  "scripts": {
@@ -11,6 +11,10 @@
11
11
  "test-strat-alerts": "node ./tests/alerts/strategyAlert.js",
12
12
  "test": "npm run test-prices"
13
13
  },
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/Mathieu2301/TradingView-API.git"
17
+ },
14
18
  "keywords": [
15
19
  "tradingwiew",
16
20
  "market",
@@ -24,11 +28,15 @@
24
28
  "author": "Mathieu Colmon",
25
29
  "license": "ISC",
26
30
  "dependencies": {
31
+ "jszip": "^3.7.1",
27
32
  "ws": "^7.4.3"
28
33
  },
29
34
  "devDependencies": {
35
+ "@babel/eslint-parser": "^7.15.7",
36
+ "@mathieuc/console": "^1.0.1",
30
37
  "eslint": "^7.25.0",
31
38
  "eslint-config-airbnb-base": "^14.2.1",
32
- "eslint-plugin-import": "^2.22.1"
39
+ "eslint-plugin-import": "^2.22.1",
40
+ "jsdoc-to-markdown": "^7.1.0"
33
41
  }
34
42
  }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * @typedef {Object} GraphicLabel
3
+ * @prop {number} id Drawing ID
4
+ */
5
+
6
+ /**
7
+ * @typedef {Object} GraphicLine
8
+ * @prop {number} id Drawing ID
9
+ */
10
+
11
+ /**
12
+ * @typedef {Object} GraphicBox
13
+ * @prop {number} id Drawing ID
14
+ */
15
+
16
+ /**
17
+ * @typedef {Object} GraphicTable
18
+ * @prop {number} id Drawing ID
19
+ */
20
+
21
+ /**
22
+ * @typedef {Object} GraphicHorizline
23
+ * @prop {number} id Drawing ID
24
+ * @prop {number} level Y position of the line
25
+ * @prop {number} startIndex Start index of the line (`chart.periods[line.startIndex]`)
26
+ * @prop {number} endIndex End index of the line (`chart.periods[line.endIndex]`)
27
+ * @prop {boolean} extendRight Is the line extended to the right
28
+ * @prop {boolean} extendLeft Is the line extended to the left
29
+ */
30
+
31
+ /**
32
+ * @typedef {Object} GraphicPoint
33
+ * @prop {number} index X position of the point
34
+ * @prop {number} level Y position of the point
35
+ */
36
+
37
+ /**
38
+ * @typedef {Object} GraphicPolygon
39
+ * @prop {number} id Drawing ID
40
+ * @prop {GraphicPoint[]} points List of polygon points
41
+ */
42
+
43
+ /**
44
+ * @typedef {Object} GraphicHorizHist
45
+ * @prop {number} id Drawing ID
46
+ * @prop {number} priceLow Low Y position
47
+ * @prop {number} priceHigh High Y position
48
+ * @prop {number} firstBarTime First X position
49
+ * @prop {number} lastBarTime Last X position
50
+ * @prop {number[]} rate List of values
51
+ */
52
+
53
+ /**
54
+ * @typedef {Object} GraphicData List of drawings indexed by type
55
+ * @prop {GraphicLabel[]} labels List of labels drawings
56
+ * @prop {GraphicLine[]} lines List of lines drawings
57
+ * @prop {GraphicBox[]} boxes List of boxes drawings
58
+ * @prop {GraphicTable[]} tables List of tables drawings
59
+ * @prop {GraphicPolygon[]} polygons List of polygons drawings
60
+ * @prop {GraphicHorizHist[]} horizHists List of horizontal histograms drawings
61
+ * @prop {GraphicHorizline[]} horizLines List of horizontal lines drawings
62
+ */
63
+
64
+ /**
65
+ * @param {Object} rawGraphic Raw graphic data
66
+ * @param {Object} indexes Drawings xPos indexes
67
+ * @returns {GraphicData}
68
+ */
69
+ module.exports = function graphicParse(rawGraphic = {}, indexes = []) {
70
+ // console.log('indexes', indexes);
71
+ return {
72
+ labels: Object.values(rawGraphic.dwglabels ?? {}).map((l) => ({
73
+ ...l,
74
+ })),
75
+
76
+ lines: Object.values(rawGraphic.dwglines ?? {}).map((l) => ({
77
+ ...l,
78
+ })),
79
+
80
+ boxes: Object.values(rawGraphic.dwgboxes ?? {}).map((b) => ({
81
+ ...b,
82
+ })),
83
+
84
+ tables: Object.values(rawGraphic.dwgtables ?? {}).map((t) => ({
85
+ ...t,
86
+ })),
87
+
88
+ horizLines: Object.values(rawGraphic.horizlines ?? {}).map((h) => ({
89
+ ...h,
90
+ startIndex: indexes[h.startIndex],
91
+ endIndex: indexes[h.endIndex],
92
+ })),
93
+
94
+ polygons: Object.values(rawGraphic.polygons ?? {}).map((p) => ({
95
+ ...p,
96
+ points: p.points.map((pt) => ({
97
+ ...pt,
98
+ index: indexes[pt.index],
99
+ })),
100
+ })),
101
+
102
+ horizHists: Object.values(rawGraphic.hhists ?? {}).map((h) => ({
103
+ ...h,
104
+ firstBarTime: indexes[h.firstBarTime],
105
+ lastBarTime: indexes[h.lastBarTime],
106
+ })),
107
+
108
+ raw: () => rawGraphic,
109
+ };
110
+ };