@mathieuc/tradingview 3.0.0 → 3.3.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.
Files changed (51) hide show
  1. package/.eslintrc.js +6 -0
  2. package/README.md +29 -3
  3. package/docs/DOCS.md +3 -0
  4. package/examples/AllPrivateIndicators.js +4 -4
  5. package/examples/BuiltInIndicator.js +1 -1
  6. package/examples/CustomChartType.js +1 -0
  7. package/examples/Errors.js +147 -153
  8. package/examples/FakeReplayMode.js +41 -0
  9. package/examples/FromToData.js +29 -0
  10. package/examples/GetDrawings.js +26 -0
  11. package/examples/GraphicIndicator.js +7 -5
  12. package/examples/PinePermManage.js +57 -0
  13. package/examples/ReplayMode.js +97 -0
  14. package/examples/UserLogin.js +16 -0
  15. package/main.js +2 -0
  16. package/package.json +6 -9
  17. package/src/FormData.js +36 -0
  18. package/src/chart/session.js +205 -24
  19. package/src/chart/study.js +68 -68
  20. package/src/classes/BuiltInIndicator.js +7 -1
  21. package/src/classes/PineIndicator.js +7 -7
  22. package/src/classes/PinePermManager.js +143 -0
  23. package/src/client.js +15 -10
  24. package/src/miscRequests.js +144 -101
  25. package/src/protocol.js +7 -1
  26. package/src/quote/session.js +1 -0
  27. package/src/request.js +37 -0
  28. package/src/types.js +6 -2
  29. package/src/utils.js +6 -0
  30. package/test.js +58 -147
  31. package/tests/1. Search.js +18 -0
  32. package/tests/2. QuoteSession.js +54 -0
  33. package/tests/3. SimpleChart.js +61 -0
  34. package/tests/4. Indicators.js +100 -0
  35. package/tests/5. BuiltInIndicator.js +41 -0
  36. package/tests/6. FakeReplayMode.js +59 -0
  37. package/tests/7. ReplayMode.js +51 -0
  38. package/tests/8. CustomChartTypes.js +115 -0
  39. package/tests/9. AllErrors.js +146 -0
  40. package/.github/FUNDING.yml +0 -1
  41. package/examples/_RenkoIndicator.js +0 -41
  42. package/tests/alerts/discordWH.js +0 -76
  43. package/tests/alerts/drawings.js +0 -41
  44. package/tests/alerts/envLoader.js +0 -11
  45. package/tests/alerts/manualAlert.js +0 -77
  46. package/tests/alerts/sentNotifs.js +0 -16
  47. package/tests/alerts/strategyAlert.js +0 -94
  48. package/tests/analysis.js +0 -13
  49. package/tests/indicator.js +0 -42
  50. package/tests/prices.js +0 -33
  51. package/tests/strategy.js +0 -56
