@mathieuc/tradingview 3.0.1 → 3.3.1
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 +6 -0
- package/README.md +28 -2
- package/docs/DOCS.md +3 -0
- package/examples/AllPrivateIndicators.js +4 -4
- package/examples/BuiltInIndicator.js +1 -1
- package/examples/CustomChartType.js +1 -0
- package/examples/Errors.js +147 -153
- package/examples/FakeReplayMode.js +41 -0
- package/examples/GetDrawings.js +26 -0
- package/examples/PinePermManage.js +57 -0
- package/examples/ReplayMode.js +97 -0
- package/examples/UserLogin.js +16 -0
- package/main.js +2 -0
- package/package.json +6 -9
- package/src/FormData.js +36 -0
- package/src/chart/session.js +193 -17
- package/src/chart/study.js +68 -68
- package/src/classes/BuiltInIndicator.js +1 -0
- package/src/classes/PineIndicator.js +7 -7
- package/src/classes/PinePermManager.js +143 -0
- package/src/client.js +17 -10
- package/src/miscRequests.js +148 -103
- package/src/protocol.js +7 -1
- package/src/quote/session.js +1 -0
- package/src/request.js +37 -0
- package/src/types.js +6 -2
- package/src/utils.js +6 -0
- package/test.js +58 -147
- package/tests/1. Search.js +18 -0
- package/tests/2. QuoteSession.js +54 -0
- package/tests/3. SimpleChart.js +61 -0
- package/tests/4. Indicators.js +100 -0
- package/tests/5. BuiltInIndicator.js +41 -0
- package/tests/6. FakeReplayMode.js +59 -0
- package/tests/7. ReplayMode.js +51 -0
- package/tests/8. CustomChartTypes.js +115 -0
- package/tests/9. AllErrors.js +146 -0
- package/.github/FUNDING.yml +0 -1
- package/examples/CurrentTimeframe.js +0 -34
- package/examples/_RenkoIndicator.js +0 -41
- package/tests/alerts/discordWH.js +0 -76
- package/tests/alerts/drawings.js +0 -41
- package/tests/alerts/envLoader.js +0 -11
- package/tests/alerts/manualAlert.js +0 -77
- package/tests/alerts/sentNotifs.js +0 -16
- package/tests/alerts/strategyAlert.js +0 -94
- package/tests/analysis.js +0 -13
- package/tests/indicator.js +0 -42
- package/tests/prices.js +0 -33
- package/tests/strategy.js +0 -56
package/package.json
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mathieuc/tradingview",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.1",
|
|
4
4
|
"description": "Tradingview instant stocks API, indicator alerts, trading bot, and more !",
|
|
5
5
|
"main": "main.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test
|
|
8
|
-
"test-analysis": "node ./tests/analysis.js",
|
|
9
|
-
"test-indicator": "node ./tests/indicator.js",
|
|
10
|
-
"test-manual-alerts": "node ./tests/alerts/manualAlert.js",
|
|
11
|
-
"test-strat-alerts": "node ./tests/alerts/strategyAlert.js",
|
|
12
|
-
"test": "npm run test-prices"
|
|
7
|
+
"test": "node test"
|
|
13
8
|
},
|
|
14
9
|
"repository": {
|
|
15
10
|
"type": "git",
|
|
16
11
|
"url": "git+https://github.com/Mathieu2301/TradingView-API.git"
|
|
17
12
|
},
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=14.0.0"
|
|
15
|
+
},
|
|
18
16
|
"keywords": [
|
|
19
17
|
"tradingwiew",
|
|
20
18
|
"market",
|
|
@@ -36,7 +34,6 @@
|
|
|
36
34
|
"@mathieuc/console": "^1.0.1",
|
|
37
35
|
"eslint": "^7.25.0",
|
|
38
36
|
"eslint-config-airbnb-base": "^14.2.1",
|
|
39
|
-
"eslint-plugin-import": "^2.22.1"
|
|
40
|
-
"jsdoc-to-markdown": "^7.1.0"
|
|
37
|
+
"eslint-plugin-import": "^2.22.1"
|
|
41
38
|
}
|
|
42
39
|
}
|
package/src/FormData.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** @class */
|
|
2
|
+
class FormData {
|
|
3
|
+
#string = '';
|
|
4
|
+
|
|
5
|
+
#boundary = '';
|
|
6
|
+
|
|
7
|
+
get boundary() {
|
|
8
|
+
return this.#boundary;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
constructor() {
|
|
12
|
+
const random = (Math.random() * 10 ** 20).toString(36);
|
|
13
|
+
this.#boundary = `${random}`;
|
|
14
|
+
this.#string = `--${this.boundary}`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Adds a property to the FormData object
|
|
19
|
+
* @param {string} key Property key
|
|
20
|
+
* @param {string} value Property value
|
|
21
|
+
*/
|
|
22
|
+
append(key, value) {
|
|
23
|
+
this.#string += `\r\nContent-Disposition: form-data; name="${key}"`;
|
|
24
|
+
this.#string += `\r\n\r\n${value}`;
|
|
25
|
+
this.#string += `\r\n--${this.boundary}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @returns {string}
|
|
30
|
+
*/
|
|
31
|
+
toString() {
|
|
32
|
+
return `${this.#string}--`;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = FormData;
|
package/src/chart/session.js
CHANGED
|
@@ -64,7 +64,9 @@ const ChartTypes = {
|
|
|
64
64
|
* @prop {string} session Session (ex: '24x7')
|
|
65
65
|
* @prop {string} session-correction Session correction
|
|
66
66
|
* @prop {string} session-display Session display (ex: '24x7')
|
|
67
|
-
|
|
67
|
+
*/
|
|
68
|
+
|
|
69
|
+
/**
|
|
68
70
|
* @typedef {Object} MarketInfos
|
|
69
71
|
* @prop {string} series_id Used series (ex: 'ser_1')
|
|
70
72
|
* @prop {string} base_currency Base currency (ex: 'BTC')
|
|
@@ -113,7 +115,14 @@ const ChartTypes = {
|
|
|
113
115
|
* @param {import('../client').ClientBridge} client
|
|
114
116
|
*/
|
|
115
117
|
module.exports = (client) => class ChartSession {
|
|
116
|
-
#
|
|
118
|
+
#chartSessionID = genSessionID('cs');
|
|
119
|
+
|
|
120
|
+
#replaySessionID = genSessionID('rs');
|
|
121
|
+
|
|
122
|
+
#replayMode = false;
|
|
123
|
+
|
|
124
|
+
/** @type {Object<string, (): any>} */
|
|
125
|
+
#replayOKCB = {};
|
|
117
126
|
|
|
118
127
|
/** Parent client */
|
|
119
128
|
#client = client;
|
|
@@ -148,6 +157,11 @@ module.exports = (client) => class ChartSession {
|
|
|
148
157
|
symbolLoaded: [],
|
|
149
158
|
update: [],
|
|
150
159
|
|
|
160
|
+
replayLoaded: [],
|
|
161
|
+
replayPoint: [],
|
|
162
|
+
replayResolution: [],
|
|
163
|
+
replayEnd: [],
|
|
164
|
+
|
|
151
165
|
event: [],
|
|
152
166
|
error: [],
|
|
153
167
|
};
|
|
@@ -167,7 +181,7 @@ module.exports = (client) => class ChartSession {
|
|
|
167
181
|
}
|
|
168
182
|
|
|
169
183
|
constructor() {
|
|
170
|
-
this.#client.sessions[this.#
|
|
184
|
+
this.#client.sessions[this.#chartSessionID] = {
|
|
171
185
|
type: 'chart',
|
|
172
186
|
onData: (packet) => {
|
|
173
187
|
if (global.TW_DEBUG) console.log('§90§30§106 CHART SESSION §0 DATA', packet);
|
|
@@ -188,7 +202,10 @@ module.exports = (client) => class ChartSession {
|
|
|
188
202
|
}
|
|
189
203
|
|
|
190
204
|
if (['timescale_update', 'du'].includes(packet.type)) {
|
|
205
|
+
const changes = [];
|
|
206
|
+
|
|
191
207
|
Object.keys(packet.data[1]).forEach((k) => {
|
|
208
|
+
changes.push(k);
|
|
192
209
|
if (k === '$prices') {
|
|
193
210
|
const periods = packet.data[1].$prices;
|
|
194
211
|
if (!periods || !periods.s) return;
|
|
@@ -211,7 +228,7 @@ module.exports = (client) => class ChartSession {
|
|
|
211
228
|
if (this.#studyListeners[k]) this.#studyListeners[k](packet);
|
|
212
229
|
});
|
|
213
230
|
|
|
214
|
-
this.#handleEvent('update');
|
|
231
|
+
this.#handleEvent('update', changes);
|
|
215
232
|
return;
|
|
216
233
|
}
|
|
217
234
|
|
|
@@ -232,7 +249,47 @@ module.exports = (client) => class ChartSession {
|
|
|
232
249
|
},
|
|
233
250
|
};
|
|
234
251
|
|
|
235
|
-
this.#client.
|
|
252
|
+
this.#client.sessions[this.#replaySessionID] = {
|
|
253
|
+
type: 'replay',
|
|
254
|
+
onData: (packet) => {
|
|
255
|
+
if (global.TW_DEBUG) console.log('§90§30§106 REPLAY SESSION §0 DATA', packet);
|
|
256
|
+
|
|
257
|
+
if (packet.type === 'replay_ok') {
|
|
258
|
+
if (this.#replayOKCB[packet.data[1]]) {
|
|
259
|
+
this.#replayOKCB[packet.data[1]]();
|
|
260
|
+
delete this.#replayOKCB[packet.data[1]];
|
|
261
|
+
}
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (packet.type === 'replay_instance_id') {
|
|
266
|
+
this.#handleEvent('replayLoaded', packet.data[1]);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (packet.type === 'replay_point') {
|
|
271
|
+
this.#handleEvent('replayPoint', packet.data[1]);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (packet.type === 'replay_resolutions') {
|
|
276
|
+
this.#handleEvent('replayResolution', packet.data[1], packet.data[2]);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (packet.type === 'replay_data_end') {
|
|
281
|
+
this.#handleEvent('replayEnd');
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (packet.type === 'critical_error') {
|
|
286
|
+
const [, name, description] = packet.data;
|
|
287
|
+
this.#handleError('Critical error:', name, description);
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
this.#client.send('chart_create_session', [this.#chartSessionID]);
|
|
236
293
|
}
|
|
237
294
|
|
|
238
295
|
#seriesCreated = false;
|
|
@@ -255,7 +312,7 @@ module.exports = (client) => class ChartSession {
|
|
|
255
312
|
this.#periods = {};
|
|
256
313
|
|
|
257
314
|
this.#client.send(`${this.#seriesCreated ? 'modify' : 'create'}_series`, [
|
|
258
|
-
this.#
|
|
315
|
+
this.#chartSessionID,
|
|
259
316
|
'$prices',
|
|
260
317
|
's1',
|
|
261
318
|
`ser_${this.#currentSeries}`,
|
|
@@ -278,31 +335,56 @@ module.exports = (client) => class ChartSession {
|
|
|
278
335
|
* @param {'EUR' | 'USD' | string} [options.currency] Chart currency
|
|
279
336
|
* @param {ChartType} [options.type] Chart custom type
|
|
280
337
|
* @param {ChartInputs} [options.inputs] Chart custom inputs
|
|
338
|
+
* @param {number} [options.replay] Replay mode starting point (Timestamp)
|
|
281
339
|
*/
|
|
282
340
|
setMarket(symbol, options = {}) {
|
|
283
341
|
this.#periods = {};
|
|
284
342
|
|
|
343
|
+
if (this.#replayMode) {
|
|
344
|
+
this.#replayMode = false;
|
|
345
|
+
this.#client.send('replay_delete_session', [this.#replaySessionID]);
|
|
346
|
+
}
|
|
347
|
+
|
|
285
348
|
const symbolInit = {
|
|
286
349
|
symbol: symbol || 'BTCEUR',
|
|
287
350
|
adjustment: options.adjustment || 'splits',
|
|
288
351
|
};
|
|
289
352
|
|
|
290
353
|
if (options.session) symbolInit.session = options.session;
|
|
291
|
-
|
|
292
354
|
if (options.currency) symbolInit['currency-id'] = options.currency;
|
|
293
355
|
|
|
294
|
-
|
|
356
|
+
if (options.replay) {
|
|
357
|
+
this.#replayMode = true;
|
|
358
|
+
this.#client.send('replay_create_session', [this.#replaySessionID]);
|
|
359
|
+
|
|
360
|
+
this.#client.send('replay_add_series', [
|
|
361
|
+
this.#replaySessionID,
|
|
362
|
+
'req_replay_addseries',
|
|
363
|
+
`=${JSON.stringify(symbolInit)}`,
|
|
364
|
+
options.timeframe,
|
|
365
|
+
]);
|
|
366
|
+
|
|
367
|
+
this.#client.send('replay_reset', [
|
|
368
|
+
this.#replaySessionID,
|
|
369
|
+
'req_replay_reset',
|
|
370
|
+
options.replay,
|
|
371
|
+
]);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const complex = options.type || options.replay;
|
|
375
|
+
const chartInit = complex ? {} : symbolInit;
|
|
295
376
|
|
|
296
|
-
if (
|
|
377
|
+
if (complex) {
|
|
378
|
+
if (options.replay) chartInit.replay = this.#replaySessionID;
|
|
297
379
|
chartInit.symbol = symbolInit;
|
|
298
380
|
chartInit.type = ChartTypes[options.type];
|
|
299
|
-
chartInit.inputs = { ...options.inputs };
|
|
381
|
+
if (options.type) chartInit.inputs = { ...options.inputs };
|
|
300
382
|
}
|
|
301
383
|
|
|
302
384
|
this.#currentSeries += 1;
|
|
303
385
|
|
|
304
386
|
this.#client.send('resolve_symbol', [
|
|
305
|
-
this.#
|
|
387
|
+
this.#chartSessionID,
|
|
306
388
|
`ser_${this.#currentSeries}`,
|
|
307
389
|
`=${JSON.stringify(chartInit)}`,
|
|
308
390
|
]);
|
|
@@ -316,7 +398,7 @@ module.exports = (client) => class ChartSession {
|
|
|
316
398
|
*/
|
|
317
399
|
setTimezone(timezone) {
|
|
318
400
|
this.#periods = {};
|
|
319
|
-
this.#client.send('switch_timezone', [this.#
|
|
401
|
+
this.#client.send('switch_timezone', [this.#chartSessionID, timezone]);
|
|
320
402
|
}
|
|
321
403
|
|
|
322
404
|
/**
|
|
@@ -324,7 +406,60 @@ module.exports = (client) => class ChartSession {
|
|
|
324
406
|
* @param {number} number Number of additional periods/candles you want to fetch
|
|
325
407
|
*/
|
|
326
408
|
fetchMore(number = 1) {
|
|
327
|
-
this.#client.send('request_more_data', [this.#
|
|
409
|
+
this.#client.send('request_more_data', [this.#chartSessionID, '$prices', number]);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Fetch x additional previous periods/candles values
|
|
414
|
+
* @param {number} number Number of additional periods/candles you want to fetch
|
|
415
|
+
* @returns {Promise} Raise when the data has been fetched
|
|
416
|
+
*/
|
|
417
|
+
replayStep(number = 1) {
|
|
418
|
+
return new Promise((cb) => {
|
|
419
|
+
if (!this.#replayMode) {
|
|
420
|
+
this.#handleError('No replay session');
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const reqID = genSessionID('rsq_step');
|
|
425
|
+
this.#client.send('replay_step', [this.#replaySessionID, reqID, number]);
|
|
426
|
+
this.#replayOKCB[reqID] = () => { cb(); };
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Start fetching a new period/candle every x ms
|
|
432
|
+
* @param {number} interval Number of additional periods/candles you want to fetch
|
|
433
|
+
* @returns {Promise} Raise when the replay mode starts
|
|
434
|
+
*/
|
|
435
|
+
replayStart(interval = 1000) {
|
|
436
|
+
return new Promise((cb) => {
|
|
437
|
+
if (!this.#replayMode) {
|
|
438
|
+
this.#handleError('No replay session');
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const reqID = genSessionID('rsq_start');
|
|
443
|
+
this.#client.send('replay_start', [this.#replaySessionID, reqID, interval]);
|
|
444
|
+
this.#replayOKCB[reqID] = () => { cb(); };
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Stop fetching a new period/candle every x ms
|
|
450
|
+
* @returns {Promise} Raise when the replay mode stops
|
|
451
|
+
*/
|
|
452
|
+
replayStop() {
|
|
453
|
+
return new Promise((cb) => {
|
|
454
|
+
if (!this.#replayMode) {
|
|
455
|
+
this.#handleError('No replay session');
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
const reqID = genSessionID('rsq_stop');
|
|
460
|
+
this.#client.send('replay_stop', [this.#replaySessionID, reqID]);
|
|
461
|
+
this.#replayOKCB[reqID] = () => { cb(); };
|
|
462
|
+
});
|
|
328
463
|
}
|
|
329
464
|
|
|
330
465
|
/**
|
|
@@ -338,13 +473,52 @@ module.exports = (client) => class ChartSession {
|
|
|
338
473
|
|
|
339
474
|
/**
|
|
340
475
|
* When a chart update happens
|
|
341
|
-
* @param {() => void} cb
|
|
476
|
+
* @param {(changes: ('$prices' | string)[]) => void} cb
|
|
342
477
|
* @event
|
|
343
478
|
*/
|
|
344
479
|
onUpdate(cb) {
|
|
345
480
|
this.#callbacks.update.push(cb);
|
|
346
481
|
}
|
|
347
482
|
|
|
483
|
+
/**
|
|
484
|
+
* When the replay session is ready
|
|
485
|
+
* @param {() => void} cb
|
|
486
|
+
* @event
|
|
487
|
+
*/
|
|
488
|
+
onReplayLoaded(cb) {
|
|
489
|
+
this.#callbacks.replayLoaded.push(cb);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* When the replay session has new resolution
|
|
494
|
+
* @param {(
|
|
495
|
+
* timeframe: import('../types').TimeFrame,
|
|
496
|
+
* index: number,
|
|
497
|
+
* ) => void} cb
|
|
498
|
+
* @event
|
|
499
|
+
*/
|
|
500
|
+
onReplayResolution(cb) {
|
|
501
|
+
this.#callbacks.replayResolution.push(cb);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* When the replay session ends
|
|
506
|
+
* @param {() => void} cb
|
|
507
|
+
* @event
|
|
508
|
+
*/
|
|
509
|
+
onReplayEnd(cb) {
|
|
510
|
+
this.#callbacks.replayEnd.push(cb);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* When the replay session cursor has moved
|
|
515
|
+
* @param {(index: number) => void} cb
|
|
516
|
+
* @event
|
|
517
|
+
*/
|
|
518
|
+
onReplayPoint(cb) {
|
|
519
|
+
this.#callbacks.replayPoint.push(cb);
|
|
520
|
+
}
|
|
521
|
+
|
|
348
522
|
/**
|
|
349
523
|
* When chart error happens
|
|
350
524
|
* @param {(...any) => void} cb Callback
|
|
@@ -356,7 +530,7 @@ module.exports = (client) => class ChartSession {
|
|
|
356
530
|
|
|
357
531
|
/** @type {ChartSessionBridge} */
|
|
358
532
|
#chartSession = {
|
|
359
|
-
sessionID: this.#
|
|
533
|
+
sessionID: this.#chartSessionID,
|
|
360
534
|
studyListeners: this.#studyListeners,
|
|
361
535
|
indexes: {},
|
|
362
536
|
send: (t, p) => this.#client.send(t, p),
|
|
@@ -366,7 +540,9 @@ module.exports = (client) => class ChartSession {
|
|
|
366
540
|
|
|
367
541
|
/** Delete the chart session */
|
|
368
542
|
delete() {
|
|
369
|
-
this.#client.send('
|
|
370
|
-
|
|
543
|
+
if (this.#replayMode) this.#client.send('replay_delete_session', [this.#replaySessionID]);
|
|
544
|
+
this.#client.send('chart_delete_session', [this.#chartSessionID]);
|
|
545
|
+
delete this.#client.sessions[this.#chartSessionID];
|
|
546
|
+
this.#replayMode = false;
|
|
371
547
|
}
|
|
372
548
|
};
|
package/src/chart/study.js
CHANGED
|
@@ -16,11 +16,11 @@ function getInputs(options) {
|
|
|
16
16
|
if (options.pineId) pineInputs.pineId = options.pineId;
|
|
17
17
|
if (options.pineVersion) pineInputs.pineVersion = options.pineVersion;
|
|
18
18
|
|
|
19
|
-
Object.keys(options.inputs).forEach((inputID) => {
|
|
19
|
+
Object.keys(options.inputs).forEach((inputID, n) => {
|
|
20
20
|
const input = options.inputs[inputID];
|
|
21
21
|
|
|
22
22
|
pineInputs[inputID] = {
|
|
23
|
-
v: input.value,
|
|
23
|
+
v: (input.type !== 'color') ? input.value : n,
|
|
24
24
|
f: input.isFake,
|
|
25
25
|
t: input.type,
|
|
26
26
|
};
|
|
@@ -35,80 +35,80 @@ function getInputs(options) {
|
|
|
35
35
|
/**
|
|
36
36
|
* @typedef {Object} TradeReport Trade report
|
|
37
37
|
|
|
38
|
-
* @
|
|
39
|
-
* @
|
|
40
|
-
* @
|
|
41
|
-
* @
|
|
42
|
-
* @
|
|
43
|
-
|
|
44
|
-
* @
|
|
45
|
-
* @
|
|
46
|
-
* @
|
|
47
|
-
* @
|
|
48
|
-
|
|
49
|
-
* @
|
|
50
|
-
* @
|
|
51
|
-
* @
|
|
52
|
-
* @
|
|
53
|
-
* @
|
|
38
|
+
* @prop {Object} entry Trade entry
|
|
39
|
+
* @prop {string} entry.name Trade name
|
|
40
|
+
* @prop {'long' | 'short'} entry.type Entry type (long/short)
|
|
41
|
+
* @prop {number} entry.value Entry price value
|
|
42
|
+
* @prop {number} entry.time Entry timestamp
|
|
43
|
+
|
|
44
|
+
* @prop {Object} exit Trade exit
|
|
45
|
+
* @prop {'' | string} exit.name Trade name ('' if false exit)
|
|
46
|
+
* @prop {number} exit.value Exit price value
|
|
47
|
+
* @prop {number} exit.time Exit timestamp
|
|
48
|
+
|
|
49
|
+
* @prop {number} quantity Trade quantity
|
|
50
|
+
* @prop {RelAbsValue} profit Trade profit
|
|
51
|
+
* @prop {RelAbsValue} cumulative Trade cummulative profit
|
|
52
|
+
* @prop {RelAbsValue} runup Trade run-up
|
|
53
|
+
* @prop {RelAbsValue} drawdown Trade drawdown
|
|
54
54
|
*/
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* @typedef {Object} PerfReport
|
|
58
|
-
* @
|
|
59
|
-
* @
|
|
60
|
-
* @
|
|
61
|
-
* @
|
|
62
|
-
* @
|
|
63
|
-
* @
|
|
64
|
-
* @
|
|
65
|
-
* @
|
|
66
|
-
* @
|
|
67
|
-
* @
|
|
68
|
-
* @
|
|
69
|
-
* @
|
|
70
|
-
* @
|
|
71
|
-
* @
|
|
72
|
-
* @
|
|
73
|
-
* @
|
|
74
|
-
* @
|
|
75
|
-
* @
|
|
76
|
-
* @
|
|
77
|
-
* @
|
|
78
|
-
* @
|
|
79
|
-
* @
|
|
80
|
-
* @
|
|
81
|
-
* @
|
|
82
|
-
* @
|
|
83
|
-
* @
|
|
84
|
-
* @
|
|
85
|
-
* @
|
|
86
|
-
* @
|
|
58
|
+
* @prop {number} avgBarsInTrade Average bars in trade
|
|
59
|
+
* @prop {number} avgBarsInWinTrade Average bars in winning trade
|
|
60
|
+
* @prop {number} avgBarsInLossTrade Average bars in losing trade
|
|
61
|
+
* @prop {number} avgTrade Average trade gain
|
|
62
|
+
* @prop {number} avgTradePercent Average trade performace
|
|
63
|
+
* @prop {number} avgLosTrade Average losing trade gain
|
|
64
|
+
* @prop {number} avgLosTradePercent Average losing trade performace
|
|
65
|
+
* @prop {number} avgWinTrade Average winning trade gain
|
|
66
|
+
* @prop {number} avgWinTradePercent Average winning trade performace
|
|
67
|
+
* @prop {number} commissionPaid Commission paid
|
|
68
|
+
* @prop {number} grossLoss Gross loss value
|
|
69
|
+
* @prop {number} grossLossPercent Gross loss percent
|
|
70
|
+
* @prop {number} grossProfit Gross profit
|
|
71
|
+
* @prop {number} grossProfitPercent Gross profit percent
|
|
72
|
+
* @prop {number} largestLosTrade Largest losing trade gain
|
|
73
|
+
* @prop {number} largestLosTradePercent Largent losing trade performance (percentage)
|
|
74
|
+
* @prop {number} largestWinTrade Largest winning trade gain
|
|
75
|
+
* @prop {number} largestWinTradePercent Largest winning trade performance (percentage)
|
|
76
|
+
* @prop {number} marginCalls Margin calls
|
|
77
|
+
* @prop {number} maxContractsHeld Max Contracts Held
|
|
78
|
+
* @prop {number} netProfit Net profit
|
|
79
|
+
* @prop {number} netProfitPercent Net performance (percentage)
|
|
80
|
+
* @prop {number} numberOfLosingTrades Number of losing trades
|
|
81
|
+
* @prop {number} numberOfWiningTrades Number of winning trades
|
|
82
|
+
* @prop {number} percentProfitable Strategy winrate
|
|
83
|
+
* @prop {number} profitFactor Profit factor
|
|
84
|
+
* @prop {number} ratioAvgWinAvgLoss Ratio Average Win / Average Loss
|
|
85
|
+
* @prop {number} totalOpenTrades Total open trades
|
|
86
|
+
* @prop {number} totalTrades Total trades
|
|
87
87
|
*/
|
|
88
88
|
|
|
89
89
|
/**
|
|
90
90
|
* @typedef {Object} StrategyReport
|
|
91
|
-
* @
|
|
92
|
-
* @
|
|
93
|
-
* @
|
|
94
|
-
* @
|
|
95
|
-
* @
|
|
96
|
-
* @
|
|
97
|
-
* @
|
|
98
|
-
* @
|
|
99
|
-
* @
|
|
100
|
-
* @
|
|
101
|
-
* @
|
|
102
|
-
* @
|
|
103
|
-
* @
|
|
104
|
-
* @
|
|
105
|
-
* @
|
|
106
|
-
* @
|
|
107
|
-
* @
|
|
108
|
-
* @
|
|
109
|
-
* @
|
|
110
|
-
* @
|
|
111
|
-
* @
|
|
91
|
+
* @prop {'EUR' | 'USD' | 'JPY' | '' | 'CHF'} [currency] Selected currency
|
|
92
|
+
* @prop {TradeReport[]} trades Trade list starting by the last
|
|
93
|
+
* @prop {Object} history History Chart value
|
|
94
|
+
* @prop {number[]} [history.buyHold] Buy hold values
|
|
95
|
+
* @prop {number[]} [history.buyHoldPercent] Buy hold percent values
|
|
96
|
+
* @prop {number[]} [history.drawDown] Drawdown values
|
|
97
|
+
* @prop {number[]} [history.drawDownPercent] Drawdown percent values
|
|
98
|
+
* @prop {number[]} [history.equity] Equity values
|
|
99
|
+
* @prop {number[]} [history.equityPercent] Equity percent values
|
|
100
|
+
* @prop {Object} performance Strategy performance
|
|
101
|
+
* @prop {PerfReport} [performance.all] Strategy long/short performances
|
|
102
|
+
* @prop {PerfReport} [performance.long] Strategy long performances
|
|
103
|
+
* @prop {PerfReport} [performance.short] Strategy short performances
|
|
104
|
+
* @prop {number} [performance.buyHoldReturn] Strategy Buy & Hold Return
|
|
105
|
+
* @prop {number} [performance.buyHoldReturnPercent] Strategy Buy & Hold Return percent
|
|
106
|
+
* @prop {number} [performance.maxDrawDown] Strategy max drawdown
|
|
107
|
+
* @prop {number} [performance.maxDrawDownPercent] Strategy max drawdown percent
|
|
108
|
+
* @prop {number} [performance.openPL] Strategy Open P&L (Profit And Loss)
|
|
109
|
+
* @prop {number} [performance.openPLPercent] Strategy Open P&L (Profit And Loss) percent
|
|
110
|
+
* @prop {number} [performance.sharpeRatio] Strategy Sharpe Ratio
|
|
111
|
+
* @prop {number} [performance.sortinoRatio] Strategy Sortino Ratio
|
|
112
112
|
*/
|
|
113
113
|
|
|
114
114
|
/**
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
* @property {string} inline Input inline name
|
|
5
5
|
* @property {string} [internalID] Input internal ID
|
|
6
6
|
* @property {string} [tooltip] Input tooltip
|
|
7
|
-
* @property {'text' | 'source' | 'integer'
|
|
7
|
+
* @property {'text' | 'source' | 'integer'
|
|
8
|
+
* | 'float' | 'resolution' | 'bool' | 'color'
|
|
9
|
+
* } type Input type
|
|
8
10
|
* @property {string | number | boolean} value Input default value
|
|
9
11
|
* @property {boolean} isHidden If the input is hidden
|
|
10
12
|
* @property {boolean} isFake If the input is fake
|
|
@@ -26,6 +28,8 @@
|
|
|
26
28
|
* @typedef {'Script@tv-scripting-101!'
|
|
27
29
|
* | 'StrategyScript@tv-scripting-101!'} IndicatorType Indicator type
|
|
28
30
|
*/
|
|
31
|
+
|
|
32
|
+
/** @class */
|
|
29
33
|
module.exports = class PineIndicator {
|
|
30
34
|
#options;
|
|
31
35
|
|
|
@@ -113,12 +117,8 @@ module.exports = class PineIndicator {
|
|
|
113
117
|
text: 'String',
|
|
114
118
|
};
|
|
115
119
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|| (input.type === 'integer' && typeof value !== 'number')
|
|
119
|
-
|| (input.type === 'float' && typeof value !== 'number')
|
|
120
|
-
|| (input.type === 'text' && typeof value !== 'string')
|
|
121
|
-
) {
|
|
120
|
+
// eslint-disable-next-line valid-typeof
|
|
121
|
+
if (types[input.type] && typeof value !== types[input.type].toLowerCase()) {
|
|
122
122
|
throw new Error(`Input '${input.name}' (${propI}) must be a ${types[input.type]} !`);
|
|
123
123
|
}
|
|
124
124
|
|