market-data-tradingview-ws 0.0.12 → 0.0.14

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/lib/client.js CHANGED
@@ -13,10 +13,18 @@ const periods = {
13
13
  3600: '60',
14
14
  14400: '240',
15
15
  86400: '1D',
16
+ 604800: '1W',
17
+ 2592000: '1M',
16
18
  };
17
19
 
20
+ const extractJSON = (str) => {
21
+ const match = str.match(/\{.*?\}/s);
22
+ return match ? JSON.parse(match[0]) : null;
23
+ }
24
+
25
+ let ws = null;
26
+
18
27
  module.exports = class Client {
19
- #ws;
20
28
  #charts = {
21
29
  values: {},
22
30
  set({ key, data }) {
@@ -29,6 +37,7 @@ module.exports = class Client {
29
37
  frame: data.frame,
30
38
  i: data.i,
31
39
  deleted: data.deleted || false,
40
+ limit: data.limit || 1500,
32
41
  };
33
42
  }
34
43
  },
@@ -84,17 +93,29 @@ module.exports = class Client {
84
93
  if (index !== -1) arr.splice(index, 1);
85
94
  },
86
95
  };
96
+ #symbols = {
97
+ values: {},
98
+ max: 1,
99
+ getSymbol({ key, symbol }) {
100
+ // let data = this.values[symbol];
101
+ // if (data === undefined) data = this.set({ key, symbol });
102
+ // return data;
103
+ const symbolKey = 'sds_sym_' + this.max++;
104
+ ws.send(tv.resolveSymbol({ chartSession: key, symbolKey, symbol }));
105
+ this.values[symbol] = { key: symbolKey };
106
+ return this.values[symbol];
107
+ },
108
+ };
87
109
 
88
- // #resolved;
89
110
  #token = null;
90
- #maxSymbolKey = 1;
91
111
 
92
112
  constructor() {
93
113
  // this.#resolved = new Map();
94
114
  }
95
115
 
96
- close() {
97
- this.#ws.close();
116
+ async close() {
117
+ // token delete !!!
118
+ ws.close();
98
119
  }
99
120
 
100
121
  getAll() {
@@ -130,23 +151,31 @@ module.exports = class Client {
130
151
  });
131
152
  }
132
153
 