@@ -0,0 +1,97 @@
1
+ const TradingView = require('../main');
2
+
3
+ /*
4
+ This example tests the real replay
5
+ mode by fetching indicator data and
6
+ stores it in a 'periods' variable
7
+ */
8
+
9
+ const config = {
10
+ symbol: 'BINANCE:BTCEUR',
11
+ timeframe: 'D',
12
+ startFrom: Math.round(Date.now() / 1000) - 86400 * 7, // Seven days before now
13
+ // startFrom: 1600000000,
14
+ };
15
+
16
+ console.log('----- Testing ReplayMode: -----');
17
+
18
+ const client = new TradingView.Client();
19
+ const chart = new client.Session.Chart();
20
+
21
+ chart.setMarket(config.symbol, {
22
+ timeframe: config.timeframe,
23
+ replay: config.startFrom,
24
+ range: 1,
25
+ });
26
+
27
+ // chart.onReplayLoaded(() => {
28
+ // console.log('STARTING REPLAY MODE');
29
+ // chart.replayStart(1000);
30
+
31
+ // // setTimeout(() => {
32
+ // // console.log('STOPPING REPLAY MODE');
33
+ // // chart.replayStop();
34
+ // // }, 7000);
35
+ // });
36
+
37
+ let loading = 0;
38
+ const indicators = [];
39
+ const periods = {};
40
+
41
+ let interval = NaN;
42
+
43
+ async function step() {
44
+ const period = { ...chart.periods[0] };
45
+
46
+ const times = Object.keys(periods);
47
+ const intrval = times[times.length - 1] - times[times.length - 2];
48
+ if (Number.isNaN(interval) && times.length >= 2) interval = intrval;
49
+
50
+ if (!Number.isNaN(interval) && interval !== intrval) {
51
+ throw new Error(`Wrong interval: ${intrval} (should be ${interval})`);
52
+ }
53
+
54
+ indicators.forEach(([n, i]) => {
55
+ const plots = { ...i.periods[0] };
56
+ delete plots.$time;
57
+ period[n] = { plots };
58
+
59
+ Object.keys(i.graphic).forEach((g) => {
60
+ if (!i.graphic[g].length) return;
61
+ if (!period[n].graphics) period[n].graphics = {};
62
+ period[n].graphics[g] = i.graphic[g];
63
+ });
64
+ });
65
+
66
+ periods[period.time] = period;
67
+
68
+ console.log('Next ->', period.time, times.length);
69
+
70
+ await chart.replayStep(1);
71
+ step();
72
+ }
73
+
74
+ chart.onReplayEnd(async () => {
75
+ await client.end();
76
+ console.log('Done !', Object.keys(periods).length);
77
+ });
78
+
79
+ async function addIndicator(name, pineId, options = {}) {
80
+ loading += 1;
81
+
82
+ const indic = pineId.includes('@')
83
+ ? new TradingView.BuiltInIndicator(pineId)
84
+ : await TradingView.getIndicator(pineId);
85
+ Object.keys(options).forEach((o) => { indic.setOption(o, options[o]); });
86
+
87
+ const std = new chart.Study(indic);
88
+
89
+ std.onReady(() => {
90
+ indicators.push([name, std]);
91
+ if (loading === indicators.length) step();
92
+ });
93
+ }
94
+
95
+ addIndicator('Volume', 'Volume@tv-basicstudies-144');
96
+ addIndicator('EMA_50', 'STD;EMA', { Length: 50 });
97
+ addIndicator('EMA_200', 'STD;EMA', { Length: 200 });
@@ -0,0 +1,16 @@
1
+ const TradingView = require('../main');
2
+
3
+ /*
4
+ This example tests the
5
+ user login function
6
+ */
7
+
8
+ if (!process.argv[2]) throw Error('Please specify your username/email');
9
+ if (!process.argv[3]) throw Error('Please specify your password');
10
+
11
+ TradingView.loginUser(process.argv[2], process.argv[3], false).then((user) => {
12
+ console.log('User:', user);
13
+ console.log('Sessionid:', user.session);
14
+ }).catch((err) => {
15
+ console.error('Login error:', err.message);
16
+ });
package/main.js CHANGED
@@ -2,8 +2,10 @@ const miscRequests = require('./src/miscRequests');
2
2
  const Client = require('./src/client');
3
3
  const BuiltInIndicator = require('./src/classes/BuiltInIndicator');
4
4
  const PineIndicator = require('./src/classes/PineIndicator');
5
+ const PinePermManager = require('./src/classes/PinePermManager');
5
6
 
6
7
  module.exports = { ...miscRequests };
7
8
  module.exports.Client = Client;
8
9
  module.exports.BuiltInIndicator = BuiltInIndicator;
9
10
  module.exports.PineIndicator = PineIndicator;
11
+ module.exports.PinePermManager = PinePermManager;
package/package.json CHANGED
@@ -1,20 +1,18 @@
1
1
  {
2
2
  "name": "@mathieuc/tradingview",
3
- "version": "3.0.0",
3
+ "version": "3.3.0",
4
4
  "description": "Tradingview instant stocks API, indicator alerts, trading bot, and more !",
5
5
  "main": "main.js",
6
6
  "scripts": {
7
- "test-prices": "node ./tests/prices.js",
8
- "test-analysis": "node ./tests/analysis.js",
9
- "test-indicator": "node ./tests/indicator.js",
10
- "test-manual-alerts": "node ./tests/alerts/manualAlert.js",
11
- "test-strat-alerts": "node ./tests/alerts/strategyAlert.js",
12
- "test": "npm run test-prices"
7
+ "test": "node test"
13
8
  },
14
9
  "repository": {
15
10
  "type": "git",
16
11
  "url": "git+https://github.com/Mathieu2301/TradingView-API.git"
17
12
  },
13
+ "engines": {
14
+ "node": ">=14.0.0"
15
+ },
18
16
  "keywords": [
19
17
  "tradingwiew",
20
18
  "market",
@@ -36,7 +34,6 @@
36
34
  "@mathieuc/console": "^1.0.1",
37
35
  "eslint": "^7.25.0",
38
36
  "eslint-config-airbnb-base": "^14.2.1",
39
- "eslint-plugin-import": "^2.22.1",
40
- "jsdoc-to-markdown": "^7.1.0"
37
+ "eslint-plugin-import": "^2.22.1"
41
38
  }
42
39
  }
