@mathieuc/tradingview 2.0.91 → 3.0.0-pre
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 +5 -97
- 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 +44 -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 -504
- package/package.json +5 -2
- package/src/chart/graphicParser.js +110 -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/{miscRequests.js → src/miscRequests.js} +97 -56
- 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/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/indicator.js +42 -42
- package/tests/strategy.js +56 -56
|
@@ -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,504 +1,9 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
function parse(str) {
|
|
12
|
-
const packets = str.replace(/~h~/g, '').split(/~m~[0-9]{1,}~m~/g).map((p) => {
|
|
13
|
-
if (!p) return false;
|
|
14
|
-
try {
|
|
15
|
-
return JSON.parse(p);
|
|
16
|
-
} catch (error) {
|
|
17
|
-
console.warn('Cant parse', p);
|
|
18
|
-
return false;
|
|
19
|
-
}
|
|
20
|
-
}).filter((p) => p);
|
|
21
|
-
|
|
22
|
-
packets.forEach((packet) => {
|
|
23
|
-
if (packet.m === 'protocol_error') {
|
|
24
|
-
return onPacket({
|
|
25
|
-
type: 'error',
|
|
26
|
-
syntax: packet.p[0],
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (packet.m && packet.p) {
|
|
31
|
-
return onPacket({
|
|
32
|
-
type: packet.m,
|
|
33
|
-
session: packet.p[0],
|
|
34
|
-
data: packet.p[1],
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (typeof packet === 'number') return onPacket({ type: 'ping', ping: packet });
|
|
39
|
-
|
|
40
|
-
return onPacket({ type: 'info', ...packet });
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function genSession() {
|
|
45
|
-
let r = '';
|
|
46
|
-
const c = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
47
|
-
for (let i = 0; i < 12; i += 1) r += c.charAt(Math.floor(Math.random() * c.length));
|
|
48
|
-
return `qs_${r}`;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
module.exports = (autoInit = true) => {
|
|
52
|
-
const callbacks = {
|
|
53
|
-
connected: [],
|
|
54
|
-
disconnected: [],
|
|
55
|
-
logged: [],
|
|
56
|
-
subscribed: [],
|
|
57
|
-
ping: [],
|
|
58
|
-
price: [],
|
|
59
|
-
data: [],
|
|
60
|
-
|
|
61
|
-
error: [],
|
|
62
|
-
event: [],
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
const chartEventNames = [
|
|
66
|
-
'du', 'timescale_update',
|
|
67
|
-
'series_loading', 'series_completed', 'series_error',
|
|
68
|
-
'symbol_resolved', 'symbol_error',
|
|
69
|
-
'study_loading', 'study_error',
|
|
70
|
-
];
|
|
71
|
-
const chartCBs = {};
|
|
72
|
-
|
|
73
|
-
function handleEvent(ev, ...data) {
|
|
74
|
-
callbacks[ev].forEach((e) => e(...data));
|
|
75
|
-
callbacks.event.forEach((e) => e(ev, ...data));
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function handleError(...msgs) {
|
|
79
|
-
if (callbacks.error.length === 0) console.error(...msgs);
|
|
80
|
-
else handleEvent('error', ...msgs);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const ws = new WebSocket('wss://widgetdata.tradingview.com/socket.io/websocket', {
|
|
84
|
-
origin: 'https://s.tradingview.com',
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
let logged = false;
|
|
88
|
-
/** ID of the quote session */
|
|
89
|
-
let sessionId = '';
|
|
90
|
-
|
|
91
|
-
/** List of subscribed symbols */
|
|
92
|
-
let subscribed = [];
|
|
93
|
-
|
|
94
|
-
/** Websockets status */
|
|
95
|
-
let isEnded = false;
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Send a custom packet
|
|
99
|
-
* @param {string} t Packet type
|
|
100
|
-
* @param {string[]} p Packet data
|
|
101
|
-
* @example
|
|
102
|
-
* // Subscribe manualy to BTCEUR
|
|
103
|
-
* send('quote_add_symbols', [sessionId, 'BTCEUR']);
|
|
104
|
-
*/
|
|
105
|
-
function send(t, p = []) {
|
|
106
|
-
if (!sessionId) return;
|
|
107
|
-
|
|
108
|
-
const msg = JSON.stringify({ m: t, p });
|
|
109
|
-
ws.send(`~m~${msg.length}~m~${msg}`);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
ws.on('open', () => {
|
|
113
|
-
sessionId = genSession();
|
|
114
|
-
handleEvent('connected');
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
ws.on('close', () => {
|
|
118
|
-
logged = false;
|
|
119
|
-
sessionId = '';
|
|
120
|
-
handleEvent('disconnected');
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
ws.on('message', parse);
|
|
124
|
-
|
|
125
|
-
onPacket = (packet) => {
|
|
126
|
-
if (packet.type === 'ping') {
|
|
127
|
-
const pingStr = `~h~${packet.ping}`;
|
|
128
|
-
ws.send(`~m~${pingStr.length}~m~${pingStr}`);
|
|
129
|
-
handleEvent('ping', packet.ping);
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (packet.type === 'quote_completed' && packet.data) {
|
|
134
|
-
handleEvent('subscribed', packet.data);
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (packet.type === 'qsd' && packet.data.n && packet.data.v.lp) {
|
|
139
|
-
handleEvent('price', {
|
|
140
|
-
symbol: packet.data.n,
|
|
141
|
-
price: packet.data.v.lp,
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (chartEventNames.includes(packet.type) && chartCBs[packet.session]) {
|
|
148
|
-
chartCBs[packet.session](packet);
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (!logged && packet.type === 'info') {
|
|
153
|
-
if (autoInit) {
|
|
154
|
-
send('set_auth_token', ['unauthorized_user_token']);
|
|
155
|
-
send('quote_create_session', [sessionId]);
|
|
156
|
-
send('quote_set_fields', [sessionId, 'lp']);
|
|
157
|
-
|
|
158
|
-
subscribed.forEach((symbol) => send('quote_add_symbols', [sessionId, symbol]));
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
handleEvent('logged', packet);
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (packet.type === 'error') {
|
|
166
|
-
handleError(`Market API critical error: ${packet.syntax}`);
|
|
167
|
-
ws.close();
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
handleEvent('data', packet);
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
return {
|
|
175
|
-
/** Event listener
|
|
176
|
-
* @param { 'connected' | 'disconnected' | 'logged'
|
|
177
|
-
* | 'subscribed' | 'price' | 'data' | 'error' | 'ping' } event Event
|
|
178
|
-
* @param {(...data: object) => null} cb Callback
|
|
179
|
-
*/
|
|
180
|
-
on(event, cb) {
|
|
181
|
-
if (!callbacks[event]) {
|
|
182
|
-
console.log('Wrong event:', event);
|
|
183
|
-
console.log('Available events:', Object.keys(callbacks));
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
callbacks[event].push(cb);
|
|
188
|
-
},
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Close the websocket connection
|
|
192
|
-
* @return {Promise<void>} When websocket is closed
|
|
193
|
-
*/
|
|
194
|
-
end() {
|
|
195
|
-
return new Promise((cb) => {
|
|
196
|
-
isEnded = true;
|
|
197
|
-
ws.close();
|
|
198
|
-
cb();
|
|
199
|
-
});
|
|
200
|
-
},
|
|
201
|
-
|
|
202
|
-
search,
|
|
203
|
-
getScreener,
|
|
204
|
-
getTA,
|
|
205
|
-
subscribed,
|
|
206
|
-
searchIndicator,
|
|
207
|
-
getUser,
|
|
208
|
-
getChartToken,
|
|
209
|
-
getDrawings,
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Unsubscribe to a market
|
|
213
|
-
* @param {string} symbol Market symbol (Example: BTCEUR or COINBASE:BTCEUR)
|
|
214
|
-
*/
|
|
215
|
-
subscribe(symbol) {
|
|
216
|
-
if (subscribed.includes(symbol)) return;
|
|
217
|
-
send('quote_add_symbols', [sessionId, symbol]);
|
|
218
|
-
subscribed.push(symbol);
|
|
219
|
-
},
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Unsubscribe from a market
|
|
223
|
-
* @param {string} symbol Market symbol (Example: BTCEUR or COINBASE:BTCEUR)
|
|
224
|
-
*/
|
|
225
|
-
unsubscribe(symbol) {
|
|
226
|
-
if (!subscribed.includes(symbol)) return;
|
|
227
|
-
send('quote_remove_symbols', [sessionId, symbol]);
|
|
228
|
-
subscribed = subscribed.filter((s) => s !== symbol);
|
|
229
|
-
},
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* @typedef {Object} IndicatorInfos Indicator infos
|
|
233
|
-
* @property {string} id ID of the indicator (Like: XXX;XXXXXXXXXXXXXXXXXXXXX)
|
|
234
|
-
* @property {string} [name] Name of the indicator
|
|
235
|
-
* @property {string} version Wanted version of the indicator
|
|
236
|
-
* @property {(string | number | boolean | null)[]} [settings] Indicator settings value
|
|
237
|
-
* @property {'study' | 'strategy'} [type] Script type
|
|
238
|
-
*
|
|
239
|
-
* @typedef {Object} ChartInfos
|
|
240
|
-
* @property {string} [session] User 'sessionid' cookie
|
|
241
|
-
* @property {string} symbol Market symbol (Example: BTCEUR or COINBASE:BTCEUR)
|
|
242
|
-
* @property { '1' | '3' | '5' | '15' | '30' | '45'
|
|
243
|
-
* | '60' | '120' | '180' | '240'
|
|
244
|
-
* | '1D' | '1W' | '1M'
|
|
245
|
-
* } [period] Period
|
|
246
|
-
* @property {number} [range] Number of loaded periods
|
|
247
|
-
* @property {string} [timezone] Timezone in 'Europe/Paris' format
|
|
248
|
-
* @property {IndicatorInfos[]} [indicators] List of indicators
|
|
249
|
-
*/
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* @typedef {Object} Period List of prices / indicator values
|
|
253
|
-
* @property {number} $time
|
|
254
|
-
* @property {{
|
|
255
|
-
* time: number, open: number, close: number,
|
|
256
|
-
* max: number, min: number, change: number,
|
|
257
|
-
* }} $prices
|
|
258
|
-
*/
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Init a chart instance
|
|
262
|
-
* @param {ChartInfos} chart
|
|
263
|
-
* @param {{(prices: Period[], strategies: Object<string, StrategyReport>): null}} onUpdate
|
|
264
|
-
*/
|
|
265
|
-
async initChart(chart, onUpdate) {
|
|
266
|
-
const chartSession = genSession();
|
|
267
|
-
const periods = [];
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* @typedef {Object} RelAbsValue Relative and Absolute values
|
|
271
|
-
* @property {number} v Absolute value
|
|
272
|
-
* @property {number} p Relative value
|
|
273
|
-
*/
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* @typedef {Object} TradeReport Trade report
|
|
277
|
-
|
|
278
|
-
* @property {Object} entry Trade entry
|
|
279
|
-
* @property {string} entry.name Trade name
|
|
280
|
-
* @property {'long' | 'short'} entry.type Entry type (long/short)
|
|
281
|
-
* @property {number} entry.value Entry price value
|
|
282
|
-
* @property {number} entry.time Entry timestamp
|
|
283
|
-
|
|
284
|
-
* @property {Object} exit Trade exit
|
|
285
|
-
* @property {'' | string} exit.name Trade name ('' if false exit)
|
|
286
|
-
* @property {number} exit.value Exit price value
|
|
287
|
-
* @property {number} exit.time Exit timestamp
|
|
288
|
-
|
|
289
|
-
* @property {number} quantity Trade quantity
|
|
290
|
-
* @property {RelAbsValue} profit Trade profit
|
|
291
|
-
* @property {RelAbsValue} cumulative Trade cummulative profit
|
|
292
|
-
* @property {RelAbsValue} runup Trade run-up
|
|
293
|
-
* @property {RelAbsValue} drawdown Trade drawdown
|
|
294
|
-
*/
|
|
295
|
-
|
|
296
|
-
/**
|
|
297
|
-
* @typedef {Object} PerfReport
|
|
298
|
-
* @property {number} avgBarsInTrade Average bars in trade
|
|
299
|
-
* @property {number} avgBarsInWinTrade Average bars in winning trade
|
|
300
|
-
* @property {number} avgBarsInLossTrade Average bars in losing trade
|
|
301
|
-
* @property {number} avgTrade Average trade gain
|
|
302
|
-
* @property {number} avgTradePercent Average trade performace
|
|
303
|
-
* @property {number} avgLosTrade Average losing trade gain
|
|
304
|
-
* @property {number} avgLosTradePercent Average losing trade performace
|
|
305
|
-
* @property {number} avgWinTrade Average winning trade gain
|
|
306
|
-
* @property {number} avgWinTradePercent Average winning trade performace
|
|
307
|
-
* @property {number} commissionPaid Commission paid
|
|
308
|
-
* @property {number} grossLoss Gross loss value
|
|
309
|
-
* @property {number} grossLossPercent Gross loss percent
|
|
310
|
-
* @property {number} grossProfit Gross profit
|
|
311
|
-
* @property {number} grossProfitPercent Gross profit percent
|
|
312
|
-
* @property {number} largestLosTrade Largest losing trade gain
|
|
313
|
-
* @property {number} largestLosTradePercent Largent losing trade performance
|
|
314
|
-
* @property {number} largestWinTrade Largest winning trade gain
|
|
315
|
-
* @property {number} largestWinTradePercent Largest winning trade performance
|
|
316
|
-
* @property {number} marginCalls Margin calls
|
|
317
|
-
* @property {number} maxContractsHeld Max Contracts Held
|
|
318
|
-
* @property {number} netProfit Net profit
|
|
319
|
-
* @property {number} netProfitPercent Net performance
|
|
320
|
-
* @property {number} numberOfLosingTrades Number of losing trades
|
|
321
|
-
* @property {number} numberOfWiningTrades Number of winning trades
|
|
322
|
-
* @property {number} percentProfitable Strategy winrate
|
|
323
|
-
* @property {number} profitFactor Profit factor
|
|
324
|
-
* @property {number} ratioAvgWinAvgLoss Ratio Average Win / Average Loss
|
|
325
|
-
* @property {number} totalOpenTrades Total open trades
|
|
326
|
-
* @property {number} totalTrades Total trades
|
|
327
|
-
*/
|
|
328
|
-
|
|
329
|
-
/**
|
|
330
|
-
* @typedef {Object} StrategyReport
|
|
331
|
-
* @property {'EUR' | 'USD' | 'JPY' | '' | 'CHF'} currency Selected currency
|
|
332
|
-
* @property {TradeReport[]} trades Trade list
|
|
333
|
-
* @property {Object} history History Chart value
|
|
334
|
-
* @property {number[]} history.buyHold Buy hold values
|
|
335
|
-
* @property {number[]} history.buyHoldPercent Buy hold percent values
|
|
336
|
-
* @property {number[]} history.drawDown Drawdown values
|
|
337
|
-
* @property {number[]} history.drawDownPercent Drawdown percent values
|
|
338
|
-
* @property {number[]} history.equity Equity values
|
|
339
|
-
* @property {number[]} history.equityPercent Equity percent values
|
|
340
|
-
* @property {Object} performance Strategy performance
|
|
341
|
-
* @property {PerfReport} performance.all Strategy long/short performances
|
|
342
|
-
* @property {PerfReport} performance.long Strategy long performances
|
|
343
|
-
* @property {PerfReport} performance.short Strategy short performances
|
|
344
|
-
* @property {number} performance.buyHoldReturn Strategy Buy & Hold Return
|
|
345
|
-
* @property {number} performance.buyHoldReturnPercent Strategy Buy & Hold Return percent
|
|
346
|
-
* @property {number} performance.maxDrawDown Strategy max drawdown
|
|
347
|
-
* @property {number} performance.maxDrawDownPercent Strategy max drawdown percent
|
|
348
|
-
* @property {number} performance.openPL Strategy Open P&L (Profit And Loss)
|
|
349
|
-
* @property {number} performance.openPLPercent Strategy Open P&L (Profit And Loss) percent
|
|
350
|
-
* @property {number} performance.sharpeRatio Strategy Sharpe Ratio
|
|
351
|
-
* @property {number} performance.sortinoRatio Strategy Sortino Ratio
|
|
352
|
-
*/
|
|
353
|
-
|
|
354
|
-
/** @type {Object<string, StrategyReport>} Strategies */
|
|
355
|
-
const strategies = {};
|
|
356
|
-
|
|
357
|
-
const indicators = await Promise.all(
|
|
358
|
-
(chart.indicators || []).map((i) => getIndicator(i.id, i.version, i.settings, i.type)),
|
|
359
|
-
);
|
|
360
|
-
|
|
361
|
-
async function updatePeriods(packet) {
|
|
362
|
-
const newData = packet.data;
|
|
363
|
-
|
|
364
|
-
await Promise.all(Object.keys(newData).map(async (type) => {
|
|
365
|
-
const std = chart.indicators[parseInt(type, 10)] || {};
|
|
366
|
-
|
|
367
|
-
if (newData[type].ns && newData[type].ns.d) {
|
|
368
|
-
const stratData = JSON.parse(newData[type].ns.d);
|
|
369
|
-
|
|
370
|
-
if (stratData.dataCompressed) {
|
|
371
|
-
const zip = new JSZip();
|
|
372
|
-
const data = JSON.parse(
|
|
373
|
-
await (
|
|
374
|
-
await zip.loadAsync(stratData.dataCompressed, { base64: true })
|
|
375
|
-
).file('').async('text'),
|
|
376
|
-
);
|
|
377
|
-
|
|
378
|
-
strategies[std.name || type] = {
|
|
379
|
-
currency: data.report.currency,
|
|
380
|
-
|
|
381
|
-
trades: data.report.trades.map((t) => ({
|
|
382
|
-
entry: {
|
|
383
|
-
name: t.e.c,
|
|
384
|
-
type: (t.e.tp[0] === 's' ? 'short' : 'long'),
|
|
385
|
-
value: t.e.p,
|
|
386
|
-
time: t.e.tm,
|
|
387
|
-
},
|
|
388
|
-
exit: {
|
|
389
|
-
name: t.x.c,
|
|
390
|
-
value: t.x.p,
|
|
391
|
-
time: t.x.tm,
|
|
392
|
-
},
|
|
393
|
-
quantity: t.q,
|
|
394
|
-
profit: t.tp,
|
|
395
|
-
cumulative: t.cp,
|
|
396
|
-
runup: t.rn,
|
|
397
|
-
drawdown: t.dd,
|
|
398
|
-
})),
|
|
399
|
-
|
|
400
|
-
history: {
|
|
401
|
-
buyHold: data.report.buyHold,
|
|
402
|
-
buyHoldPercent: data.report.buyHoldPercent,
|
|
403
|
-
drawDown: data.report.drawDown,
|
|
404
|
-
drawDownPercent: data.report.drawDownPercent,
|
|
405
|
-
equity: data.report.equity,
|
|
406
|
-
equityPercent: data.report.equityPercent,
|
|
407
|
-
},
|
|
408
|
-
|
|
409
|
-
performance: data.report.performance,
|
|
410
|
-
};
|
|
411
|
-
return;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
if (stratData.data && stratData.data.report && stratData.data.report.performance) {
|
|
415
|
-
if (!strategies[std.name || type]) strategies[std.name || type] = { performance: {} };
|
|
416
|
-
strategies[std.name || type].performance = stratData.data.report.performance;
|
|
417
|
-
return;
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
return;
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
(newData[type].s || newData[type].st || []).forEach((p) => {
|
|
424
|
-
if (!periods[p.i]) periods[p.i] = {};
|
|
425
|
-
|
|
426
|
-
if (newData[type].s) {
|
|
427
|
-
[periods[p.i].$time] = p.v;
|
|
428
|
-
|
|
429
|
-
periods[p.i][type] = {
|
|
430
|
-
open: p.v[1],
|
|
431
|
-
close: p.v[4],
|
|
432
|
-
max: p.v[2],
|
|
433
|
-
min: p.v[3],
|
|
434
|
-
change: Math.round(p.v[5] * 100) / 100,
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
if (newData[type].st) {
|
|
439
|
-
const period = {};
|
|
440
|
-
const indicator = indicators[parseInt(type, 10)];
|
|
441
|
-
|
|
442
|
-
p.v.forEach((val, i) => {
|
|
443
|
-
if (i === 0) return;
|
|
444
|
-
if (indicator.plots[`plot_${i - 1}`]) period[indicator.plots[`plot_${i - 1}`]] = val;
|
|
445
|
-
else period[`_plot_${i - 1}`] = val;
|
|
446
|
-
});
|
|
447
|
-
periods[p.i][chart.indicators[parseInt(type, 10)].name || `st${type}`] = period;
|
|
448
|
-
}
|
|
449
|
-
});
|
|
450
|
-
}));
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
chartCBs[chartSession] = async (packet) => {
|
|
454
|
-
if (isEnded) return;
|
|
455
|
-
|
|
456
|
-
if (['timescale_update', 'du'].includes(packet.type)) {
|
|
457
|
-
await updatePeriods(packet);
|
|
458
|
-
onUpdate([...periods].reverse(), strategies);
|
|
459
|
-
return;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
if (packet.type.endsWith('_error')) {
|
|
463
|
-
handleError(`Error on '${chart.symbol}' (${chartSession}) chart: "${packet.type}":`, packet);
|
|
464
|
-
}
|
|
465
|
-
};
|
|
466
|
-
|
|
467
|
-
if (chart.session) send('set_auth_token', [(await getUser(chart.session)).authToken]);
|
|
468
|
-
send('chart_create_session', [chartSession, '']);
|
|
469
|
-
if (chart.timezone) send('switch_timezone', [chartSession, chart.timezone]);
|
|
470
|
-
send('resolve_symbol', [chartSession, 'sds_sym_1', `={"symbol":"${chart.symbol || 'BTCEUR'}","adjustment":"splits"}`]);
|
|
471
|
-
send('create_series', [chartSession, '$prices', 's1', 'sds_sym_1', (chart.period || '240'), (chart.range || 100), '']);
|
|
472
|
-
|
|
473
|
-
indicators.forEach(async (indicator, i) => {
|
|
474
|
-
const pineInfos = {
|
|
475
|
-
pineId: indicator.pineId,
|
|
476
|
-
pineVersion: indicator.pineVersion,
|
|
477
|
-
text: indicator.script,
|
|
478
|
-
};
|
|
479
|
-
|
|
480
|
-
Object.keys(indicator.inputs).forEach((inputID, inp) => {
|
|
481
|
-
const input = indicator.inputs[inputID];
|
|
482
|
-
if (input.type === 'bool' && typeof input.value !== 'boolean') handleError(`Input '${input.name}' (${inp}) must be a boolean !`);
|
|
483
|
-
if (input.type === 'integer' && typeof input.value !== 'number') handleError(`Input '${input.name}' (${inp}) must be a number !`);
|
|
484
|
-
if (input.type === 'float' && typeof input.value !== 'number') handleError(`Input '${input.name}' (${inp}) must be a number !`);
|
|
485
|
-
if (input.type === 'text' && typeof input.value !== 'string') handleError(`Input '${input.name}' (${inp}) must be a string !`);
|
|
486
|
-
if (input.options && !input.options.includes(input.value)) {
|
|
487
|
-
handleError(`Input '${input.name}' (${inp}) must be one of these values:`, input.options);
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
pineInfos[inputID] = {
|
|
491
|
-
v: input.value,
|
|
492
|
-
f: input.isFake,
|
|
493
|
-
t: input.type,
|
|
494
|
-
};
|
|
495
|
-
});
|
|
496
|
-
|
|
497
|
-
send('create_study', [chartSession, `${i}`, 'st1', '$prices', indicator.typeID, pineInfos]);
|
|
498
|
-
});
|
|
499
|
-
},
|
|
500
|
-
|
|
501
|
-
send,
|
|
502
|
-
sessionId,
|
|
503
|
-
};
|
|
504
|
-
};
|
|
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-pre",
|
|
4
4
|
"description": "Tradingview instant stocks API, indicator alerts, trading bot, and more !",
|
|
5
5
|
"main": "main.js",
|
|
6
6
|
"scripts": {
|
|
@@ -32,8 +32,11 @@
|
|
|
32
32
|
"ws": "^7.4.3"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
+
"@babel/eslint-parser": "^7.15.7",
|
|
36
|
+
"@mathieuc/console": "^1.0.1",
|
|
35
37
|
"eslint": "^7.25.0",
|
|
36
38
|
"eslint-config-airbnb-base": "^14.2.1",
|
|
37
|
-
"eslint-plugin-import": "^2.22.1"
|
|
39
|
+
"eslint-plugin-import": "^2.22.1",
|
|
40
|
+
"jsdoc-to-markdown": "^7.1.0"
|
|
38
41
|
}
|
|
39
42
|
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} GraphicLabel
|
|
3
|
+
* @prop {number} id Drawing ID
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {Object} GraphicLine
|
|
8
|
+
* @prop {number} id Drawing ID
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @typedef {Object} GraphicBox
|
|
13
|
+
* @prop {number} id Drawing ID
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {Object} GraphicTable
|
|
18
|
+
* @prop {number} id Drawing ID
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {Object} GraphicHorizline
|
|
23
|
+
* @prop {number} id Drawing ID
|
|
24
|
+
* @prop {number} level Y position of the line
|
|
25
|
+
* @prop {number} startIndex Start index of the line (`chart.periods[line.startIndex]`)
|
|
26
|
+
* @prop {number} endIndex End index of the line (`chart.periods[line.endIndex]`)
|
|
27
|
+
* @prop {boolean} extendRight Is the line extended to the right
|
|
28
|
+
* @prop {boolean} extendLeft Is the line extended to the left
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @typedef {Object} GraphicPoint
|
|
33
|
+
* @prop {number} index X position of the point
|
|
34
|
+
* @prop {number} level Y position of the point
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @typedef {Object} GraphicPolygon
|
|
39
|
+
* @prop {number} id Drawing ID
|
|
40
|
+
* @prop {GraphicPoint[]} points List of polygon points
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @typedef {Object} GraphicHorizHist
|
|
45
|
+
* @prop {number} id Drawing ID
|
|
46
|
+
* @prop {number} priceLow Low Y position
|
|
47
|
+
* @prop {number} priceHigh High Y position
|
|
48
|
+
* @prop {number} firstBarTime First X position
|
|
49
|
+
* @prop {number} lastBarTime Last X position
|
|
50
|
+
* @prop {number[]} rate List of values
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @typedef {Object} GraphicData List of drawings indexed by type
|
|
55
|
+
* @prop {GraphicLabel[]} labels List of labels drawings
|
|
56
|
+
* @prop {GraphicLine[]} lines List of lines drawings
|
|
57
|
+
* @prop {GraphicBox[]} boxes List of boxes drawings
|
|
58
|
+
* @prop {GraphicTable[]} tables List of tables drawings
|
|
59
|
+
* @prop {GraphicPolygon[]} polygons List of polygons drawings
|
|
60
|
+
* @prop {GraphicHorizHist[]} horizHists List of horizontal histograms drawings
|
|
61
|
+
* @prop {GraphicHorizline[]} horizLines List of horizontal lines drawings
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @param {Object} rawGraphic Raw graphic data
|
|
66
|
+
* @param {Object} indexes Drawings xPos indexes
|
|
67
|
+
* @returns {GraphicData}
|
|
68
|
+
*/
|
|
69
|
+
module.exports = function graphicParse(rawGraphic = {}, indexes = []) {
|
|
70
|
+
// console.log('indexes', indexes);
|
|
71
|
+
return {
|
|
72
|
+
labels: Object.values(rawGraphic.dwglabels ?? {}).map((l) => ({
|
|
73
|
+
...l,
|
|
74
|
+
})),
|
|
75
|
+
|
|
76
|
+
lines: Object.values(rawGraphic.dwglines ?? {}).map((l) => ({
|
|
77
|
+
...l,
|
|
78
|
+
})),
|
|
79
|
+
|
|
80
|
+
boxes: Object.values(rawGraphic.dwgboxes ?? {}).map((b) => ({
|
|
81
|
+
...b,
|
|
82
|
+
})),
|
|
83
|
+
|
|
84
|
+
tables: Object.values(rawGraphic.dwgtables ?? {}).map((t) => ({
|
|
85
|
+
...t,
|
|
86
|
+
})),
|
|
87
|
+
|
|
88
|
+
horizLines: Object.values(rawGraphic.horizlines ?? {}).map((h) => ({
|
|
89
|
+
...h,
|
|
90
|
+
startIndex: indexes[h.startIndex],
|
|
91
|
+
endIndex: indexes[h.endIndex],
|
|
92
|
+
})),
|
|
93
|
+
|
|
94
|
+
polygons: Object.values(rawGraphic.polygons ?? {}).map((p) => ({
|
|
95
|
+
...p,
|
|
96
|
+
points: p.points.map((pt) => ({
|
|
97
|
+
...pt,
|
|
98
|
+
index: indexes[pt.index],
|
|
99
|
+
})),
|
|
100
|
+
})),
|
|
101
|
+
|
|
102
|
+
horizHists: Object.values(rawGraphic.hhists ?? {}).map((h) => ({
|
|
103
|
+
...h,
|
|
104
|
+
firstBarTime: indexes[h.firstBarTime],
|
|
105
|
+
lastBarTime: indexes[h.lastBarTime],
|
|
106
|
+
})),
|
|
107
|
+
|
|
108
|
+
raw: () => rawGraphic,
|
|
109
|
+
};
|
|
110
|
+
};
|