133
- async connect({ url, options, login, pass, result }) {
134
- this.#token = await this.getToken({ login, pass });
135
- // console.log(this.#token);
154
+ async connect({ url, options, login, pass, result, token }) {
155
+ this.#token = token || await this.getToken({ login, pass });
156
+
136
157
  if (this.#token.length > 10) {
137
158
  return new Promise((resolv, reject) => {
138
- this.#ws = new WebSocket(url, options);
159
+ url += '?from=chart%2FKq8EfKQU%2F';
160
+ const date = new Date();
161
+ url += '&date=' + date.getFullYear() + '_' + String(date.getMonth() + 1).padStart(2, '0') + '_' + String(date.getDate()).padStart(2, '0') + '-11_25';
162
+ url += '&type=chart';
163
+ ws = new WebSocket(url, options);
164
+
165
+ const originalSend = ws.send;
166
+ ws.send = function(data) {
167
+ // console.log('Message sent:', data);
168
+ originalSend.apply(ws, arguments);
169
+ };
139
170
 
140
- this.#ws.onclose = () => console.log('disconnected');
171
+ ws.onclose = () => console.log('disconnected');
141
172
 
142
- this.#ws.onerror = (error) => reject(console.error('error: ', error));
143
- this.#ws.onopen = () => {
144
- this.#ws.send(this.auth(this.#token));
145
- this.#ws.send(tv.setLocaleRu());
173
+ ws.onerror = (error) => reject(console.error('error: ', error));
174
+ ws.onopen = () => {
175
+ ws.send(this.auth(this.#token));
176
+ ws.send(tv.setLocaleRu());
146
177
 
147
- // const chartKey = this.createChartSession();
148
178
  const quoteKey = this.createQuoteSmallSession();
149
- // this.addQuoteSymbols({ symbols: ['NASDAQ:PYPL'] });
150
179
 
151
180
  this.messageListner({ result });
152
181
  resolv('open');
@@ -159,24 +188,24 @@ module.exports = class Client {
159
188
  createQuoteSmallSession() {
160
189
  const key = tv.createSession({ startsWith: 'qs_' });
161
190
  this.#quotes.set({ key, data: { id: key, type: 'small', symbols: [] } });
162
- this.#ws.send(tv.quoteCreateSession({ session: key }));
163
- this.#ws.send(tv.setFieldsSmall({ session: key }));
164
- // this.#ws.send(tv.setFields({ session: key }));
191
+ ws.send(tv.quoteCreateSession({ session: key }));
192
+ ws.send(tv.setFieldsSmall({ session: key }));
193
+ // ws.send(tv.setFields({ session: key }));
165
194
  return key;
166
195
  }
167
196
 
168
197
  createQuoteFullSession() {
169
198
  const key = tv.createSession({ startsWith: 'qs_' });
170
199
  this.#quotes.set({ key, data: { id: key, type: 'full', symbols: [] } });
171
- this.#ws.send(tv.quoteCreateSession({ session: key }));
200
+ ws.send(tv.quoteCreateSession({ session: key }));
172
201
  return key;
173
202
  }
174
203
 
175
204
  createChartSession() {
176
205
  const key = tv.createSession({ startsWith: 'cs_' });
177
206
  this.#charts.set({ key, data: { id: key, symbol: '', period: 0, symbolKey: '', frame: '', i: '', deleted: false } });
178
- this.#ws.send(tv.chartCreateSession({ chartSession: key }));
179
- this.#ws.send(tv.switchTimezone({ chartSession: key, tz: 'Etc/UTC' }));
207
+ ws.send(tv.chartCreateSession({ chartSession: key }));
208
+ ws.send(tv.switchTimezone({ chartSession: key, tz: 'Etc/UTC' }));
180
209
  return key;
181
210
  }
182
211
 
@@ -189,9 +218,8 @@ module.exports = class Client {
189
218
  result.exist.push(symbol);
190
219
  } else {
191
220
  key = this.#quotes.add({ symbol });
192
- // console.log(key, symbol);
193
- this.#ws.send(tv.quoteAddSymbol({ session: key, symbol }));
194
- // this.#ws.send(tv.quoteFastSymbol({ session: key, symbol }));
221
+ ws.send(tv.quoteAddSymbol({ session: key, symbol }));
222
+ // ws.send(tv.quoteFastSymbol({ session: key, symbol }));
195
223
  result.add.push(symbol);
196
224
  }
197
225
  });
@@ -202,39 +230,31 @@ module.exports = class Client {
202
230
  const key = this.#quotes.getBySymbol({ symbol });
203
231
  if (!key) return 'unlisted';
204
232
  this.#quotes.delete({ key, symbol });
205
- this.#ws.send(tv.quoteRemoveSymbols({ session: key, symbol }));
233
+ ws.send(tv.quoteRemoveSymbols({ session: key, symbol }));
206
234
  return 'delete';
207
235
  }
208
236
 
209
- resolveSymbol({ key, symbol }) {
210
- const symbolKey = 'sds_sym_' + this.#maxSymbolKey++;
211
- this.#ws.send(tv.resolveSymbol({ chartSession: key, symbolKey, symbol }));
212
- return symbolKey;
213
- }
214
-
215
237
  addChartSymbol({ symbol, period, limit }) {
216
238
  let key = this.#charts.getBySymbolPeriod({ symbol, period });
217
239
  if (key) {
218
- console.warn('addChartSymbol exist');
240
+ console.warn('addChartSymbol exist:', key, symbol, period);
219
241
  return 'exist';
220
242
  }
221
-
222
243
  // console.warn('modules addChartSymbol1', symbol, period, limit, key);
223
- const exist = this.#charts.getDeleted();
224
- if (exist !== null) return this.updateChartSymbol({ symbol, period, limit, last: { symbol: exist.symbol, period: parseInt(exist.period) }});
244
+ const deleted = this.#charts.getDeleted();
245
+ if (deleted !== null && deleted.limit === limit) return this.updateChartSymbol({ symbol, period, limit, last: { symbol: deleted.symbol, period: parseInt(deleted.period) }});
225
246
 
226
247
  key = this.createChartSession();
227
248
  const data = this.#charts.get({ key });
228
249
 
229
- data.symbolKey = this.resolveSymbol({ key, symbol });
250
+ data.symbolKey = this.#symbols.getSymbol({ key, symbol }).key;
230
251
  data.symbol = symbol;
231
252
  data.period = period;
232
253
  data.frame = 'sds_' + Object.keys(this.#charts.values).length;
233
254
  data.i = 's' + 1;
255
+ data.limit = limit;
234
256
 
235
- // this.#ws.send(tv.resolveSymbol({ chartSession: key, symbolKey: data.symbolKey, symbol }));
236
- // this.#resolved.set(symbol, { key: data.symbolKey });
237
- this.#ws.send(
257
+ ws.send(
238
258
  tv.createSeries({
239
259
  chartSession: key,
240
260
  frame: data.frame,
@@ -244,49 +264,56 @@ module.exports = class Client {
244
264
  limit,
245
265
  }),
246
266
  );
247
- // console.log('addChartSymbol4', periods[period], data);
267
+ // console.warn('addChartSymbol4', periods[period], data);
248
268
  this.#charts.set({ key, data });
249
-
250
269
  return 'add';
251
270
  }
252
271
 
253
272
  updateChartSymbol({ symbol, period, limit, last }) {
254
- // period = parseInt(period);
255
- // last.period = parseInt(last.period);
256
273
  const key = this.#charts.getBySymbolPeriod({ symbol: last.symbol, period: last.period });
257
274
  const data = this.#charts.get({ key });
258
275
 
259
276
  // console.warn('modules updateChartSymbol 1: ', symbol, period, limit, last, data);
260
-
261
- const alreadyKey = this.#charts.getBySymbolPeriod({ symbol, period })
262
- if (alreadyKey) {
263
- // console.warn('updateChartSymbol exist: ', alreadyKey);
264
- this.#charts.delete({ key });
265
- console.warn('updateChartSymbol exist');
266
- return 'exist';
267
- }
277
+ // const alreadyKey = this.#charts.getBySymbolPeriod({ symbol, period })
278
+ // if (alreadyKey) {
279
+ // // console.warn('updateChartSymbol exist: ', alreadyKey);
280
+ // this.#charts.delete({ key });
281
+ // // console.warn('updateChartSymbol exist');
282
+ // return 'exist';
283
+ // }
268
284
 
269
- data.symbolKey = this.resolveSymbol({ key, symbol });
285
+ data.symbolKey = this.#symbols.getSymbol({ key, symbol }).key;
270
286
  data.symbol = symbol;
271
287
  data.period = period;
272
288
  data.i = 's' + (parseInt(data.i.replace(/[^0-9]/g, '')) + 1);
273
- this.#ws.send(
289
+
290
+ ws.send(
274
291
  tv.modifySeries({
275
292
  chartSession: key,
276
293
  frame: data.frame,
277
294
  increment: data.i,
278
295
  symbolKey: data.symbolKey,
279
296
  interval: periods[period],
280
- limit,
281
297
  }),
282
298
  );
283
299
  this.#charts.set({ key, data });
300
+ if (data.limit < limit) {
301
+ ws.send(tv.moreData({ chartSession: key, frame: data.frame, add: limit - data.limit }));
302
+ data.limit = limit;
303
+ }
284
304
  return 'update';
285
305
  }
286
306
 
307
+ deleteChartSymbol({ symbol, period }) {
308
+ let key = this.#charts.getBySymbolPeriod({ symbol, period });
309
+ if (!key) return void console.warn('deleteChartSymbol empty key');
310
+ this.#charts.delete({ key });
311
+ return void console.log('deleteChartSymbol: ', key);
312
+ }
313
+
287
314
  messageListner({ result }) {
288
- this.#ws.on('message', (raw) => {
289
- if (this.#ws.readyState !== this.#ws.OPEN) return;
315
+ ws.on('message', (raw) => {
316
+ if (ws.readyState !== ws.OPEN) return;
290
317
 
291
318
  // console.log('raw: ', JSON.stringify(raw.toString()));
292
319
  let session = '';
@@ -296,19 +323,14 @@ module.exports = class Client {
296
323
  if (packet === undefined) {
297
324
  console.warn('undefined: ', raw.toString());
298
325
  } else {
299
- // eslint-disable-next-line no-lonely-if
300
326
  if (packet.type === 'ping') {
301
327
  // console.info('pong', packet);
302
- this.#ws.send(tv.createPong({ value: packet.data }));
303
- // } else if (packet.type === 'protocol_error') {
304
- // console.warn('protocol_error', packet);
305
- // this.close();
328
+ ws.send(tv.createPong({ value: packet.data }));
306
329
  } else if (packet.type === 'qsd') {
307
330
  send = packet.data[1].v;
308
331
  name = send.bid === undefined && send.ask === undefined ? 'data' : 'levelI';
309
- // console.warn(packet.data[1]);
310
- // console.log(JSON.parse(packet.data[1].n.substring(1)).symbol);
311
- send.symbol = JSON.parse(packet.data[1].n.substring(1)).symbol;
332
+ // console.log(packet.data[1].n);
333
+ send.symbol =(extractJSON(packet.data[1].n) || {symbol: packet.data[1].n}).symbol;
312
334
  result(name, send);
313
335
  } else if (['du', 'timescale_update'].includes(packet.type)) {
314
336
  session = this.#charts.get({ key: packet.data[0] });
@@ -335,12 +357,12 @@ module.exports = class Client {
335
357
  } else if (['series', 'symbol', 'quote', 'session'].includes(packet.type)) {
336
358
  // console.info(packet);
337
359
  } else {
338
- console.warn('error', raw.toString());
360
+ // console.error('error', raw.toString());
339
361
  result('error', raw.toString())
340
362
  }
341
363
  }
342
- name = null;
343
- send = null;
364
+ // name = null;
365
+ // send = null;
344
366
  });
345
367
  });
346
368
  }
@@ -35,8 +35,7 @@ class TradingView {
35
35
  // }
36
36
 
37
37
  // await utils.pause();
38
- // old return 'eyJhbGciOiJSUzUxMiIsImtpZCI6IkdaeFUiLCJ0eXAiOiJKV1QifQ.eyJ1c2VyX2lkIjoxMjI1ODcxMiwiZXhwIjoxNjkwNzkzMjIwLCJpYXQiOjE2OTA3Nzg4MjAsInBsYW4iOiJwcm9fcHJlbWl1bSIsImV4dF9ob3VycyI6MSwicGVybSI6IiIsInN0dWR5X3Blcm0iOiJ0di1jaGFydF9wYXR0ZXJucyx0di1jaGFydHBhdHRlcm5zLHR2LXByb3N0dWRpZXMsdHYtdm9sdW1lYnlwcmljZSIsIm1heF9zdHVkaWVzIjoyNSwibWF4X2Z1bmRhbWVudGFscyI6MCwibWF4X2NoYXJ0cyI6OCwibWF4X2FjdGl2ZV9hbGVydHMiOjQwMCwibWF4X3N0dWR5X29uX3N0dWR5IjoyNCwibWF4X2FjdGl2ZV9wcmltaXRpdmVfYWxlcnRzIjo0MDAsIm1heF9hY3RpdmVfY29tcGxleF9hbGVydHMiOjQwMCwibWF4X2Nvbm5lY3Rpb25zIjo1MH0.pZQgM_oTZMnm1Qb4qBf6CfM9dHKltp2T3O2g_VVoK9l66UngEL8mLdeH7-ZpUZ8Y50rV5yr0ND8WRPsRH2w2izk_SNMD63p6GJzXXGPvILgirXW3SFa7HmaHv8Xp6SPhQTWb-ahyv_2GUIueKTvHWBNF5-eBnqYeoXQeWkRFsLA';
39
- return 'eyJhbGciOiJSUzUxMiIsImtpZCI6IkdaeFUiLCJ0eXAiOiJKV1QifQ.eyJ1c2VyX2lkIjoxMjI1ODcxMiwiZXhwIjoxNzA1OTE3OTE1LCJpYXQiOjE3MDU5MDM1MTUsInBsYW4iOiJwcm9fcHJlbWl1bSIsImV4dF9ob3VycyI6MSwicGVybSI6IiIsInN0dWR5X3Blcm0iOiJ0di1wcm9zdHVkaWVzLHR2LWNoYXJ0X3BhdHRlcm5zLHR2LXZvbHVtZWJ5cHJpY2UsdHYtY2hhcnRwYXR0ZXJucyIsIm1heF9zdHVkaWVzIjoyNSwibWF4X2Z1bmRhbWVudGFscyI6MTAsIm1heF9jaGFydHMiOjgsIm1heF9hY3RpdmVfYWxlcnRzIjo0MDAsIm1heF9zdHVkeV9vbl9zdHVkeSI6MjQsIm1heF9vdmVyYWxsX2FsZXJ0cyI6MjAwMCwibWF4X2FjdGl2ZV9wcmltaXRpdmVfYWxlcnRzIjo0MDAsIm1heF9hY3RpdmVfY29tcGxleF9hbGVydHMiOjQwMCwibWF4X2Nvbm5lY3Rpb25zIjo1MH0.Zoe1DAU3Fn2XpNNvI7uYK0JGHEsNs_OjTqBsnj8vsqWWeDDaOFPVO5m_DLuaUaSFZ4w3diQtgqfeovyPzzUCFDooGEF9FRypY9-zTjGbV0pbQajx-a8X3j79qxvLWe7VYg7cx5RQJGBoN7CGIPzrdpvNvVEYGb_QqXOPy_-F1BE';
38
+ return 'eyJhbGciOiJSUzUxMiIsImtpZCI6IkdaeFUiLCJ0eXAiOiJKV1QifQ.eyJ1c2VyX2lkIjoxMjI1ODcxMiwiZXhwIjoxNzI0OTI1MjkyLCJpYXQiOjE3MjQ5MTA4OTIsInBsYW4iOiJwcm9fcHJlbWl1bSIsImV4dF9ob3VycyI6MSwicGVybSI6InVzLXN0b2NrcyxuYXNkYXFfZ2lkcyxvdGMsbnlzZSxuYXNkYXEsYW1leCIsInN0dWR5X3Blcm0iOiJ0di1jaGFydHBhdHRlcm5zLHR2LXByb3N0dWRpZXMsdHYtY2hhcnRfcGF0dGVybnMsdHYtdm9sdW1lYnlwcmljZSIsIm1heF9zdHVkaWVzIjoyNSwibWF4X2Z1bmRhbWVudGFscyI6MTAsIm1heF9jaGFydHMiOjgsIm1heF9hY3RpdmVfYWxlcnRzIjo0MDAsIm1heF9zdHVkeV9vbl9zdHVkeSI6MjQsImZpZWxkc19wZXJtaXNzaW9ucyI6WyJyZWZib25kcyJdLCJtYXhfb3ZlcmFsbF9hbGVydHMiOjIwMDAsIm1heF9hY3RpdmVfcHJpbWl0aXZlX2FsZXJ0cyI6NDAwLCJtYXhfYWN0aXZlX2NvbXBsZXhfYWxlcnRzIjo0MDAsIm1heF9jb25uZWN0aW9ucyI6NTB9.UT2AdPBEAWk6HJEa6isX4gjVqlfThn8bgOVl3Xa36U36Qifg5xXz9Va8q7DWSY3Smaap2wX4cBF4UQXv1t66Q0TQvFudLuvA2dnetBAHYZSOJIi2PGdG1KuMaCRshUuuH9mxgw_xPGITAuQsbBPZJLTOxq5pQN05pZnedC7SaMs';
40
39
  }
41
40
 
42
41
  async getSymbol({ symbol, type }) {
@@ -60,9 +59,7 @@ class TradingView {
60
59
 
61
60
  createSession({ startsWith }) {
62
61
  let session = startsWith;
63
- while (session.length < 12) {
64
- session += letters[Math.floor(Math.random() * letters.length)];
65
- }
62
+ while (session.length < 12) session += letters[Math.floor(Math.random() * letters.length)];
66
63
  return session;
67
64
  }
68
65
 
@@ -229,10 +226,11 @@ class TradingView {
229
226
  }
230
227
 
231
228
  quoteAddSymbol({ session, symbol }) {
232
- // console.log('quoteAddSymbol', session, symbol);
229
+ const symbolString = symbol.split(':')[0] === 'BINANCE' ? symbol : '={"adjustment":"splits","currency-id":"USD","symbol":"' + symbol + '"}';
230
+ // console.log('quoteAddSymbol', session, symbol, symbolString);
233
231
  // return this.formatWSPacket({ packet: { m: 'quote_add_symbols', p: [session, symbol] } });
234
232
  return this.formatWSPacket({
235
- packet: { m: 'quote_add_symbols', p: [session, '={"adjustment":"splits","currency-id":"USD","symbol":"' + symbol + '"}'] }
233
+ packet: { m: 'quote_add_symbols', p: [session, symbolString] }
236
234
  });
237
235
  }
238
236
 
@@ -269,10 +267,10 @@ class TradingView {
269
267
  // symbolKey - id запрошенного тиккера
270
268
  // frame - рамка или окно для данной серии
271
269
  // increment - инкрементируемый номер изменений запрашиваемого графика в данном frame (таймефрейм)
272
- createSeries({ chartSession, frame = 'sds_1', increment = 's1', symbolKey, interval = '60', limit = 1000 }) {
270
+ createSeries({ chartSession, frame = 'sds_1', increment = 's1', symbolKey, period = '60', limit = 1000 }) {
273
271
  // console.warn('createSeries', chartSession, frame, increment, symbolKey, interval, limit);
274
272
  return this.formatWSPacket({
275
- packet: { m: 'create_series', p: [chartSession, frame, increment, symbolKey, interval, limit] },
273
+ packet: { m: 'create_series', p: [chartSession, frame, increment, symbolKey, period, limit] },
276
274
  });
277
275
  }
278
276
 
@@ -287,13 +285,22 @@ class TradingView {
287
285
  return this.formatWSPacket({ packet: { m: 'remove_study', p: [ session, st ] } });
288
286
  }
289
287
 
290
- modifySeries({ chartSession, frame = 'sds_1', increment = 's1', symbolKey, interval = '60', limit = 1000 }) {
288
+ modifySeries({ chartSession, frame = 'sds_1', increment = 's1', symbolKey, interval = '60' }) {
291
289
  // console.warn('modify_series', chartSession, frame, increment, symbolKey, interval, limit);
292
290
  return this.formatWSPacket({
293
291
  packet: { m: 'modify_series', p: [chartSession, frame, increment, symbolKey, interval] },
294
292
  });
295
293
  }
296
294
 
295
+ moreData({ chartSession, frame, add }) {
296
+ return this.formatWSPacket({ packet: { m: 'request_more_data', p: [chartSession, frame, add] } });
297
+ }
298
+
299
+ moreTickmark({chartSession, frame, add}) {
300
+ // "m":"request_more_tickmarks","p":["cs_lslcBHYaqGTT","sds_1",10]
301
+ return this.formatWSPacket({ packet: { m: 'request_more_tickmarks', p: [chartSession, frame, add] } });
302
+ }
303
+
297
304
  createPong({ value }) {
298
305
  return this.formatWSPacket({ packet: '~h~' + value });
299
306
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "market-data-tradingview-ws",
3
- "version": "0.0.12",
3
+ "version": "0.0.14",
4
4
  "description": "marketData from Tradingview ws",
5
5
  "main": "index.js",
6
6
  "scripts": {