@@ -0,0 +1,36 @@
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;
@@ -53,17 +53,20 @@ const ChartTypes = {
53
53
  * @prop {number} close Period close value
54
54
  * @prop {number} max Period max value
55
55
  * @prop {number} min Period min value
56
- * @prop {number} change Period change absolute value
56
+ * @prop {number} volume Period volume value
57
57
  */
58
58
 
59
59
  /**
60
60
  * @typedef {Object} Subsession
61
61
  * @prop {string} id Subsession ID (ex: 'regular')
62
62
  * @prop {string} description Subsession description (ex: 'Regular')
63
- * @prop {string} privat If private
63
+ * @prop {boolean} private If private
64
64
  * @prop {string} session Session (ex: '24x7')
65
+ * @prop {string} session-correction Session correction
65
66
  * @prop {string} session-display Session display (ex: '24x7')
66
- *
67
+ */
68
+
69
+ /**
67
70
  * @typedef {Object} MarketInfos
68
71
  * @prop {string} series_id Used series (ex: 'ser_1')
69
72
  * @prop {string} base_currency Base currency (ex: 'BTC')
@@ -91,7 +94,7 @@ const ChartTypes = {
91
94
  * @prop {number} minmove2 Minimum move value 2
92
95
  * @prop {string} timezone Used timezone
93
96
  * @prop {boolean} is_replayable If the replay mode is available
94
- * @prop {boolean} has_adjustment If the adjustment mode is enabled ????
97
+ * @prop {boolean} has_adjustment If the adjustment mode is enabled
95
98
  * @prop {boolean} has_extended_hours Has extended hours
96
99
  * @prop {string} bar_source Bar source
97
100
  * @prop {string} bar_transform Bar transform
@@ -112,7 +115,14 @@ const ChartTypes = {
112
115
  * @param {import('../client').ClientBridge} client
113
116
  */
114
117
  module.exports = (client) => class ChartSession {
115
- #sessionID = genSessionID('cs');
118
+ #chartSessionID = genSessionID('cs');
119
+
120
+ #replaySessionID = genSessionID('rs');
121
+
122
+ #replayMode = false;
123
+
124
+ /** @type {Object<string, (): any>} */
125
+ #replayOKCB = {};
116
126
 
117
127
  /** Parent client */
118
128
  #client = client;
@@ -147,6 +157,11 @@ module.exports = (client) => class ChartSession {
147
157
  symbolLoaded: [],
148
158
  update: [],
149
159
 
160
+ replayLoaded: [],
161
+ replayPoint: [],
162
+ replayResolution: [],
163
+ replayEnd: [],
164
+
150
165
  event: [],
151
166
  error: [],
152
167
  };
@@ -166,7 +181,7 @@ module.exports = (client) => class ChartSession {
166
181
  }
167
182
 
168
183
  constructor() {
169
- this.#client.sessions[this.#sessionID] = {
184
+ this.#client.sessions[this.#chartSessionID] = {
170
185
  type: 'chart',
171
186
  onData: (packet) => {
172
187
  if (global.TW_DEBUG) console.log('§90§30§106 CHART SESSION §0 DATA', packet);
@@ -187,7 +202,10 @@ module.exports = (client) => class ChartSession {
187
202
  }
188
203
 
189
204
  if (['timescale_update', 'du'].includes(packet.type)) {
205
+ const changes = [];
206
+
190
207
  Object.keys(packet.data[1]).forEach((k) => {
208
+ changes.push(k);
191
209
  if (k === '$prices') {
192
210
  const periods = packet.data[1].$prices;
193
211
  if (!periods || !periods.s) return;
@@ -200,7 +218,7 @@ module.exports = (client) => class ChartSession {
200
218
  close: p.v[4],
201
219
  max: p.v[2],
202
220
  min: p.v[3],
203
- change: Math.round(p.v[5] * 100) / 100,
221
+ volume: Math.round(p.v[5] * 100) / 100,
204
222
  };
205
223
  });
206
224
 
@@ -210,7 +228,7 @@ module.exports = (client) => class ChartSession {
210
228
  if (this.#studyListeners[k]) this.#studyListeners[k](packet);
211
229
  });
212
230
 
213
- this.#handleEvent('update');
231
+ this.#handleEvent('update', changes);
214
232
  return;
215
233
  }
216
234
 
@@ -231,7 +249,47 @@ module.exports = (client) => class ChartSession {
231
249
  },
232
250
  };
233
251
 
234
- this.#client.send('chart_create_session', [this.#sessionID]);
252
+ this.#client.sessions[this.#replaySessionID] = {
253
+ type: 'replay',
254
+ onData: (packet) => {
255
+ if (global.TW_DEBUG) console.log('§90§30§106 REPLAY SESSION §0 DATA', packet);
256
+
257
+ if (packet.type === 'replay_ok') {
258
+ if (this.#replayOKCB[packet.data[1]]) {
259
+ this.#replayOKCB[packet.data[1]]();
260
+ delete this.#replayOKCB[packet.data[1]];
261
+ }
262
+ return;
263
+ }
264
+
265
+ if (packet.type === 'replay_instance_id') {
266
+ this.#handleEvent('replayLoaded', packet.data[1]);
267
+ return;
268
+ }
269
+
270
+ if (packet.type === 'replay_point') {
271
+ this.#handleEvent('replayPoint', packet.data[1]);
272
+ return;
273
+ }
274
+
275
+ if (packet.type === 'replay_resolutions') {
276
+ this.#handleEvent('replayResolution', packet.data[1], packet.data[2]);
277
+ return;
278
+ }
279
+
280
+ if (packet.type === 'replay_data_end') {
281
+ this.#handleEvent('replayEnd');
282
+ return;
283
+ }
284
+
285
+ if (packet.type === 'critical_error') {
286
+ const [, name, description] = packet.data;
287
+ this.#handleError('Critical error:', name, description);
288
+ }
289
+ },
290
+ };
291
+
292
+ this.#client.send('chart_create_session', [this.#chartSessionID]);
235
293
  }
236
294
 
237
295
  #seriesCreated = false;
@@ -241,22 +299,25 @@ module.exports = (client) => class ChartSession {
241
299
  /**
242
300
  * @param {import('../types').TimeFrame} timeframe Chart period timeframe
243
301
  * @param {number} [range] Number of loaded periods/candles (Default: 100)
302
+ * @param {number} [reference] Reference candle timestamp (Default is now)
244
303
  */
245
- setSeries(timeframe = '240', range = 100) {
304
+ setSeries(timeframe = '240', range = 100, reference = null) {
246
305
  if (!this.#currentSeries) {
247
306
  this.#handleError('Please set the market before setting series');
248
307
  return;
249
308
  }
250
309
 
310
+ const calcRange = !reference ? range : ['bar_count', reference, range];
311
+
251
312
  this.#periods = {};
252
313
 
253
314
  this.#client.send(`${this.#seriesCreated ? 'modify' : 'create'}_series`, [
254
- this.#sessionID,
315
+ this.#chartSessionID,
255
316
  '$prices',
256
317
  's1',
257
318
  `ser_${this.#currentSeries}`,
258
319
  timeframe,
259
- this.#seriesCreated ? '' : range,
320
+ this.#seriesCreated ? '' : calcRange,
260
321
  ]);
261
322
 
262
323
  this.#seriesCreated = true;
@@ -268,41 +329,67 @@ module.exports = (client) => class ChartSession {
268
329
  * @param {Object} [options] Chart options
269
330
  * @param {import('../types').TimeFrame} [options.timeframe] Chart period timeframe
270
331
  * @param {number} [options.range] Number of loaded periods/candles (Default: 100)
332
+ * @param {number} [options.to] Last candle timestamp (Default is now)
271
333
  * @param {'splits' | 'dividends'} [options.adjustment] Market adjustment
272
334
  * @param {'regular' | 'extended'} [options.session] Chart session
273
335
  * @param {'EUR' | 'USD' | string} [options.currency] Chart currency
274
336
  * @param {ChartType} [options.type] Chart custom type
275
337
  * @param {ChartInputs} [options.inputs] Chart custom inputs
338
+ * @param {number} [options.replay] Replay mode starting point (Timestamp)
276
339
  */
277
340
  setMarket(symbol, options = {}) {
278
341
  this.#periods = {};
279
342
 
343
+ if (this.#replayMode) {
344
+ this.#replayMode = false;
345
+ this.#client.send('replay_delete_session', [this.#replaySessionID]);
346
+ }
347
+
280
348
  const symbolInit = {
281
349
  symbol: symbol || 'BTCEUR',
282
350
  adjustment: options.adjustment || 'splits',
283
351
  };
284
352
 
285
353
  if (options.session) symbolInit.session = options.session;
286
-
287
354
  if (options.currency) symbolInit['currency-id'] = options.currency;
288
355
 
289
- const chartInit = (options.type && ChartTypes[options.type]) ? {} : symbolInit;
356
+ if (options.replay) {
357
+ this.#replayMode = true;
358
+ this.#client.send('replay_create_session', [this.#replaySessionID]);
359
+
360
+ this.#client.send('replay_add_series', [
361
+ this.#replaySessionID,
362
+ 'req_replay_addseries',
363
+ `=${JSON.stringify(symbolInit)}`,
364
+ options.timeframe,
365
+ ]);
366
+
367
+ this.#client.send('replay_reset', [
368
+ this.#replaySessionID,
369
+ 'req_replay_reset',
370
+ options.replay,
371
+ ]);
372
+ }
373
+
374
+ const complex = options.type || options.replay;
375
+ const chartInit = complex ? {} : symbolInit;
290
376
 
291
- if (options.type && ChartTypes[options.type]) {
377
+ if (complex) {
378
+ if (options.replay) chartInit.replay = this.#replaySessionID;
292
379
  chartInit.symbol = symbolInit;
293
380
  chartInit.type = ChartTypes[options.type];
294
- chartInit.inputs = { ...options.inputs };
381
+ if (options.type) chartInit.inputs = { ...options.inputs };
295
382
  }
296
383
 
297
384
  this.#currentSeries += 1;
298
385
 
299
386
  this.#client.send('resolve_symbol', [
300
- this.#sessionID,
387
+ this.#chartSessionID,
301
388
  `ser_${this.#currentSeries}`,
302
389
  `=${JSON.stringify(chartInit)}`,
303
390
  ]);
