market-data-tradingview-ws 0.0.2 → 0.0.4
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/index.js +2 -0
- package/lib/client.js +195 -69
- package/lib/tradingView.js +49 -21
- package/lib/utils.js +2 -1
- package/package.json +1 -1
- package/old.js +0 -141
- package/server.js +0 -91
package/index.js
CHANGED
package/lib/client.js
CHANGED
|
@@ -1,50 +1,93 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const tv = new tradingView();
|
|
1
|
+
/* eslint-disable prefer-const */
|
|
2
|
+
/* eslint-disable no-unused-vars */
|
|
3
|
+
'use strict';
|
|
5
4
|
|
|
6
|
-
const
|
|
7
|
-
|
|
5
|
+
const WebSocket = require('ws');
|
|
6
|
+
const tv = require('./tradingView.js');
|
|
8
7
|
|
|
8
|
+
const quotes = {
|
|
9
|
+
values: {},
|
|
9
10
|
set({ key, data }) {
|
|
10
11
|
if (data) {
|
|
11
|
-
this.values
|
|
12
|
+
this.values[key] = data;
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
add({ symbol }) {
|
|
16
|
+
for (let key in this.values) {
|
|
17
|
+
this.values[key].symbols.push(symbol);
|
|
18
|
+
return key;
|
|
12
19
|
}
|
|
20
|
+
return null;
|
|
13
21
|
},
|
|
14
22
|
get({ key }) {
|
|
15
|
-
return this.values
|
|
23
|
+
return this.values[key];
|
|
16
24
|
},
|
|
17
|
-
|
|
18
|
-
|
|
25
|
+
getBySymbol({ symbol }) {
|
|
26
|
+
for (let key in this.values) {
|
|
27
|
+
if (this.values[key].symbols.includes(symbol)) return key;
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
19
30
|
},
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
31
|
+
delete({ key, symbol }) {
|
|
32
|
+
const arr = this.values[key].symbols;
|
|
33
|
+
const index = arr.indexOf(symbol);
|
|
34
|
+
if (index !== -1) arr.splice(index, 1);
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const charts = {
|
|
39
|
+
values: {},
|
|
40
|
+
set({ key, data }) {
|
|
41
|
+
if (data) {
|
|
42
|
+
this.values[key] = data;
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
get({ key }) {
|
|
46
|
+
return this.values[key];
|
|
47
|
+
},
|
|
48
|
+
getBySymbolPeriod({ symbol, period }) {
|
|
49
|
+
for (let key of Object.keys(this.values)) {
|
|
50
|
+
if (this.values[key].symbol === symbol && this.values[key].period === period) return key;
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
26
53
|
},
|
|
27
54
|
};
|
|
28
55
|
|
|
56
|
+
const periods = {
|
|
57
|
+
60: '1',
|
|
58
|
+
300: '5',
|
|
59
|
+
900: '15',
|
|
60
|
+
1800: '30',
|
|
61
|
+
3600: '60',
|
|
62
|
+
14400: '240',
|
|
63
|
+
86400: '1D',
|
|
64
|
+
};
|
|
65
|
+
|
|
29
66
|
module.exports = class Client {
|
|
30
67
|
#ws;
|
|
31
|
-
#
|
|
68
|
+
#charts;
|
|
69
|
+
#quotes;
|
|
70
|
+
#resolved;
|
|
32
71
|
#token = null;
|
|
72
|
+
maxSymbolKey = 0;
|
|
33
73
|
constructor() {
|
|
34
|
-
this.#
|
|
74
|
+
this.#charts = charts;
|
|
75
|
+
this.#quotes = quotes;
|
|
76
|
+
this.#resolved = new Map();
|
|
35
77
|
}
|
|
36
78
|
|
|
37
79
|
close() {
|
|
80
|
+
// console.log(this.#sessions.values, this.#charts.values, this.#quotes.values);
|
|
38
81
|
this.#ws.close();
|
|
39
82
|
}
|
|
40
83
|
|
|
41
|
-
|
|
42
|
-
return this.#
|
|
84
|
+
getAll() {
|
|
85
|
+
return { quotes: this.#quotes.values, charts: this.#charts.values };
|
|
43
86
|
}
|
|
44
87
|
|
|
45
|
-
getSessionKeys() {
|
|
46
|
-
|
|
47
|
-
}
|
|
88
|
+
// getSessionKeys() {
|
|
89
|
+
// return this.#sessions.values.keys();
|
|
90
|
+
// }
|
|
48
91
|
|
|
49
92
|
async getToken({ login, pass }) {
|
|
50
93
|
return tv.getToken({ login, pass });
|
|
@@ -75,42 +118,125 @@ module.exports = class Client {
|
|
|
75
118
|
};
|
|
76
119
|
});
|
|
77
120
|
}
|
|
121
|
+
return null;
|
|
78
122
|
}
|
|
79
123
|
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
this.#ws.send(tv.wsCreateChartSession({ chartSession: key }));
|
|
87
|
-
this.#ws.send(tv.switchTimezone({ chartSession: key, tz: 'Etc/UTC' }));
|
|
88
|
-
} else {
|
|
89
|
-
this.#ws.send(tv.wsQuoteCreateSession({ session: key }));
|
|
90
|
-
this.#ws.send(tv.setFieldsChart({ session: key }));
|
|
91
|
-
}
|
|
92
|
-
return session;
|
|
124
|
+
createQuoteSession() {
|
|
125
|
+
const key = tv.createSession({ startsWith: 'qs_' });
|
|
126
|
+
this.#quotes.set({ key, data: { id: key, symbols: [] } });
|
|
127
|
+
this.#ws.send(tv.wsQuoteCreateSession({ session: key }));
|
|
128
|
+
this.#ws.send(tv.setFieldsChart({ session: key }));
|
|
129
|
+
return key;
|
|
93
130
|
}
|
|
94
131
|
|
|
95
|
-
|
|
96
|
-
const key =
|
|
97
|
-
this.#
|
|
132
|
+
createChartSession() {
|
|
133
|
+
const key = tv.createSession({ startsWith: 'cs_' });
|
|
134
|
+
this.#charts.set({ key, data: { id: key, symbol: '', period: '', symbolKey: '', frame: '', i: '' } });
|
|
135
|
+
this.#ws.send(tv.wsCreateChartSession({ chartSession: key }));
|
|
136
|
+
this.#ws.send(tv.switchTimezone({ chartSession: key, tz: 'Etc/UTC' }));
|
|
137
|
+
return key;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
addQuoteSymbols({ symbols }) {
|
|
141
|
+
const result = { add: [], exist: [] };
|
|
142
|
+
if (Object.keys(this.#quotes.values).length === 0) this.createQuoteSession();
|
|
143
|
+
symbols.forEach((symbol) => {
|
|
144
|
+
let key = this.#quotes.getBySymbol({ symbol });
|
|
145
|
+
if (key) {
|
|
146
|
+
result.exist.push(symbol);
|
|
147
|
+
} else {
|
|
148
|
+
key = this.#quotes.add({ symbol });
|
|
149
|
+
console.log(key, symbol);
|
|
150
|
+
this.#ws.send(tv.quoteAddSymbols({ session: key, symbols: [symbol] }));
|
|
151
|
+
// this.#ws.send(tv.quoteFastSymbols({ session: key, symbols }));
|
|
152
|
+
result.add.push(symbol);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
98
157
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
this.#
|
|
158
|
+
deleteQuoteSymbol({ symbol }) {
|
|
159
|
+
const key = this.#quotes.getBySymbol({ symbol });
|
|
160
|
+
if (!key) return 'unlisted';
|
|
161
|
+
this.#quotes.delete({ key, symbol });
|
|
162
|
+
this.#ws.send(tv.quoteRemoveSymbols({ session: key, symbol }));
|
|
163
|
+
return 'delete';
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
addChartSymbol({ symbol, period, limit }) {
|
|
167
|
+
let key = this.#charts.getBySymbolPeriod({ symbol, period });
|
|
168
|
+
if (key) return 'exist';
|
|
169
|
+
console.log('addChartSymbol', symbol, period, limit, key);
|
|
170
|
+
key = this.createChartSession();
|
|
171
|
+
const data = this.#charts.get({ key });
|
|
172
|
+
|
|
173
|
+
data.symbol = symbol;
|
|
174
|
+
data.period = period;
|
|
175
|
+
data.symbolKey = 'sds_sym_' + ++this.maxSymbolKey;
|
|
176
|
+
data.frame = 'sds_' + Object.keys(this.#charts.values).length;
|
|
177
|
+
data.i = 's' + 1;
|
|
178
|
+
|
|
179
|
+
this.#ws.send(tv.resolveSymbol({ chartSession: key, symbolKey: data.symbolKey, symbol }));
|
|
180
|
+
this.#resolved.set(symbol, { key: data.symbolKey });
|
|
181
|
+
this.#ws.send(
|
|
182
|
+
tv.createSeries({
|
|
183
|
+
chartSession: key,
|
|
184
|
+
frame: data.frame,
|
|
185
|
+
increment: data.i,
|
|
186
|
+
symbolKey: data.symbolKey,
|
|
187
|
+
interval: periods[period],
|
|
188
|
+
limit,
|
|
189
|
+
}),
|
|
190
|
+
);
|
|
191
|
+
console.log('addChartSymbol1', periods[period], data);
|
|
192
|
+
this.#charts.set({ key, data });
|
|
193
|
+
|
|
194
|
+
return 'add';
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
updateChartSymbol({ symbol, period, limit, exist }) {
|
|
198
|
+
let key = this.#charts.getBySymbolPeriod({ symbol: exist.symbol, period: exist.period });
|
|
199
|
+
const data = this.#charts.get({ key });
|
|
200
|
+
|
|
201
|
+
console.log('updateChartSymbol', symbol, exist.symbol, data);
|
|
202
|
+
|
|
203
|
+
if (this.#resolved.has(symbol)) {
|
|
204
|
+
data.symbolKey = this.#resolved.get(symbol).key;
|
|
205
|
+
} else {
|
|
206
|
+
data.symbolKey = 'sds_sym_' + ++this.maxSymbolKey;
|
|
207
|
+
this.#ws.send(tv.resolveSymbol({ chartSession: key, symbolKey: data.symbolKey, symbol }));
|
|
208
|
+
this.#resolved.set(symbol, { key: data.symbolKey });
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
data.symbol = symbol;
|
|
212
|
+
data.period = period;
|
|
213
|
+
data.i = 's' + (parseInt(data.i.replace(/[^0-9]/g, '')) + 1);
|
|
214
|
+
this.#ws.send(
|
|
215
|
+
tv.modifySeries({
|
|
216
|
+
chartSession: key,
|
|
217
|
+
frame: data.frame,
|
|
218
|
+
increment: data.i,
|
|
219
|
+
symbolKey: data.symbolKey,
|
|
220
|
+
interval: periods[period],
|
|
221
|
+
limit,
|
|
222
|
+
}),
|
|
223
|
+
);
|
|
224
|
+
console.log('updateChartSymbol1', key, data);
|
|
225
|
+
this.#charts.set({ key, data });
|
|
226
|
+
return 'update';
|
|
103
227
|
}
|
|
104
228
|
|
|
105
229
|
messageListner({ result }) {
|
|
106
230
|
this.#ws.on('message', (raw) => {
|
|
107
231
|
if (this.#ws.readyState !== this.#ws.OPEN) return;
|
|
108
232
|
|
|
233
|
+
// console.log('raw: ', JSON.stringify(raw.toString()));
|
|
109
234
|
let session = '';
|
|
110
235
|
tv.parsePacket({ str: raw }).forEach((packet) => {
|
|
111
236
|
if (packet === undefined) {
|
|
112
237
|
console.log('undefined: ', raw.toString());
|
|
113
238
|
} else {
|
|
239
|
+
// eslint-disable-next-line no-lonely-if
|
|
114
240
|
if (packet.type === 'ping') {
|
|
115
241
|
// console.info('pong', packet);
|
|
116
242
|
this.#ws.send(tv.createPong({ value: packet.data }));
|
|
@@ -118,39 +244,39 @@ module.exports = class Client {
|
|
|
118
244
|
console.warn('protocol_error', packet);
|
|
119
245
|
this.close();
|
|
120
246
|
} else if (packet.type === 'qsd') {
|
|
121
|
-
session = sessions.get({ key: packet.data[0] });
|
|
122
|
-
// console.log('qsd', packet.data, session);
|
|
123
247
|
const send = packet.data[1].v;
|
|
248
|
+
const name = send.bid !== undefined && send.ask !== undefined ? 'levelI' : 'data';
|
|
124
249
|
send.symbol = packet.data[1].n;
|
|
125
|
-
|
|
126
|
-
result({ packet: send });
|
|
250
|
+
result(name, send);
|
|
127
251
|
} else if (['du', 'timescale_update'].includes(packet.type)) {
|
|
128
|
-
session =
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
252
|
+
session = this.#charts.get({ key: packet.data[0] });
|
|
253
|
+
if (packet.data[1][session.frame].s.length > 0) {
|
|
254
|
+
const send = {
|
|
255
|
+
symbol: session.symbol,
|
|
256
|
+
period: session.period,
|
|
257
|
+
chart: [],
|
|
258
|
+
end: packet.data[1][session.frame].lbs.bar_close_time,
|
|
259
|
+
};
|
|
260
|
+
for (let each of packet.data[1][session.frame].s) {
|
|
261
|
+
send.chart.push({
|
|
262
|
+
open: each.v[1],
|
|
263
|
+
high: each.v[2],
|
|
264
|
+
low: each.v[3],
|
|
265
|
+
close: each.v[4],
|
|
266
|
+
timestamp: each.v[0] * 1000,
|
|
267
|
+
// turnover: bar.TradeCount,
|
|
268
|
+
volume: each.v[5],
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
result(packet.type === 'du' ? 'chart_update' : 'chart_history', send);
|
|
272
|
+
}
|
|
273
|
+
} else if (['series', 'symbol', 'quote', 'session'].includes(packet.type)) {
|
|
274
|
+
// console.info(packet);
|
|
146
275
|
} else {
|
|
147
276
|
console.warn('error', raw.toString());
|
|
148
277
|
}
|
|
149
|
-
// else {
|
|
150
|
-
// console.log('received: %s', raw);
|
|
151
|
-
// }
|
|
152
278
|
}
|
|
153
279
|
});
|
|
154
280
|
});
|
|
155
281
|
}
|
|
156
|
-
}
|
|
282
|
+
};
|
package/lib/tradingView.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/* eslint-disable no-unused-vars */
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
// const Utils = require('./utils.js');
|
|
5
|
+
// const utils = new Utils();
|
|
3
6
|
|
|
4
7
|
const letters = 'abcdefghijklmnopqrstuvwxyz';
|
|
5
8
|
const cleanerRgx = /~h~/g;
|
|
6
9
|
const splitterRgx = /~m~[0-9]{1,}~m~/g;
|
|
7
10
|
const urlSymbolSearch = 'https://symbol-search.tradingview.com/symbol_search';
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
class TradingView {
|
|
10
13
|
async getToken({ login, pass }) {
|
|
11
14
|
const url = 'https://www.tradingview.com/accounts/signin/';
|
|
12
15
|
const headers = {
|
|
@@ -31,7 +34,7 @@ module.exports = class tradingView {
|
|
|
31
34
|
// return data.user.auth_token;
|
|
32
35
|
// }
|
|
33
36
|
|
|
34
|
-
// await
|
|
37
|
+
// await utils.pause();
|
|
35
38
|
return 'eyJhbGciOiJSUzUxMiIsImtpZCI6IkdaeFUiLCJ0eXAiOiJKV1QifQ.eyJ1c2VyX2lkIjoxMjI1ODcxMiwiZXhwIjoxNjkwNzkzMjIwLCJpYXQiOjE2OTA3Nzg4MjAsInBsYW4iOiJwcm9fcHJlbWl1bSIsImV4dF9ob3VycyI6MSwicGVybSI6IiIsInN0dWR5X3Blcm0iOiJ0di1jaGFydF9wYXR0ZXJucyx0di1jaGFydHBhdHRlcm5zLHR2LXByb3N0dWRpZXMsdHYtdm9sdW1lYnlwcmljZSIsIm1heF9zdHVkaWVzIjoyNSwibWF4X2Z1bmRhbWVudGFscyI6MCwibWF4X2NoYXJ0cyI6OCwibWF4X2FjdGl2ZV9hbGVydHMiOjQwMCwibWF4X3N0dWR5X29uX3N0dWR5IjoyNCwibWF4X2FjdGl2ZV9wcmltaXRpdmVfYWxlcnRzIjo0MDAsIm1heF9hY3RpdmVfY29tcGxleF9hbGVydHMiOjQwMCwibWF4X2Nvbm5lY3Rpb25zIjo1MH0.pZQgM_oTZMnm1Qb4qBf6CfM9dHKltp2T3O2g_VVoK9l66UngEL8mLdeH7-ZpUZ8Y50rV5yr0ND8WRPsRH2w2izk_SNMD63p6GJzXXGPvILgirXW3SFa7HmaHv8Xp6SPhQTWb-ahyv_2GUIueKTvHWBNF5-eBnqYeoXQeWkRFsLA';
|
|
36
39
|
}
|
|
37
40
|
|
|
@@ -97,18 +100,23 @@ module.exports = class tradingView {
|
|
|
97
100
|
return { type: 'ping', data: packet }; // Ping
|
|
98
101
|
} else if (packet.m && packet.p) {
|
|
99
102
|
// Normal packet
|
|
100
|
-
if (packet.m.startsWith('du')) return { type: packet.m, data: packet.p };
|
|
101
|
-
if (packet.m.startsWith('timescale') || packet.m.startsWith('qsd')
|
|
103
|
+
// if (packet.m.startsWith('du')) return { type: packet.m, data: packet.p };
|
|
104
|
+
if (packet.m.startsWith('timescale') || packet.m.startsWith('qsd') || packet.m.startsWith('du'))
|
|
105
|
+
return { type: packet.m, data: packet.p };
|
|
106
|
+
// return new Map([
|
|
107
|
+
// ['type', packet.m],
|
|
108
|
+
// ['data', packet.p],
|
|
109
|
+
// ]);
|
|
102
110
|
if (packet.m.startsWith('series') || packet.m.startsWith('symbol') || packet.m.startsWith('quote')) {
|
|
103
111
|
const type = packet.m.split('_');
|
|
104
112
|
return { type: type[0], data: { sub: type[1], detail: packet.p, time: packet.t_ms ? packet.t_ms : Date.now() } };
|
|
105
113
|
} else {
|
|
106
|
-
return { type: 'msg', data:
|
|
114
|
+
return { type: 'msg', data: packet.data };
|
|
107
115
|
}
|
|
108
116
|
// const session = packet.p[0];
|
|
109
117
|
|
|
110
118
|
// if (session && this.#sessions[session]) {
|
|
111
|
-
// this.#sessions[session].onData(
|
|
119
|
+
// this.#sessions[session].onData(packet);
|
|
112
120
|
//
|
|
113
121
|
// }
|
|
114
122
|
} else if (packet.session_id) {
|
|
@@ -175,39 +183,57 @@ module.exports = class tradingView {
|
|
|
175
183
|
});
|
|
176
184
|
}
|
|
177
185
|
|
|
178
|
-
quoteAddSymbols({ session,
|
|
179
|
-
|
|
186
|
+
quoteAddSymbols({ session, symbols }) {
|
|
187
|
+
console.log('quoteAddSymbols', session, symbols);
|
|
188
|
+
return this.formatWSPacket({ packet: { m: 'quote_add_symbols', p: [session, ...symbols] } });
|
|
180
189
|
}
|
|
181
190
|
|
|
182
|
-
|
|
183
|
-
|
|
191
|
+
quoteFastSymbols({ session, symbols }) {
|
|
192
|
+
console.log('quoteFastSymbols', session, symbols);
|
|
193
|
+
return this.formatWSPacket({
|
|
194
|
+
packet: { m: 'quote_fast_symbols', p: [session, '={"adjustment":"splits","currency-id":"USD","symbol":"BATS:PYPL"}', ...symbols] },
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
quoteRemoveSymbols({ session, symbol }) {
|
|
199
|
+
return this.formatWSPacket({ packet: { m: 'quote_remove_symbols', p: [session, symbol] } });
|
|
184
200
|
}
|
|
185
201
|
|
|
186
202
|
switchTimezone({ chartSession, tz = 'Etc/UTC' }) {
|
|
187
203
|
return this.formatWSPacket({ packet: { m: 'switch_timezone', p: [chartSession, tz] } });
|
|
188
204
|
}
|
|
189
205
|
|
|
190
|
-
resolveSymbol({ chartSession,
|
|
206
|
+
resolveSymbol({ chartSession, symbolKey, symbol }) {
|
|
207
|
+
console.log('resolveSymbol', chartSession, symbolKey, symbol);
|
|
191
208
|
return this.formatWSPacket({
|
|
192
209
|
packet: {
|
|
193
210
|
m: 'resolve_symbol',
|
|
194
|
-
p: [chartSession,
|
|
211
|
+
p: [chartSession, symbolKey, '=' + JSON.stringify({ symbol, adjustment: 'splits', session: 'extended' })],
|
|
195
212
|
},
|
|
196
213
|
});
|
|
197
214
|
}
|
|
198
215
|
|
|
199
|
-
|
|
216
|
+
// symbolKey - id запрошенного тиккера
|
|
217
|
+
// frame - рамка или окно для данной серии
|
|
218
|
+
// increment - инкрементируемый номер изменений запрашиваемого графика в данном frame (таймефрейм)
|
|
219
|
+
createSeries({ chartSession, frame = 'sds_1', increment = 's1', symbolKey, interval = '60', limit = 1000 }) {
|
|
220
|
+
console.log('createSeries', chartSession, frame, increment, symbolKey, interval, limit);
|
|
200
221
|
return this.formatWSPacket({
|
|
201
|
-
packet: { m: 'create_series', p: [chartSession,
|
|
222
|
+
packet: { m: 'create_series', p: [chartSession, frame, increment, symbolKey, interval, limit] },
|
|
202
223
|
});
|
|
203
224
|
}
|
|
204
225
|
|
|
205
|
-
|
|
226
|
+
// study = {id: st1 (st2, st3, st4 ...), name: 'Volume@tv-basicstudies-221', details: { length: 20, col_prev_close: false}
|
|
227
|
+
createStudy({ chartSession, study, series = 'st1', frame = 'sds_1' }) {
|
|
206
228
|
return this.formatWSPacket({
|
|
207
|
-
packet: {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
229
|
+
packet: { m: 'create_study', p: [chartSession, study.id, series, frame, study.name, study.details] },
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
modifySeries({ chartSession, frame = 'sds_1', increment = 's1', symbolKey, interval = '60', limit = 1000 }) {
|
|
234
|
+
console.log('modify_series', chartSession, frame, increment, symbolKey, interval, limit);
|
|
235
|
+
return this.formatWSPacket({
|
|
236
|
+
packet: { m: 'modify_series', p: [chartSession, frame, increment, symbolKey, interval] },
|
|
211
237
|
});
|
|
212
238
|
}
|
|
213
239
|
|
|
@@ -215,3 +241,5 @@ module.exports = class tradingView {
|
|
|
215
241
|
return this.formatWSPacket({ packet: '~h~' + value });
|
|
216
242
|
}
|
|
217
243
|
}
|
|
244
|
+
|
|
245
|
+
module.exports = new TradingView();
|
package/lib/utils.js
CHANGED
package/package.json
CHANGED
package/old.js
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
process.title = 'socket_client';
|
|
4
|
-
|
|
5
|
-
const urlWS = 'wss://data.tradingview.com/socket.io/websocket';
|
|
6
|
-
|
|
7
|
-
const login = 'sulimenko@ptfin.kz';
|
|
8
|
-
const pass = '7n8zGZ8v27pH';
|
|
9
|
-
const token = '';
|
|
10
|
-
const interval = '5';
|
|
11
|
-
const total_candle = 100;
|
|
12
|
-
|
|
13
|
-
const stop = async () => {
|
|
14
|
-
const closed = new Promise((resolve) => {
|
|
15
|
-
setTimeout(() => {
|
|
16
|
-
resolve(console.log('exit'));
|
|
17
|
-
}, 1000);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
for (const sessionKey of sessions.values.keys()) {
|
|
21
|
-
sessions.values.delete(sessionKey);
|
|
22
|
-
console.log('session clear: ', sessionKey);
|
|
23
|
-
}
|
|
24
|
-
socket.close();
|
|
25
|
-
await closed;
|
|
26
|
-
process.exit(1);
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
function changeWSSymbol({ key, chartkey, symbolId }) {
|
|
30
|
-
const s = sessions.get({ key });
|
|
31
|
-
const c = sessions.get({ key: chartkey });
|
|
32
|
-
const queue = [];
|
|
33
|
-
|
|
34
|
-
// console.log(s, symbolId, id, c, chartkey, sessions);
|
|
35
|
-
if (s.symbolId) queue.push(tv.removeSymbols({ session: s.main, symbolId: s.symbolId }));
|
|
36
|
-
queue.push(tv.addSymbols({ session: s.main, symbolId }));
|
|
37
|
-
queue.push(tv.resolveSymbol({ chartSession: c.main, id, symbolId }));
|
|
38
|
-
|
|
39
|
-
queue.forEach((each) => {
|
|
40
|
-
console.log(each);
|
|
41
|
-
socket.send(each);
|
|
42
|
-
});
|
|
43
|
-
id++;
|
|
44
|
-
sessions.set({ key, val: { main: key, symbolId } });
|
|
45
|
-
sessions.set({ key: chartkey, val: { main: chartkey, symbolId } });
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
(async () => {
|
|
49
|
-
|
|
50
|
-
// const data = await tv.getSymbol({ symbol: 'TSLA', type: 'stock' }); // NASDAQ:TSLA
|
|
51
|
-
// const data = await tv.getSymbol({ symbol: 'MRNA', type: 'stock' }); // NASDAQ:MRNA
|
|
52
|
-
const data = await tv.getSymbol({ symbol: 'BTCUSDT', type: 'crypto' });
|
|
53
|
-
// console.log('symbol: ', data);
|
|
54
|
-
|
|
55
|
-
const symbolId = tv.getSymbolId({ data });
|
|
56
|
-
console.log(symbolId);
|
|
57
|
-
|
|
58
|
-
socket = new WebSocket(urlWS, { origin: 'https://data.tradingview.com' });
|
|
59
|
-
// // const socket = new WebSocket('wss://data.tradingview.com/socket.io/websocket?&type=chart', { origin: 'https://s.tradingview.com' });
|
|
60
|
-
|
|
61
|
-
const sessionKey = tv.createSession({ startsWith: 'qs_' });
|
|
62
|
-
const chartSessionKey = tv.createSession({ startsWith: 'cs_' });
|
|
63
|
-
sessions.set({ key: sessionKey, val: { main: sessionKey } });
|
|
64
|
-
sessions.set({ key: chartSessionKey, val: { main: chartSessionKey } });
|
|
65
|
-
let session = sessions.get({ key: sessionKey });
|
|
66
|
-
let chartSession = sessions.get({ key: chartSessionKey });
|
|
67
|
-
// console.log(session);
|
|
68
|
-
|
|
69
|
-
socket.on('open', (e) => {
|
|
70
|
-
console.log('open');
|
|
71
|
-
socket.send(auth());
|
|
72
|
-
|
|
73
|
-
socket.send(tv.wsCreateChartSession({ chartSession: chartSession.main }));
|
|
74
|
-
socket.send(tv.switchTimezone({ chartSession: chartSession.main, tz: 'Etc/UTC' }));
|
|
75
|
-
socket.send(tv.wsCreateSession({ session: session.main }));
|
|
76
|
-
// socket.send(tv.resolveSymbol({ chartSession, id: 1, symbolId }));
|
|
77
|
-
changeWSSymbol({ key: session.main, chartkey: chartSession.main, symbolId });
|
|
78
|
-
|
|
79
|
-
socket.send(tv.setFieldsChart({ session: session.main }));
|
|
80
|
-
socket.send(tv.createSeries({ chartSession: chartSession.main, id: 1, interval, total_candle }));
|
|
81
|
-
|
|
82
|
-
// setTimeout(() => {
|
|
83
|
-
// addWSSymbol({ key: session.main, symbolId: 'NASDAQ:TSLA' });
|
|
84
|
-
setTimeout(() => {
|
|
85
|
-
changeWSSymbol({ key: session.main, chartkey: chartSession.main, symbolId: 'NASDAQ:TSLA' });
|
|
86
|
-
}, 10000);
|
|
87
|
-
// }, 10000);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
socket.on('message', (raw) => {
|
|
91
|
-
const response = tv.parsePacket({ readyState: socket.readyState, str: raw });
|
|
92
|
-
// console.log(response, response === undefined);
|
|
93
|
-
response.forEach((packet) => {
|
|
94
|
-
// console.log(packet);
|
|
95
|
-
if (packet === undefined) {
|
|
96
|
-
console.log('undefined: ', raw.toString());
|
|
97
|
-
} else {
|
|
98
|
-
if (packet.type === 'ping') {
|
|
99
|
-
// console.log('pong', packet);
|
|
100
|
-
socket.send(tv.createPong({ value: packet.data }));
|
|
101
|
-
} else if (packet.type === 'protocol_error') {
|
|
102
|
-
console.log('protocol_error', packet);
|
|
103
|
-
socket.close();
|
|
104
|
-
} else if (packet.type === 'qsd') {
|
|
105
|
-
session = sessions.get({ key: packet.data[0] });
|
|
106
|
-
// console.log(packet.data, session, sessions);
|
|
107
|
-
const send = packet.data[1].v;
|
|
108
|
-
send.symbol = session.symbolId;
|
|
109
|
-
console.log(JSON.stringify([packet.type, send]));
|
|
110
|
-
} else if (['du', 'timescale_update'].includes(packet.type)) {
|
|
111
|
-
session = sessions.get({ key: packet.data[0] });
|
|
112
|
-
// console.log(packet.data, session, sessions);
|
|
113
|
-
const arr = [];
|
|
114
|
-
for (var key in packet.data[1].s1.s) {
|
|
115
|
-
const send = {
|
|
116
|
-
symbol: session.symbolId,
|
|
117
|
-
end: packet.data[1].s1.s[key + 1] !== undefined ? packet.data[1].s1.s[key + 1].v[0] : packet.data[1].s1.lbs.bar_close_time,
|
|
118
|
-
};
|
|
119
|
-
// console.log(key, packet.data[1].s1.s);
|
|
120
|
-
[send.start, send.open, send.low, send.high, send.close, send.passed] = packet.data[1].s1.s[key].v;
|
|
121
|
-
// console.log(send);
|
|
122
|
-
arr.push(send);
|
|
123
|
-
}
|
|
124
|
-
// [send.start, send.open, send.low, send.high, send.close, send.passed] = packet.data[1].s1.s.v;
|
|
125
|
-
console.log(JSON.stringify([packet.type, arr]));
|
|
126
|
-
} else {
|
|
127
|
-
console.log('error', raw.toString());
|
|
128
|
-
}
|
|
129
|
-
// else {
|
|
130
|
-
// console.log('received: %s', raw);
|
|
131
|
-
// }
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
// // setTimeout(async () => {
|
|
136
|
-
// // console.log('exit');
|
|
137
|
-
// // }, 3000);
|
|
138
|
-
|
|
139
|
-
process.on('SIGINT', stop);
|
|
140
|
-
process.on('SIGTERM', stop);
|
|
141
|
-
})();
|
package/server.js
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
process.title = 'marketDataClient';
|
|
4
|
-
|
|
5
|
-
const Client = './lib/client.js';
|
|
6
|
-
const Utils = './lib/utils.js';
|
|
7
|
-
const u = new Utils();
|
|
8
|
-
|
|
9
|
-
const url = 'wss://data.tradingview.com/socket.io/websocket';
|
|
10
|
-
const options = { origin: 'https://data.tradingview.com' };
|
|
11
|
-
|
|
12
|
-
const login = 'sulimenko@ptfin.kz';
|
|
13
|
-
const pass = '7n8zGZ8v27pH';
|
|
14
|
-
|
|
15
|
-
const interval = '5';
|
|
16
|
-
const total_candle = 100;
|
|
17
|
-
|
|
18
|
-
const marketData = new Client();
|
|
19
|
-
|
|
20
|
-
const clients = new Map();
|
|
21
|
-
const symbolQuotes = new Map();
|
|
22
|
-
|
|
23
|
-
const stop = async () => {
|
|
24
|
-
const closed = new Promise((resolve) => {
|
|
25
|
-
setTimeout(() => {
|
|
26
|
-
resolve(console.log('exit'));
|
|
27
|
-
}, 1000);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
// for (const sessionKey of marketData.getSessionKeys()) {
|
|
31
|
-
// let data = marketData.getSession({ key: sessionKey });
|
|
32
|
-
// console.log('session clear: ', sessionKey, data);
|
|
33
|
-
// }
|
|
34
|
-
marketData.close();
|
|
35
|
-
await closed;
|
|
36
|
-
console.log(clients, symbolQuotes);
|
|
37
|
-
process.exit(1);
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const result = ({ packet }) => {
|
|
41
|
-
let data = symbolQuotes.get(packet.symbol);
|
|
42
|
-
if (!data) data = {};
|
|
43
|
-
Object.keys(packet).forEach((key) => {
|
|
44
|
-
// console.log(key, data, packet);
|
|
45
|
-
data[key] = packet[key];
|
|
46
|
-
});
|
|
47
|
-
symbolQuotes.set(packet.symbol, data);
|
|
48
|
-
|
|
49
|
-
clients.forEach((each) => {
|
|
50
|
-
if (each.quote.includes(packet.symbol)) {
|
|
51
|
-
each.client({ acc: each.acc, packet });
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
// function changeWSSymbol({ key, chartkey, symbolId }) {
|
|
57
|
-
// const s = sessions.get({ key });
|
|
58
|
-
// const c = sessions.get({ key: chartkey });
|
|
59
|
-
// const queue = [];
|
|
60
|
-
|
|
61
|
-
// // console.log(s, symbolId, id, c, chartkey, sessions);
|
|
62
|
-
// if (s.symbolId) queue.push(tv.removeSymbols({ session: s.main, symbolId: s.symbolId }));
|
|
63
|
-
// queue.push(tv.addSymbols({ session: s.main, symbolId }));
|
|
64
|
-
// queue.push(tv.resolveSymbol({ chartSession: c.main, id, symbolId }));
|
|
65
|
-
|
|
66
|
-
// queue.forEach((each) => {
|
|
67
|
-
// console.log(each);
|
|
68
|
-
// socket.send(each);
|
|
69
|
-
// });
|
|
70
|
-
// id++;
|
|
71
|
-
// sessions.set({ key, val: { main: key, symbolId } });
|
|
72
|
-
// sessions.set({ key: chartkey, val: { main: chartkey, symbolId } });
|
|
73
|
-
// }
|
|
74
|
-
|
|
75
|
-
(async () => {
|
|
76
|
-
clients.set('123', { acc: '123', quote: ['NASDAQ:TSLA', 'NASDAQ:META'], client: ({ acc, packet }) => console.log(acc, packet) });
|
|
77
|
-
clients.set('U444', { acc: 'U444', quote: ['NASDAQ:MRNA', 'NASDAQ:LI'], client: ({ acc, packet }) => console.log(acc, packet) });
|
|
78
|
-
|
|
79
|
-
const status = await marketData.connect({ url, options, login, pass, result });
|
|
80
|
-
console.log(status);
|
|
81
|
-
// await u.pause(2000);
|
|
82
|
-
marketData.createSession({ type: 'chart' });
|
|
83
|
-
marketData.createSession({ type: 'quote' });
|
|
84
|
-
|
|
85
|
-
clients.forEach((each) => {
|
|
86
|
-
marketData.addQuoteSymbols({ symbolIds: each.quote });
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
process.on('SIGINT', stop);
|
|
90
|
-
process.on('SIGTERM', stop);
|
|
91
|
-
})();
|