@mathieuc/tradingview 3.0.1 → 3.2.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/README.md +15 -2
- package/examples/AllPrivateIndicators.js +4 -4
- package/examples/CustomChartType.js +1 -0
- package/examples/Errors.js +147 -153
- package/examples/FakeReplayMode.js +41 -0
- package/examples/PinePermManage.js +57 -0
- package/examples/ReplayMode.js +97 -0
- package/main.js +2 -0
- package/package.json +5 -7
- package/src/FormData.js +35 -0
- package/src/chart/session.js +190 -16
- package/src/classes/PinePermManager.js +142 -0
- package/src/client.js +1 -1
- package/src/miscRequests.js +5 -33
- package/src/quote/session.js +1 -0
- package/src/request.js +37 -0
- package/test.js +56 -147
- package/tests/1. Search.js +18 -0
- package/tests/2. QuoteSession.js +54 -0
- package/tests/3. SimpleChart.js +61 -0
- package/tests/4. Indicators.js +99 -0
- package/tests/5. BuiltInIndicator.js +41 -0
- package/tests/6. FakeReplayMode.js +59 -0
- package/tests/7. ReplayMode.js +51 -0
- package/tests/8. CustomChartTypes.js +115 -0
- package/tests/9. AllErrors.js +146 -0
- package/tests/disabled/x. Template.js +24 -0
- package/.github/FUNDING.yml +0 -1
- package/tests/alerts/discordWH.js +0 -76
- package/tests/alerts/drawings.js +0 -41
- package/tests/alerts/envLoader.js +0 -11
- package/tests/alerts/manualAlert.js +0 -77
- package/tests/alerts/sentNotifs.js +0 -16
- package/tests/alerts/strategyAlert.js +0 -94
- package/tests/analysis.js +0 -13
- package/tests/indicator.js +0 -42
- package/tests/prices.js +0 -33
- package/tests/strategy.js +0 -56
package/src/chart/session.js
CHANGED
|
@@ -113,7 +113,14 @@ const ChartTypes = {
|
|
|
113
113
|
* @param {import('../client').ClientBridge} client
|
|
114
114
|
*/
|
|
115
115
|
module.exports = (client) => class ChartSession {
|
|
116
|
-
#
|
|
116
|
+
#chartSessionID = genSessionID('cs');
|
|
117
|
+
|
|
118
|
+
#replaySessionID = genSessionID('rs');
|
|
119
|
+
|
|
120
|
+
#replayMode = false;
|
|
121
|
+
|
|
122
|
+
/** @type {Object<string, (): any>} */
|
|
123
|
+
#replayOKCB = {}
|
|
117
124
|
|
|
118
125
|
/** Parent client */
|
|
119
126
|
#client = client;
|
|
@@ -148,6 +155,11 @@ module.exports = (client) => class ChartSession {
|
|
|
148
155
|
symbolLoaded: [],
|
|
149
156
|
update: [],
|
|
150
157
|
|
|
158
|
+
replayLoaded: [],
|
|
159
|
+
replayPoint: [],
|
|
160
|
+
replayResolution: [],
|
|
161
|
+
replayEnd: [],
|
|
162
|
+
|
|
151
163
|
event: [],
|
|
152
164
|
error: [],
|
|
153
165
|
};
|
|
@@ -167,7 +179,7 @@ module.exports = (client) => class ChartSession {
|
|
|
167
179
|
}
|
|
168
180
|
|
|
169
181
|
constructor() {
|
|
170
|
-
this.#client.sessions[this.#
|
|
182
|
+
this.#client.sessions[this.#chartSessionID] = {
|
|
171
183
|
type: 'chart',
|
|
172
184
|
onData: (packet) => {
|
|
173
185
|
if (global.TW_DEBUG) console.log('§90§30§106 CHART SESSION §0 DATA', packet);
|
|
@@ -188,7 +200,10 @@ module.exports = (client) => class ChartSession {
|
|
|
188
200
|
}
|
|
189
201
|
|
|
190
202
|
if (['timescale_update', 'du'].includes(packet.type)) {
|
|
203
|
+
const changes = [];
|
|
204
|
+
|
|
191
205
|
Object.keys(packet.data[1]).forEach((k) => {
|
|
206
|
+
changes.push(k);
|
|
192
207
|
if (k === '$prices') {
|
|
193
208
|
const periods = packet.data[1].$prices;
|
|
194
209
|
if (!periods || !periods.s) return;
|
|
@@ -211,7 +226,7 @@ module.exports = (client) => class ChartSession {
|
|
|
211
226
|
if (this.#studyListeners[k]) this.#studyListeners[k](packet);
|
|
212
227
|
});
|
|
213
228
|
|
|
214
|
-
this.#handleEvent('update');
|
|
229
|
+
this.#handleEvent('update', changes);
|
|
215
230
|
return;
|
|
216
231
|
}
|
|
217
232
|
|
|
@@ -232,7 +247,47 @@ module.exports = (client) => class ChartSession {
|
|
|
232
247
|
},
|
|
233
248
|
};
|
|
234
249
|
|
|
235
|
-
this.#client.
|
|
250
|
+
this.#client.sessions[this.#replaySessionID] = {
|
|
251
|
+
type: 'replay',
|
|
252
|
+
onData: (packet) => {
|
|
253
|
+
if (global.TW_DEBUG) console.log('§90§30§106 REPLAY SESSION §0 DATA', packet);
|
|
254
|
+
|
|
255
|
+
if (packet.type === 'replay_ok') {
|
|
256
|
+
if (this.#replayOKCB[packet.data[1]]) {
|
|
257
|
+
this.#replayOKCB[packet.data[1]]();
|
|
258
|
+
delete this.#replayOKCB[packet.data[1]];
|
|
259
|
+
}
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (packet.type === 'replay_instance_id') {
|
|
264
|
+
this.#handleEvent('replayLoaded', packet.data[1]);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (packet.type === 'replay_point') {
|
|
269
|
+
this.#handleEvent('replayPoint', packet.data[1]);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (packet.type === 'replay_resolutions') {
|
|
274
|
+
this.#handleEvent('replayResolution', packet.data[1], packet.data[2]);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (packet.type === 'replay_data_end') {
|
|
279
|
+
this.#handleEvent('replayEnd');
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (packet.type === 'critical_error') {
|
|
284
|
+
const [, name, description] = packet.data;
|
|
285
|
+
this.#handleError('Critical error:', name, description);
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
this.#client.send('chart_create_session', [this.#chartSessionID]);
|
|
236
291
|
}
|
|
237
292
|
|
|
238
293
|
#seriesCreated = false;
|
|
@@ -255,7 +310,7 @@ module.exports = (client) => class ChartSession {
|
|
|
255
310
|
this.#periods = {};
|
|
256
311
|
|
|
257
312
|
this.#client.send(`${this.#seriesCreated ? 'modify' : 'create'}_series`, [
|
|
258
|
-
this.#
|
|
313
|
+
this.#chartSessionID,
|
|
259
314
|
'$prices',
|
|
260
315
|
's1',
|
|
261
316
|
`ser_${this.#currentSeries}`,
|
|
@@ -278,31 +333,56 @@ module.exports = (client) => class ChartSession {
|
|
|
278
333
|
* @param {'EUR' | 'USD' | string} [options.currency] Chart currency
|
|
279
334
|
* @param {ChartType} [options.type] Chart custom type
|
|
280
335
|
* @param {ChartInputs} [options.inputs] Chart custom inputs
|
|
336
|
+
* @param {number} [options.replay] Replay mode starting point (Timestamp)
|
|
281
337
|
*/
|
|
282
338
|
setMarket(symbol, options = {}) {
|
|
283
339
|
this.#periods = {};
|
|
284
340
|
|
|
341
|
+
if (this.#replayMode) {
|
|
342
|
+
this.#replayMode = false;
|
|
343
|
+
this.#client.send('replay_delete_session', [this.#replaySessionID]);
|
|
344
|
+
}
|
|
345
|
+
|
|
285
346
|
const symbolInit = {
|
|
286
347
|
symbol: symbol || 'BTCEUR',
|
|
287
348
|
adjustment: options.adjustment || 'splits',
|
|
288
349
|
};
|
|
289
350
|
|
|
290
351
|
if (options.session) symbolInit.session = options.session;
|
|
291
|
-
|
|
292
352
|
if (options.currency) symbolInit['currency-id'] = options.currency;
|
|
293
353
|
|
|
294
|
-
|
|
354
|
+
if (options.replay) {
|
|
355
|
+
this.#replayMode = true;
|
|
356
|
+
this.#client.send('replay_create_session', [this.#replaySessionID]);
|
|
357
|
+
|
|
358
|
+
this.#client.send('replay_add_series', [
|
|
359
|
+
this.#replaySessionID,
|
|
360
|
+
'req_replay_addseries',
|
|
361
|
+
`=${JSON.stringify(symbolInit)}`,
|
|
362
|
+
options.timeframe,
|
|
363
|
+
]);
|
|
364
|
+
|
|
365
|
+
this.#client.send('replay_reset', [
|
|
366
|
+
this.#replaySessionID,
|
|
367
|
+
'req_replay_reset',
|
|
368
|
+
options.replay,
|
|
369
|
+
]);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const complex = options.type || options.replay;
|
|
373
|
+
const chartInit = complex ? {} : symbolInit;
|
|
295
374
|
|
|
296
|
-
if (
|
|
375
|
+
if (complex) {
|
|
376
|
+
if (options.replay) chartInit.replay = this.#replaySessionID;
|
|
297
377
|
chartInit.symbol = symbolInit;
|
|
298
378
|
chartInit.type = ChartTypes[options.type];
|
|
299
|
-
chartInit.inputs = { ...options.inputs };
|
|
379
|
+
if (options.type) chartInit.inputs = { ...options.inputs };
|
|
300
380
|
}
|
|
301
381
|
|
|
302
382
|
this.#currentSeries += 1;
|
|
303
383
|
|
|
304
384
|
this.#client.send('resolve_symbol', [
|
|
305
|
-
this.#
|
|
385
|
+
this.#chartSessionID,
|
|
306
386
|
`ser_${this.#currentSeries}`,
|
|
307
387
|
`=${JSON.stringify(chartInit)}`,
|
|
308
388
|
]);
|
|
@@ -316,7 +396,7 @@ module.exports = (client) => class ChartSession {
|
|
|
316
396
|
*/
|
|
317
397
|
setTimezone(timezone) {
|
|
318
398
|
this.#periods = {};
|
|
319
|
-
this.#client.send('switch_timezone', [this.#
|
|
399
|
+
this.#client.send('switch_timezone', [this.#chartSessionID, timezone]);
|
|
320
400
|
}
|
|
321
401
|
|
|
322
402
|
/**
|
|
@@ -324,7 +404,60 @@ module.exports = (client) => class ChartSession {
|
|
|
324
404
|
* @param {number} number Number of additional periods/candles you want to fetch
|
|
325
405
|
*/
|
|
326
406
|
fetchMore(number = 1) {
|
|
327
|
-
this.#client.send('request_more_data', [this.#
|
|
407
|
+
this.#client.send('request_more_data', [this.#chartSessionID, '$prices', number]);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Fetch x additional previous periods/candles values
|
|
412
|
+
* @param {number} number Number of additional periods/candles you want to fetch
|
|
413
|
+
* @returns {Promise} Raise when the data has been fetched
|
|
414
|
+
*/
|
|
415
|
+
replayStep(number = 1) {
|
|
416
|
+
return new Promise((cb) => {
|
|
417
|
+
if (!this.#replayMode) {
|
|
418
|
+
this.#handleError('No replay session');
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const reqID = genSessionID('rsq_step');
|
|
423
|
+
this.#client.send('replay_step', [this.#replaySessionID, reqID, number]);
|
|
424
|
+
this.#replayOKCB[reqID] = () => { cb(); };
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Start fetching a new period/candle every x ms
|
|
430
|
+
* @param {number} interval Number of additional periods/candles you want to fetch
|
|
431
|
+
* @returns {Promise} Raise when the replay mode starts
|
|
432
|
+
*/
|
|
433
|
+
replayStart(interval = 1000) {
|
|
434
|
+
return new Promise((cb) => {
|
|
435
|
+
if (!this.#replayMode) {
|
|
436
|
+
this.#handleError('No replay session');
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const reqID = genSessionID('rsq_start');
|
|
441
|
+
this.#client.send('replay_start', [this.#replaySessionID, reqID, interval]);
|
|
442
|
+
this.#replayOKCB[reqID] = () => { cb(); };
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Stop fetching a new period/candle every x ms
|
|
448
|
+
* @returns {Promise} Raise when the replay mode stops
|
|
449
|
+
*/
|
|
450
|
+
replayStop() {
|
|
451
|
+
return new Promise((cb) => {
|
|
452
|
+
if (!this.#replayMode) {
|
|
453
|
+
this.#handleError('No replay session');
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const reqID = genSessionID('rsq_stop');
|
|
458
|
+
this.#client.send('replay_stop', [this.#replaySessionID, reqID]);
|
|
459
|
+
this.#replayOKCB[reqID] = () => { cb(); };
|
|
460
|
+
});
|
|
328
461
|
}
|
|
329
462
|
|
|
330
463
|
/**
|
|
@@ -338,13 +471,52 @@ module.exports = (client) => class ChartSession {
|
|
|
338
471
|
|
|
339
472
|
/**
|
|
340
473
|
* When a chart update happens
|
|
341
|
-
* @param {() => void} cb
|
|
474
|
+
* @param {(changes: ('$prices' | string)[]) => void} cb
|
|
342
475
|
* @event
|
|
343
476
|
*/
|
|
344
477
|
onUpdate(cb) {
|
|
345
478
|
this.#callbacks.update.push(cb);
|
|
346
479
|
}
|
|
347
480
|
|
|
481
|
+
/**
|
|
482
|
+
* When the replay session is ready
|
|
483
|
+
* @param {() => void} cb
|
|
484
|
+
* @event
|
|
485
|
+
*/
|
|
486
|
+
onReplayLoaded(cb) {
|
|
487
|
+
this.#callbacks.replayLoaded.push(cb);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* When the replay session has new resolution
|
|
492
|
+
* @param {(
|
|
493
|
+
* timeframe: import('../types').TimeFrame,
|
|
494
|
+
* index: number,
|
|
495
|
+
* ) => void} cb
|
|
496
|
+
* @event
|
|
497
|
+
*/
|
|
498
|
+
onReplayResolution(cb) {
|
|
499
|
+
this.#callbacks.replayResolution.push(cb);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* When the replay session ends
|
|
504
|
+
* @param {() => void} cb
|
|
505
|
+
* @event
|
|
506
|
+
*/
|
|
507
|
+
onReplayEnd(cb) {
|
|
508
|
+
this.#callbacks.replayEnd.push(cb);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* When the replay session cursor has moved
|
|
513
|
+
* @param {(index: number) => void} cb
|
|
514
|
+
* @event
|
|
515
|
+
*/
|
|
516
|
+
onReplayPoint(cb) {
|
|
517
|
+
this.#callbacks.replayPoint.push(cb);
|
|
518
|
+
}
|
|
519
|
+
|
|
348
520
|
/**
|
|
349
521
|
* When chart error happens
|
|
350
522
|
* @param {(...any) => void} cb Callback
|
|
@@ -356,7 +528,7 @@ module.exports = (client) => class ChartSession {
|
|
|
356
528
|
|
|
357
529
|
/** @type {ChartSessionBridge} */
|
|
358
530
|
#chartSession = {
|
|
359
|
-
sessionID: this.#
|
|
531
|
+
sessionID: this.#chartSessionID,
|
|
360
532
|
studyListeners: this.#studyListeners,
|
|
361
533
|
indexes: {},
|
|
362
534
|
send: (t, p) => this.#client.send(t, p),
|
|
@@ -366,7 +538,9 @@ module.exports = (client) => class ChartSession {
|
|
|
366
538
|
|
|
367
539
|
/** Delete the chart session */
|
|
368
540
|
delete() {
|
|
369
|
-
this.#client.send('
|
|
370
|
-
|
|
541
|
+
if (this.#replayMode) this.#client.send('replay_delete_session', [this.#replaySessionID]);
|
|
542
|
+
this.#client.send('chart_delete_session', [this.#chartSessionID]);
|
|
543
|
+
delete this.#client.sessions[this.#chartSessionID];
|
|
544
|
+
this.#replayMode = false;
|
|
371
545
|
}
|
|
372
546
|
};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
const request = require('../request');
|
|
2
|
+
const FormData = require('../FormData');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {Object} AuthorizationUser
|
|
6
|
+
* @prop {id} id User id
|
|
7
|
+
* @prop {string} username User's username
|
|
8
|
+
* @prop {string} userpic User's profile picture URL
|
|
9
|
+
* @prop {string} expiration Authorization expiration date
|
|
10
|
+
* @prop {string} created Authorization creation date
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
class PinePermManager {
|
|
14
|
+
sessionId;
|
|
15
|
+
|
|
16
|
+
pineId;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Creates a PinePermManager instance
|
|
20
|
+
* @param {string} sessionId Token from sessionid cookie
|
|
21
|
+
* @param {string} pineId Indicator ID (Like: PUB;XXXXXXXXXXXXXXXXXXXXX)
|
|
22
|
+
*/
|
|
23
|
+
constructor(sessionId, pineId) {
|
|
24
|
+
if (!sessionId) throw new Error('Please provide a SessionID');
|
|
25
|
+
if (!pineId) throw new Error('Please provide a PineID');
|
|
26
|
+
this.pineId = pineId;
|
|
27
|
+
this.sessionId = sessionId;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get list of authorized users
|
|
32
|
+
* @param {number} limit Fetching limit
|
|
33
|
+
* @param {'user__username'
|
|
34
|
+
* | '-user__username'
|
|
35
|
+
* | 'created' | 'created'
|
|
36
|
+
* | 'expiration,user__username'
|
|
37
|
+
* | '-expiration,user__username'
|
|
38
|
+
* } order Fetching order
|
|
39
|
+
* @returns {AuthorizationUser[]}
|
|
40
|
+
*/
|
|
41
|
+
async getUsers(limit = 10, order = '-created') {
|
|
42
|
+
const data = await request({
|
|
43
|
+
method: 'POST',
|
|
44
|
+
host: 'www.tradingview.com',
|
|
45
|
+
path: `/pine_perm/list_users/?limit=${limit}&order_by=${order}`,
|
|
46
|
+
headers: {
|
|
47
|
+
origin: 'https://www.tradingview.com',
|
|
48
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
49
|
+
cookie: `sessionid=${this.sessionId}`,
|
|
50
|
+
},
|
|
51
|
+
}, false, `pine_id=${this.pineId.replace(/;/g, '%3B')}`);
|
|
52
|
+
|
|
53
|
+
if (!data.results) throw new Error('Wrong sessionId or pineId');
|
|
54
|
+
|
|
55
|
+
return data.results;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Adds an user to the authorized list
|
|
60
|
+
* @param {string} username User's username
|
|
61
|
+
* @param {Date} [expiration] Expiration date
|
|
62
|
+
* @returns {'ok' | 'exists' | null}
|
|
63
|
+
*/
|
|
64
|
+
async addUser(username, expiration = null) {
|
|
65
|
+
const formData = new FormData();
|
|
66
|
+
formData.append('pine_id', this.pineId);
|
|
67
|
+
formData.append('username_recip', username);
|
|
68
|
+
if (expiration && expiration instanceof Date) {
|
|
69
|
+
formData.append('expiration', expiration.toString());
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const data = await request({
|
|
73
|
+
method: 'POST',
|
|
74
|
+
host: 'www.tradingview.com',
|
|
75
|
+
path: '/pine_perm/add/',
|
|
76
|
+
headers: {
|
|
77
|
+
origin: 'https://www.tradingview.com',
|
|
78
|
+
'Content-Type': `multipart/form-data; boundary=${formData.boundary}`,
|
|
79
|
+
cookie: `sessionid=${this.sessionId}`,
|
|
80
|
+
},
|
|
81
|
+
}, false, formData.toString());
|
|
82
|
+
|
|
83
|
+
if (!data.status) throw new Error('Wrong sessionId or pineId');
|
|
84
|
+
return data.status;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Modify an authorization expiration date
|
|
89
|
+
* @param {string} username User's username
|
|
90
|
+
* @param {Date} [expiration] New expiration date
|
|
91
|
+
* @returns {'ok' | null}
|
|
92
|
+
*/
|
|
93
|
+
async modifyExpiration(username, expiration = null) {
|
|
94
|
+
const formData = new FormData();
|
|
95
|
+
formData.append('pine_id', this.pineId);
|
|
96
|
+
formData.append('username_recip', username);
|
|
97
|
+
if (expiration && expiration instanceof Date) {
|
|
98
|
+
formData.append('expiration', expiration.toISOString());
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const data = await request({
|
|
102
|
+
method: 'POST',
|
|
103
|
+
host: 'www.tradingview.com',
|
|
104
|
+
path: '/pine_perm/modify_user_expiration/',
|
|
105
|
+
headers: {
|
|
106
|
+
origin: 'https://www.tradingview.com',
|
|
107
|
+
'Content-Type': `multipart/form-data; boundary=${formData.boundary}`,
|
|
108
|
+
cookie: `sessionid=${this.sessionId}`,
|
|
109
|
+
},
|
|
110
|
+
}, false, formData.toString());
|
|
111
|
+
|
|
112
|
+
if (!data.status) throw new Error('Wrong sessionId or pineId');
|
|
113
|
+
return data.status;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Removes an user to the authorized list
|
|
118
|
+
* @param {string} username User's username
|
|
119
|
+
* @returns {'ok' | null}
|
|
120
|
+
*/
|
|
121
|
+
async removeUser(username) {
|
|
122
|
+
const formData = new FormData();
|
|
123
|
+
formData.append('pine_id', this.pineId);
|
|
124
|
+
formData.append('username_recip', username);
|
|
125
|
+
|
|
126
|
+
const data = await request({
|
|
127
|
+
method: 'POST',
|
|
128
|
+
host: 'www.tradingview.com',
|
|
129
|
+
path: '/pine_perm/remove/',
|
|
130
|
+
headers: {
|
|
131
|
+
origin: 'https://www.tradingview.com',
|
|
132
|
+
'Content-Type': `multipart/form-data; boundary=${formData.boundary}`,
|
|
133
|
+
cookie: `sessionid=${this.sessionId}`,
|
|
134
|
+
},
|
|
135
|
+
}, false, formData.toString());
|
|
136
|
+
|
|
137
|
+
if (!data.status) throw new Error('Wrong sessionId or pineId');
|
|
138
|
+
return data.status;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
module.exports = PinePermManager;
|
package/src/client.js
CHANGED
package/src/miscRequests.js
CHANGED
|
@@ -1,43 +1,12 @@
|
|
|
1
1
|
const https = require('https');
|
|
2
|
+
const request = require('./request');
|
|
2
3
|
|
|
4
|
+
const PinePermManager = require('./classes/PinePermManager');
|
|
3
5
|
const PineIndicator = require('./classes/PineIndicator');
|
|
4
6
|
|
|
5
7
|
const indicators = ['Recommend.Other', 'Recommend.All', 'Recommend.MA'];
|
|
6
8
|
const builtInIndicList = [];
|
|
7
9
|
|
|
8
|
-
/**
|
|
9
|
-
* @param {https.RequestOptions} options HTTPS Request options
|
|
10
|
-
* @param {boolean} [raw] Get raw or JSON data
|
|
11
|
-
* @param {string} [content] Request body content
|
|
12
|
-
* @returns {Promise<string | object | array>} Result
|
|
13
|
-
*/
|
|
14
|
-
function request(options = {}, raw = false, content = '') {
|
|
15
|
-
return new Promise((cb, err) => {
|
|
16
|
-
const req = https.request(options, (res) => {
|
|
17
|
-
let data = '';
|
|
18
|
-
res.on('data', (c) => { data += c; });
|
|
19
|
-
res.on('end', () => {
|
|
20
|
-
if (raw) {
|
|
21
|
-
cb(data);
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
try {
|
|
26
|
-
data = JSON.parse(data);
|
|
27
|
-
} catch (error) {
|
|
28
|
-
err(new Error('Can\'t parse server response'));
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
cb(data);
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
req.on('error', err);
|
|
37
|
-
req.end(content);
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
|
|
41
10
|
async function fetchScanData(tickers = [], type = '', columns = []) {
|
|
42
11
|
let data = await request({
|
|
43
12
|
method: 'POST',
|
|
@@ -437,6 +406,9 @@ module.exports = {
|
|
|
437
406
|
get() {
|
|
438
407
|
return module.exports.getIndicator(ind.scriptIdPart, ind.version);
|
|
439
408
|
},
|
|
409
|
+
getManager() {
|
|
410
|
+
return new PinePermManager(ind.scriptIdPart);
|
|
411
|
+
},
|
|
440
412
|
})));
|
|
441
413
|
});
|
|
442
414
|
|
package/src/quote/session.js
CHANGED
package/src/request.js
ADDED
|
@@ -0,0 +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<string | object | array>} 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);
|
|
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);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
req.on('error', err);
|
|
33
|
+
req.end(content);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = request;
|