304
391
 
305
- this.setSeries(options.timeframe, options.range);
392
+ this.setSeries(options.timeframe, options.range, options.to);
306
393
  }
307
394
 
308
395
  /**
@@ -311,7 +398,7 @@ module.exports = (client) => class ChartSession {
311
398
  */
312
399
  setTimezone(timezone) {
313
400
  this.#periods = {};
314
- this.#client.send('switch_timezone', [this.#sessionID, timezone]);
401
+ this.#client.send('switch_timezone', [this.#chartSessionID, timezone]);
315
402
  }
316
403
 
317
404
  /**
@@ -319,7 +406,60 @@ module.exports = (client) => class ChartSession {
319
406
  * @param {number} number Number of additional periods/candles you want to fetch
320
407
  */
321
408
  fetchMore(number = 1) {
322
- this.#client.send('request_more_data', [this.#sessionID, '$prices', number]);
409
+ this.#client.send('request_more_data', [this.#chartSessionID, '$prices', number]);
410
+ }
411
+
412
+ /**
413
+ * Fetch x additional previous periods/candles values
414
+ * @param {number} number Number of additional periods/candles you want to fetch
415
+ * @returns {Promise} Raise when the data has been fetched
416
+ */
417
+ replayStep(number = 1) {
418
+ return new Promise((cb) => {
419
+ if (!this.#replayMode) {
420
+ this.#handleError('No replay session');
421
+ return;
422
+ }
423
+
424
+ const reqID = genSessionID('rsq_step');
425
+ this.#client.send('replay_step', [this.#replaySessionID, reqID, number]);
426
+ this.#replayOKCB[reqID] = () => { cb(); };
427
+ });
428
+ }
429
+
430
+ /**
431
+ * Start fetching a new period/candle every x ms
432
+ * @param {number} interval Number of additional periods/candles you want to fetch
433
+ * @returns {Promise} Raise when the replay mode starts
434
+ */
435
+ replayStart(interval = 1000) {
436
+ return new Promise((cb) => {
437
+ if (!this.#replayMode) {
438
+ this.#handleError('No replay session');
439
+ return;
440
+ }
441
+
442
+ const reqID = genSessionID('rsq_start');
443
+ this.#client.send('replay_start', [this.#replaySessionID, reqID, interval]);
444
+ this.#replayOKCB[reqID] = () => { cb(); };
445
+ });
446
+ }
447
+
448
+ /**
449
+ * Stop fetching a new period/candle every x ms
450
+ * @returns {Promise} Raise when the replay mode stops
451
+ */
452
+ replayStop() {
453
+ return new Promise((cb) => {
454
+ if (!this.#replayMode) {
455
+ this.#handleError('No replay session');
456
+ return;
457
+ }
458
+
459
+ const reqID = genSessionID('rsq_stop');
460
+ this.#client.send('replay_stop', [this.#replaySessionID, reqID]);
461
+ this.#replayOKCB[reqID] = () => { cb(); };
462
+ });
323
463
  }
