@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
package/src/client.js
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
const WebSocket = require('ws');
|
|
2
|
+
|
|
3
|
+
const misc = require('./miscRequests');
|
|
4
|
+
const protocol = require('./protocol');
|
|
5
|
+
|
|
6
|
+
const quoteSessionGenerator = require('./quote/session');
|
|
7
|
+
const chartSessionGenerator = require('./chart/session');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {Object} Session
|
|
11
|
+
* @prop {'quote' | 'chart' | 'replay'} type Session type
|
|
12
|
+
* @prop {(data: {}) => null} onData When there is a data
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** @typedef {Object<string, Session>} SessionList Session list */
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {(t: string, p: []) => null} SendPacket Send a custom packet
|
|
19
|
+
* @param {string} t Packet type
|
|
20
|
+
* @param {string[]} p Packet data
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @typedef {Object} ClientBridge
|
|
25
|
+
* @prop {SessionList} sessions
|
|
26
|
+
* @prop {SendPacket} send
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @typedef { 'connected' | 'disconnected'
|
|
31
|
+
* | 'logged' | 'ping' | 'data'
|
|
32
|
+
* | 'error' | 'event'
|
|
33
|
+
* } ClientEvent
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
module.exports = class Client {
|
|
37
|
+
#ws;
|
|
38
|
+
|
|
39
|
+
#logged = false;
|
|
40
|
+
|
|
41
|
+
/** If the client is logged in */
|
|
42
|
+
get isLogged() {
|
|
43
|
+
return this.#logged;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** If the cient was closed */
|
|
47
|
+
get isOpen() {
|
|
48
|
+
return this.#ws.readyState === this.#ws.OPEN;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** @type {SessionList} */
|
|
52
|
+
#sessions = {};
|
|
53
|
+
|
|
54
|
+
#callbacks = {
|
|
55
|
+
connected: [],
|
|
56
|
+
disconnected: [],
|
|
57
|
+
logged: [],
|
|
58
|
+
ping: [],
|
|
59
|
+
data: [],
|
|
60
|
+
|
|
61
|
+
error: [],
|
|
62
|
+
event: [],
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @param {ClientEvent} ev Client event
|
|
67
|
+
* @param {...{}} data Packet data
|
|
68
|
+
*/
|
|
69
|
+
#handleEvent(ev, ...data) {
|
|
70
|
+
this.#callbacks[ev].forEach((e) => e(...data));
|
|
71
|
+
this.#callbacks.event.forEach((e) => e(ev, ...data));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
#handleError(...msgs) {
|
|
75
|
+
if (this.#callbacks.error.length === 0) console.error(...msgs);
|
|
76
|
+
else this.#handleEvent('error', ...msgs);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* When client is connected
|
|
81
|
+
* @param {() => void} cb Callback
|
|
82
|
+
* @event
|
|
83
|
+
*/
|
|
84
|
+
onConnected(cb) {
|
|
85
|
+
this.#callbacks.connected.push(cb);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* When client is disconnected
|
|
90
|
+
* @param {() => void} cb Callback
|
|
91
|
+
* @event
|
|
92
|
+
*/
|
|
93
|
+
onDisconnected(cb) {
|
|
94
|
+
this.#callbacks.disconnected.push(cb);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @typedef {Object} SocketSession
|
|
99
|
+
* @prop {string} session_id Socket session ID
|
|
100
|
+
* @prop {number} timestamp Session start timestamp
|
|
101
|
+
* @prop {number} timestampMs Session start milliseconds timestamp
|
|
102
|
+
* @prop {string} release Release
|
|
103
|
+
* @prop {string} studies_metadata_hash Studies metadata hash
|
|
104
|
+
* @prop {'json' | string} protocol Used protocol
|
|
105
|
+
* @prop {string} javastudies Javastudies
|
|
106
|
+
* @prop {number} auth_scheme_vsn Auth scheme type
|
|
107
|
+
* @prop {string} via Socket IP
|
|
108
|
+
|
|
109
|
+
* When client is logged
|
|
110
|
+
* @param {(SocketSession: SocketSession) => void} cb Callback
|
|
111
|
+
* @event
|
|
112
|
+
*/
|
|
113
|
+
onLogged(cb) {
|
|
114
|
+
this.#callbacks.logged.push(cb);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* When server is pinging the client
|
|
119
|
+
* @param {(i: number) => void} cb Callback
|
|
120
|
+
* @event
|
|
121
|
+
*/
|
|
122
|
+
onPing(cb) {
|
|
123
|
+
this.#callbacks.ping.push(cb);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* When unparsed data is received
|
|
128
|
+
* @param {(...{}) => void} cb Callback
|
|
129
|
+
* @event
|
|
130
|
+
*/
|
|
131
|
+
onData(cb) {
|
|
132
|
+
this.#callbacks.data.push(cb);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* When a client error happens
|
|
137
|
+
* @param {(...{}) => void} cb Callback
|
|
138
|
+
* @event
|
|
139
|
+
*/
|
|
140
|
+
onError(cb) {
|
|
141
|
+
this.#callbacks.error.push(cb);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* When a client event happens
|
|
146
|
+
* @param {(...{}) => void} cb Callback
|
|
147
|
+
* @event
|
|
148
|
+
*/
|
|
149
|
+
onEvent(cb) {
|
|
150
|
+
this.#callbacks.event.push(cb);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
#parsePacket(str) {
|
|
154
|
+
if (!this.isOpen) return;
|
|
155
|
+
|
|
156
|
+
protocol.parseWSPacket(str).forEach((packet) => {
|
|
157
|
+
if (global.TW_DEBUG) console.log('§90§30§107 CLIENT §0 PACKET', packet);
|
|
158
|
+
if (typeof packet === 'number') { // Ping
|
|
159
|
+
this.#ws.send(protocol.formatWSPacket(`~h~${packet}`));
|
|
160
|
+
this.#handleEvent('ping', packet);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (packet.m === 'protocol_error') { // Error
|
|
165
|
+
this.#handleError('Client critical error:', packet.p);
|
|
166
|
+
this.#ws.close();
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (packet.m && packet.p) { // Normal packet
|
|
171
|
+
const parsed = {
|
|
172
|
+
type: packet.m,
|
|
173
|
+
data: packet.p,
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const session = packet.p[0];
|
|
177
|
+
|
|
178
|
+
if (session && this.#sessions[session]) {
|
|
179
|
+
this.#sessions[session].onData(parsed);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (!this.#logged) {
|
|
185
|
+
this.#handleEvent('logged', packet);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
this.#handleEvent('data', packet);
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
#sendQueue = [];
|
|
194
|
+
|
|
195
|
+
/** @type {SendPacket} Send a custom packet */
|
|
196
|
+
send(t, p = []) {
|
|
197
|
+
this.#sendQueue.push(protocol.formatWSPacket({ m: t, p }));
|
|
198
|
+
this.sendQueue();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** Send all waiting packets */
|
|
202
|
+
sendQueue() {
|
|
203
|
+
while (this.isOpen && this.#logged && this.#sendQueue.length > 0) {
|
|
204
|
+
const packet = this.#sendQueue.shift();
|
|
205
|
+
this.#ws.send(packet);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* @typedef {Object} ClientOptions
|
|
211
|
+
* @prop {string} [token] User auth token (in 'sessionid' cookie)
|
|
212
|
+
* @prop {boolean} [DEBUG] Enable debug mode
|
|
213
|
+
*/
|
|
214
|
+
|
|
215
|
+
/** Client object
|
|
216
|
+
* @param {ClientOptions} clientOptions TradingView client options
|
|
217
|
+
*/
|
|
218
|
+
constructor(clientOptions = {}) {
|
|
219
|
+
if (clientOptions.DEBUG) global.TW_DEBUG = clientOptions.DEBUG;
|
|
220
|
+
|
|
221
|
+
this.#ws = new WebSocket('wss://widgetdata.tradingview.com/socket.io/websocket', {
|
|
222
|
+
origin: 'https://s.tradingview.com',
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
if (clientOptions.token) {
|
|
226
|
+
misc.getUser(clientOptions.token).then((user) => {
|
|
227
|
+
this.#sendQueue.unshift(protocol.formatWSPacket({
|
|
228
|
+
m: 'set_auth_token',
|
|
229
|
+
p: [user.authToken],
|
|
230
|
+
}));
|
|
231
|
+
this.#logged = true;
|
|
232
|
+
this.sendQueue();
|
|
233
|
+
}).catch((err) => {
|
|
234
|
+
this.#handleError('Credentials error:', err.message);
|
|
235
|
+
});
|
|
236
|
+
} else {
|
|
237
|
+
this.#sendQueue.unshift(protocol.formatWSPacket({
|
|
238
|
+
m: 'set_auth_token',
|
|
239
|
+
p: ['unauthorized_user_token'],
|
|
240
|
+
}));
|
|
241
|
+
this.#logged = true;
|
|
242
|
+
this.sendQueue();
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
this.#ws.on('open', () => {
|
|
246
|
+
this.#handleEvent('connected');
|
|
247
|
+
this.sendQueue();
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
this.#ws.on('close', () => {
|
|
251
|
+
this.#logged = false;
|
|
252
|
+
this.#handleEvent('disconnected');
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
this.#ws.on('message', (data) => this.#parsePacket(data));
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/** @type {ClientBridge} */
|
|
259
|
+
#clientBridge = {
|
|
260
|
+
sessions: this.#sessions,
|
|
261
|
+
send: (t, p) => this.send(t, p),
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
/** @namespace Session */
|
|
265
|
+
Session = {
|
|
266
|
+
Quote: quoteSessionGenerator(this.#clientBridge),
|
|
267
|
+
Chart: chartSessionGenerator(this.#clientBridge),
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Close the websocket connection
|
|
272
|
+
* @return {Promise<void>} When websocket is closed
|
|
273
|
+
*/
|
|
274
|
+
end() {
|
|
275
|
+
return new Promise((cb) => {
|
|
276
|
+
this.#ws.close();
|
|
277
|
+
cb();
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
};
|