@mathieuc/tradingview 2.0.7 → 3.0.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/.eslintrc.js +18 -16
- package/README.md +26 -96
- package/examples/AllPrivateIndicators.js +35 -0
- package/examples/BuiltInIndicator.js +41 -0
- package/examples/CustomChartType.js +121 -0
- package/examples/CustomTimeframe.js +29 -0
- package/examples/Errors.js +153 -0
- package/examples/GraphicIndicator.js +34 -0
- package/examples/MultipleSyncFetch.js +38 -0
- package/examples/Search.js +15 -0
- package/examples/SimpleChart.js +63 -0
- package/examples/_RenkoIndicator.js +41 -0
- package/main.js +9 -352
- package/package.json +10 -2
- package/src/chart/graphicParser.js +308 -0
- package/src/chart/session.js +367 -0
- package/src/chart/study.js +403 -0
- package/src/classes/BuiltInIndicator.js +119 -0
- package/src/classes/PineIndicator.js +132 -0
- package/src/client.js +280 -0
- package/src/miscRequests.js +525 -0
- package/src/protocol.js +54 -0
- package/src/quote/market.js +123 -0
- package/src/quote/session.js +124 -0
- package/src/types.js +32 -0
- package/src/utils.js +8 -0
- package/test.js +155 -0
- package/tests/alerts/discordWH.js +76 -76
- package/tests/alerts/drawings.js +41 -41
- package/tests/alerts/envLoader.js +11 -11
- package/tests/alerts/manualAlert.js +77 -77
- package/tests/alerts/sentNotifs.js +16 -16
- package/tests/alerts/strategyAlert.js +94 -94
- package/tests/analysis.js +13 -13
- package/tests/indicator.js +42 -42
- package/tests/strategy.js +56 -0
- package/miscRequests.js +0 -420
|
@@ -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,352 +1,9 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const packets = str.replace(/~h~/g, '').split(/~m~[0-9]{1,}~m~/g).map((p) => {
|
|
11
|
-
if (!p) return false;
|
|
12
|
-
try {
|
|
13
|
-
return JSON.parse(p);
|
|
14
|
-
} catch (error) {
|
|
15
|
-
console.warn('Cant parse', p);
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
}).filter((p) => p);
|
|
19
|
-
|
|
20
|
-
packets.forEach((packet) => {
|
|
21
|
-
if (packet.m === 'protocol_error') {
|
|
22
|
-
return onPacket({
|
|
23
|
-
type: 'error',
|
|
24
|
-
syntax: packet.p[0],
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (packet.m && packet.p) {
|
|
29
|
-
return onPacket({
|
|
30
|
-
type: packet.m,
|
|
31
|
-
session: packet.p[0],
|
|
32
|
-
data: packet.p[1],
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (typeof packet === 'number') return onPacket({ type: 'ping', ping: packet });
|
|
37
|
-
|
|
38
|
-
return onPacket({ type: 'info', ...packet });
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function genSession() {
|
|
43
|
-
let r = '';
|
|
44
|
-
const c = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
45
|
-
for (let i = 0; i < 12; i += 1) r += c.charAt(Math.floor(Math.random() * c.length));
|
|
46
|
-
return `qs_${r}`;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
module.exports = (autoInit = true) => {
|
|
50
|
-
const callbacks = {
|
|
51
|
-
connected: [],
|
|
52
|
-
disconnected: [],
|
|
53
|
-
logged: [],
|
|
54
|
-
subscribed: [],
|
|
55
|
-
ping: [],
|
|
56
|
-
price: [],
|
|
57
|
-
data: [],
|
|
58
|
-
|
|
59
|
-
error: [],
|
|
60
|
-
event: [],
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
const chartEventNames = [
|
|
64
|
-
'du', 'timescale_update',
|
|
65
|
-
'series_loading', 'series_completed', 'series_error',
|
|
66
|
-
'symbol_resolved', 'symbol_error',
|
|
67
|
-
'study_loading', 'study_error',
|
|
68
|
-
];
|
|
69
|
-
const chartCBs = {};
|
|
70
|
-
|
|
71
|
-
function handleEvent(ev, ...data) {
|
|
72
|
-
callbacks[ev].forEach((e) => e(...data));
|
|
73
|
-
callbacks.event.forEach((e) => e(ev, ...data));
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function handleError(...msgs) {
|
|
77
|
-
if (callbacks.error.length === 0) console.error(...msgs);
|
|
78
|
-
else handleEvent('error', ...msgs);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const ws = new WebSocket('wss://widgetdata.tradingview.com/socket.io/websocket', {
|
|
82
|
-
origin: 'https://s.tradingview.com',
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
let logged = false;
|
|
86
|
-
/** ID of the quote session */
|
|
87
|
-
let sessionId = '';
|
|
88
|
-
|
|
89
|
-
/** List of subscribed symbols */
|
|
90
|
-
let subscribed = [];
|
|
91
|
-
|
|
92
|
-
/** Websockets status calls */
|
|
93
|
-
let isEnded = false;
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Send a custom packet
|
|
97
|
-
* @param {string} t Packet type
|
|
98
|
-
* @param {string[]} p Packet data
|
|
99
|
-
* @example
|
|
100
|
-
* // Subscribe manualy to BTCEUR
|
|
101
|
-
* send('quote_add_symbols', [sessionId, 'BTCEUR']);
|
|
102
|
-
*/
|
|
103
|
-
function send(t, p = []) {
|
|
104
|
-
if (!sessionId) return;
|
|
105
|
-
|
|
106
|
-
const msg = JSON.stringify({ m: t, p });
|
|
107
|
-
ws.send(`~m~${msg.length}~m~${msg}`);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
ws.on('open', () => {
|
|
111
|
-
sessionId = genSession();
|
|
112
|
-
handleEvent('connected');
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
ws.on('close', () => {
|
|
116
|
-
logged = false;
|
|
117
|
-
sessionId = '';
|
|
118
|
-
handleEvent('disconnected');
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
ws.on('message', parse);
|
|
122
|
-
|
|
123
|
-
onPacket = (packet) => {
|
|
124
|
-
if (packet.type === 'ping') {
|
|
125
|
-
const pingStr = `~h~${packet.ping}`;
|
|
126
|
-
ws.send(`~m~${pingStr.length}~m~${pingStr}`);
|
|
127
|
-
handleEvent('ping', packet.ping);
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (packet.type === 'quote_completed' && packet.data) {
|
|
132
|
-
handleEvent('subscribed', packet.data);
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (packet.type === 'qsd' && packet.data.n && packet.data.v.lp) {
|
|
137
|
-
handleEvent('price', {
|
|
138
|
-
symbol: packet.data.n,
|
|
139
|
-
price: packet.data.v.lp,
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if (chartEventNames.includes(packet.type) && chartCBs[packet.session]) {
|
|
146
|
-
chartCBs[packet.session](packet);
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (!logged && packet.type === 'info') {
|
|
151
|
-
if (autoInit) {
|
|
152
|
-
send('set_auth_token', ['unauthorized_user_token']);
|
|
153
|
-
send('quote_create_session', [sessionId]);
|
|
154
|
-
send('quote_set_fields', [sessionId, 'lp']);
|
|
155
|
-
|
|
156
|
-
subscribed.forEach((symbol) => send('quote_add_symbols', [sessionId, symbol]));
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
handleEvent('logged', packet);
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (packet.type === 'error') {
|
|
164
|
-
handleError(`Market API critical error: ${packet.syntax}`);
|
|
165
|
-
ws.close();
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
handleEvent('data', packet);
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
return {
|
|
173
|
-
/** Event listener
|
|
174
|
-
* @param { 'connected' | 'disconnected' | 'logged'
|
|
175
|
-
* | 'subscribed' | 'price' | 'data' | 'error' | 'ping' } event Event
|
|
176
|
-
* @param {(...data: object) => null} cb Callback
|
|
177
|
-
*/
|
|
178
|
-
on(event, cb) {
|
|
179
|
-
if (!callbacks[event]) {
|
|
180
|
-
console.log('Wrong event:', event);
|
|
181
|
-
console.log('Available events:', Object.keys(callbacks));
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
callbacks[event].push(cb);
|
|
186
|
-
},
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Close the websocket connection
|
|
190
|
-
* @return {Promise<void>} When websocket is closed
|
|
191
|
-
*/
|
|
192
|
-
end() {
|
|
193
|
-
return new Promise((cb) => {
|
|
194
|
-
isEnded = true;
|
|
195
|
-
ws.close();
|
|
196
|
-
cb();
|
|
197
|
-
});
|
|
198
|
-
},
|
|
199
|
-
|
|
200
|
-
getUser,
|
|
201
|
-
getChartToken,
|
|
202
|
-
getDrawings,
|
|
203
|
-
search,
|
|
204
|
-
getScreener,
|
|
205
|
-
getTA,
|
|
206
|
-
subscribed,
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Unsubscribe to a market
|
|
210
|
-
* @param {string} symbol Market symbol (Example: BTCEUR or COINBASE:BTCEUR)
|
|
211
|
-
*/
|
|
212
|
-
subscribe(symbol) {
|
|
213
|
-
if (subscribed.includes(symbol)) return;
|
|
214
|
-
send('quote_add_symbols', [sessionId, symbol]);
|
|
215
|
-
subscribed.push(symbol);
|
|
216
|
-
},
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* Unsubscribe from a market
|
|
220
|
-
* @param {string} symbol Market symbol (Example: BTCEUR or COINBASE:BTCEUR)
|
|
221
|
-
*/
|
|
222
|
-
unsubscribe(symbol) {
|
|
223
|
-
if (!subscribed.includes(symbol)) return;
|
|
224
|
-
send('quote_remove_symbols', [sessionId, symbol]);
|
|
225
|
-
subscribed = subscribed.filter((s) => s !== symbol);
|
|
226
|
-
},
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* @typedef {Object} IndicatorInfos Indicator infos
|
|
230
|
-
* @property {string} id ID of the indicator (Like: XXX;XXXXXXXXXXXXXXXXXXXXX)
|
|
231
|
-
* @property {string} version Wanted version of the indicator
|
|
232
|
-
* @property {(string | number | boolean | null)[]} [settings] Indicator settings value
|
|
233
|
-
*
|
|
234
|
-
* @typedef {Object} ChartInfos
|
|
235
|
-
* @property {string} [session] User 'sessionid' cookie
|
|
236
|
-
* @property {string} symbol Market symbol (Example: BTCEUR or COINBASE:BTCEUR)
|
|
237
|
-
* @property { '1' | '3' | '5' | '15' | '30' | '45'
|
|
238
|
-
* | '60' | '120' | '180' | '240'
|
|
239
|
-
* | '1D' | '1W' | '1M'
|
|
240
|
-
* } [period] Period
|
|
241
|
-
* @property {number} [range] Number of loaded periods
|
|
242
|
-
* @property {string} [timezone] Timezone in 'Europe/Paris' format
|
|
243
|
-
* @property {IndicatorInfos[]} [indicators] List of indicators
|
|
244
|
-
*/
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* @typedef {Object} Period List of prices / indicator values
|
|
248
|
-
* @property {number} $time
|
|
249
|
-
* @property {{
|
|
250
|
-
* time: number, open: number, close: number,
|
|
251
|
-
* max: number, min: number, change: number,
|
|
252
|
-
* }} $prices
|
|
253
|
-
*/
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Init a chart instance
|
|
257
|
-
* @param {ChartInfos} chart
|
|
258
|
-
* @param {{(prices: Period[]): null}} onUpdate List of periods starting from the lastest
|
|
259
|
-
*/
|
|
260
|
-
async initChart(chart, onUpdate) {
|
|
261
|
-
const chartSession = genSession();
|
|
262
|
-
const periods = [];
|
|
263
|
-
const indicators = await Promise.all(
|
|
264
|
-
(chart.indicators || []).map((i) => getIndicator(i.id, i.version, i.settings)),
|
|
265
|
-
);
|
|
266
|
-
|
|
267
|
-
function updatePeriods(packet) {
|
|
268
|
-
const newData = packet.data;
|
|
269
|
-
|
|
270
|
-
Object.keys(newData).forEach((type) => {
|
|
271
|
-
(newData[type].s || newData[type].st || []).forEach((p) => {
|
|
272
|
-
if (!periods[p.i]) periods[p.i] = {};
|
|
273
|
-
|
|
274
|
-
if (newData[type].s) {
|
|
275
|
-
[periods[p.i].$time] = p.v;
|
|
276
|
-
|
|
277
|
-
periods[p.i][type] = {
|
|
278
|
-
open: p.v[1],
|
|
279
|
-
close: p.v[4],
|
|
280
|
-
max: p.v[2],
|
|
281
|
-
min: p.v[3],
|
|
282
|
-
change: Math.round(p.v[5] * 100) / 100,
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
if (newData[type].st) {
|
|
287
|
-
const period = {};
|
|
288
|
-
const indicator = indicators[parseInt(type, 10)];
|
|
289
|
-
|
|
290
|
-
p.v.forEach((val, i) => {
|
|
291
|
-
if (i === 0) return;
|
|
292
|
-
if (indicator.plots[`plot_${i - 1}`]) period[indicator.plots[`plot_${i - 1}`]] = val;
|
|
293
|
-
else period[`_plot_${i - 1}`] = val;
|
|
294
|
-
});
|
|
295
|
-
periods[p.i][chart.indicators[parseInt(type, 10)].name || `st${type}`] = period;
|
|
296
|
-
}
|
|
297
|
-
});
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
chartCBs[chartSession] = (packet) => {
|
|
302
|
-
if (isEnded) return;
|
|
303
|
-
|
|
304
|
-
if (['timescale_update', 'du'].includes(packet.type)) {
|
|
305
|
-
updatePeriods(packet);
|
|
306
|
-
onUpdate([...periods].reverse());
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
if (packet.type.endsWith('_error')) {
|
|
311
|
-
handleError(`Error on '${chart.symbol}' (${chartSession}) chart: "${packet.type}"`);
|
|
312
|
-
}
|
|
313
|
-
};
|
|
314
|
-
|
|
315
|
-
if (chart.session) send('set_auth_token', [(await getUser(chart.session)).authToken]);
|
|
316
|
-
send('chart_create_session', [chartSession, '']);
|
|
317
|
-
if (chart.timezone) send('switch_timezone', [chartSession, chart.timezone]);
|
|
318
|
-
send('resolve_symbol', [chartSession, 'sds_sym_1', `={"symbol":"${chart.symbol || 'BTCEUR'}","adjustment":"splits"}`]);
|
|
319
|
-
send('create_series', [chartSession, '$prices', 's1', 'sds_sym_1', (chart.period || '240'), (chart.range || 100), '']);
|
|
320
|
-
|
|
321
|
-
indicators.forEach(async (indicator, i) => {
|
|
322
|
-
const pineInfos = {
|
|
323
|
-
pineId: indicator.pineId,
|
|
324
|
-
pineVersion: indicator.pineVersion,
|
|
325
|
-
text: indicator.script,
|
|
326
|
-
};
|
|
327
|
-
|
|
328
|
-
Object.keys(indicator.inputs).forEach((inputID, inp) => {
|
|
329
|
-
const input = indicator.inputs[inputID];
|
|
330
|
-
if (input.type === 'bool' && typeof input.value !== 'boolean') handleError(`Input '${input.name}' (${inp}) must be a boolean !`);
|
|
331
|
-
if (input.type === 'integer' && typeof input.value !== 'number') handleError(`Input '${input.name}' (${inp}) must be a number !`);
|
|
332
|
-
if (input.type === 'float' && typeof input.value !== 'number') handleError(`Input '${input.name}' (${inp}) must be a number !`);
|
|
333
|
-
if (input.type === 'text' && typeof input.value !== 'string') handleError(`Input '${input.name}' (${inp}) must be a string !`);
|
|
334
|
-
if (input.options && !input.options.includes(input.value)) {
|
|
335
|
-
handleError(`Input '${input.name}' (${inp}) must be one of these values:`, input.options);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
pineInfos[inputID] = {
|
|
339
|
-
v: input.value,
|
|
340
|
-
f: input.isFake,
|
|
341
|
-
t: input.type,
|
|
342
|
-
};
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
send('create_study', [chartSession, `${i}`, 'st1', '$prices', 'Script@tv-scripting-101!', pineInfos]);
|
|
346
|
-
});
|
|
347
|
-
},
|
|
348
|
-
|
|
349
|
-
send,
|
|
350
|
-
sessionId,
|
|
351
|
-
};
|
|
352
|
-
};
|
|
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": "
|
|
3
|
+
"version": "3.0.0",
|
|
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
|
}
|