324
464
 
325
465
  /**
@@ -333,13 +473,52 @@ module.exports = (client) => class ChartSession {
333
473
 
334
474
  /**
335
475
  * When a chart update happens
336
- * @param {() => void} cb
476
+ * @param {(changes: ('$prices' | string)[]) => void} cb
337
477
  * @event
338
478
  */
339
479
  onUpdate(cb) {
340
480
  this.#callbacks.update.push(cb);
341
481
  }
342
482
 
483
+ /**
484
+ * When the replay session is ready
485
+ * @param {() => void} cb
486
+ * @event
487
+ */
488
+ onReplayLoaded(cb) {
489
+ this.#callbacks.replayLoaded.push(cb);
490
+ }
491
+
492
+ /**
493
+ * When the replay session has new resolution
494
+ * @param {(
495
+ * timeframe: import('../types').TimeFrame,
496
+ * index: number,
497
+ * ) => void} cb
498
+ * @event
499
+ */
500
+ onReplayResolution(cb) {
501
+ this.#callbacks.replayResolution.push(cb);
502
+ }
503
+
504
+ /**
505
+ * When the replay session ends
506
+ * @param {() => void} cb
507
+ * @event
508
+ */
509
+ onReplayEnd(cb) {
510
+ this.#callbacks.replayEnd.push(cb);
511
+ }
512
+
513
+ /**
514
+ * When the replay session cursor has moved
515
+ * @param {(index: number) => void} cb
516
+ * @event
517
+ */
518
+ onReplayPoint(cb) {
519
+ this.#callbacks.replayPoint.push(cb);
520
+ }
521
+
343
522
  /**
344
523
  * When chart error happens
345
524
  * @param {(...any) => void} cb Callback
@@ -351,7 +530,7 @@ module.exports = (client) => class ChartSession {
351
530
 
352
531
  /** @type {ChartSessionBridge} */
353
532
  #chartSession = {
354
- sessionID: this.#sessionID,
533
+ sessionID: this.#chartSessionID,
355
534
  studyListeners: this.#studyListeners,
356
535
  indexes: {},
357
536
  send: (t, p) => this.#client.send(t, p),
@@ -361,7 +540,9 @@ module.exports = (client) => class ChartSession {
361
540
 
362
541
  /** Delete the chart session */
363
542
  delete() {
364
- this.#client.send('quote_delete_session', [this.#sessionID]);
365
- delete this.#client.sessions[this.#sessionID];
543
+ if (this.#replayMode) this.#client.send('replay_delete_session', [this.#replaySessionID]);
544
+ this.#client.send('chart_delete_session', [this.#chartSessionID]);
545
+ delete this.#client.sessions[this.#chartSessionID];
546
+ this.#replayMode = false;
366
547
  }
367
548
  };