@mathieuc/tradingview 3.2.0 → 3.3.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 +6 -0
- package/README.md +16 -3
- package/docs/DOCS.md +3 -0
- package/examples/BuiltInIndicator.js +1 -1
- package/examples/GetDrawings.js +26 -0
- package/examples/UserLogin.js +16 -0
- package/package.json +2 -3
- package/src/FormData.js +1 -0
- package/src/chart/session.js +4 -2
- 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 +5 -4
- package/src/client.js +14 -9
- package/src/miscRequests.js +139 -68
- package/src/protocol.js +7 -1
- package/src/request.js +37 -37
- package/src/types.js +6 -2
- package/src/utils.js +6 -0
- package/test.js +2 -0
- package/tests/4. Indicators.js +4 -3
- package/examples/CurrentTimeframe.js +0 -34
- package/examples/_RenkoIndicator.js +0 -41
- package/tests/disabled/x. Template.js +0 -24
package/.eslintrc.js
CHANGED
package/README.md
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
# TradingView API [](https://GitHub.com/Mathieu2301/TradingView-API/stargazers/)
|
|
2
2
|
|
|
3
3
|
[](https://github.com/Mathieu2301/TradingView-API/actions/workflows/node.js.yml)
|
|
4
|
-
[](https://npmjs.com/@mathieuc/tradingview)
|
|
4
|
+
[](https://www.codefactor.io/repository/github/mathieu2301/tradingview-api/overview/main)
|
|
6
5
|
[](https://GitHub.com/Mathieu2301/TradingView-API/commit/)
|
|
6
|
+
[](https://npmjs.com/@mathieuc/tradingview)
|
|
7
|
+
[](https://npmjs.com/@mathieuc/tradingview)
|
|
7
8
|
[](https://npmjs.com/package/@mathieuc/tradingview)
|
|
8
9
|
|
|
9
10
|
Get realtime market prices and indicator values from Tradingview !
|
|
10
11
|
|
|
11
12
|
## Features
|
|
13
|
+
|
|
12
14
|
- [x] Premium features
|
|
13
15
|
- [x] Automatically backtest many strategies and try many settings in a very little time
|
|
14
16
|
- [x] Get drawings you made on your chart
|
|
@@ -26,6 +28,7 @@ Get realtime market prices and indicator values from Tradingview !
|
|
|
26
28
|
- IF YOU WANT A FEATURE, ASK ME !
|
|
27
29
|
|
|
28
30
|
## Possibilities
|
|
31
|
+
|
|
29
32
|
- Trading bot
|
|
30
33
|
- Discord alerts
|
|
31
34
|
- Hard backtest
|
|
@@ -33,21 +36,31 @@ Get realtime market prices and indicator values from Tradingview !
|
|
|
33
36
|
- Free replay mode for all timeframes
|
|
34
37
|
|
|
35
38
|
___
|
|
39
|
+
|
|
36
40
|
## Installation
|
|
37
41
|
|
|
38
42
|
Stable version:
|
|
43
|
+
|
|
39
44
|
```ruby
|
|
40
45
|
npm i @mathieuc/tradingview
|
|
41
46
|
```
|
|
47
|
+
|
|
42
48
|
Last version:
|
|
49
|
+
|
|
43
50
|
```ruby
|
|
44
51
|
npm i github:Mathieu2301/TradingView-API
|
|
45
52
|
```
|
|
46
53
|
|
|
47
54
|
## Examples
|
|
55
|
+
|
|
48
56
|
You can find all the examples and snippets in `./examples` folder.
|
|
49
57
|
|
|
58
|
+
## Before opening an issue
|
|
59
|
+
|
|
60
|
+
Please look at examples and previously resolved issues before opening a new one. I can't help everyone (especially for questions that are not library related but JavaScript related). Thank you for your understanding.
|
|
50
61
|
___
|
|
62
|
+
|
|
51
63
|
## Problems
|
|
64
|
+
|
|
52
65
|
If you have errors in console or unwanted behavior,
|
|
53
66
|
please create an issue [here](https://github.com/Mathieu2301/Tradingview-API/issues).
|
package/docs/DOCS.md
ADDED
|
@@ -7,7 +7,7 @@ const TradingView = require('../main');
|
|
|
7
7
|
|
|
8
8
|
const volumeProfile = new TradingView.BuiltInIndicator('VbPFixed@tv-basicstudies-139!');
|
|
9
9
|
|
|
10
|
-
if (!process.argv[2] &&
|
|
10
|
+
if (!process.argv[2] && !['VbPFixed@tv-basicstudies-139!', 'Volume@tv-basicstudies-144'].includes(volumeProfile.type)) {
|
|
11
11
|
throw Error('Please specify your \'sessionid\' cookie');
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const TradingView = require('../main');
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
This example tests the
|
|
5
|
+
getDrawings function
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// First parameter must be the layoutID
|
|
9
|
+
// (if the layout is private) Second parameter must be the sessionid cookie
|
|
10
|
+
// (if the layout is private) Third parameter must be the userid (you can use getUser function)
|
|
11
|
+
|
|
12
|
+
if (!process.argv[2]) throw Error('Please specify a layoutID');
|
|
13
|
+
|
|
14
|
+
TradingView.getDrawings(process.argv[2], null, {
|
|
15
|
+
session: process.argv[3],
|
|
16
|
+
id: process.argv[4],
|
|
17
|
+
}).then((drawings) => {
|
|
18
|
+
console.log(`Found ${drawings.length} drawings:`, drawings.map((d) => ({
|
|
19
|
+
id: d.id,
|
|
20
|
+
symbol: d.symbol,
|
|
21
|
+
type: d.type,
|
|
22
|
+
text: d.state.text,
|
|
23
|
+
})));
|
|
24
|
+
}).catch((err) => {
|
|
25
|
+
console.error('Error:', err.message);
|
|
26
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const TradingView = require('../main');
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
This example tests the
|
|
5
|
+
user login function
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
if (!process.argv[2]) throw Error('Please specify your username/email');
|
|
9
|
+
if (!process.argv[3]) throw Error('Please specify your password');
|
|
10
|
+
|
|
11
|
+
TradingView.loginUser(process.argv[2], process.argv[3], false).then((user) => {
|
|
12
|
+
console.log('User:', user);
|
|
13
|
+
console.log('Sessionid:', user.session);
|
|
14
|
+
}).catch((err) => {
|
|
15
|
+
console.error('Login error:', err.message);
|
|
16
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mathieuc/tradingview",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "Tradingview instant stocks API, indicator alerts, trading bot, and more !",
|
|
5
5
|
"main": "main.js",
|
|
6
6
|
"scripts": {
|
|
@@ -34,7 +34,6 @@
|
|
|
34
34
|
"@mathieuc/console": "^1.0.1",
|
|
35
35
|
"eslint": "^7.25.0",
|
|
36
36
|
"eslint-config-airbnb-base": "^14.2.1",
|
|
37
|
-
"eslint-plugin-import": "^2.22.1"
|
|
38
|
-
"jsdoc-to-markdown": "^7.1.0"
|
|
37
|
+
"eslint-plugin-import": "^2.22.1"
|
|
39
38
|
}
|
|
40
39
|
}
|
package/src/FormData.js
CHANGED
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')
|
|
@@ -120,7 +122,7 @@ module.exports = (client) => class ChartSession {
|
|
|
120
122
|
#replayMode = false;
|
|
121
123
|
|
|
122
124
|
/** @type {Object<string, (): any>} */
|
|
123
|
-
#replayOKCB = {}
|
|
125
|
+
#replayOKCB = {};
|
|
124
126
|
|
|
125
127
|
/** Parent client */
|
|
126
128
|
#client = client;
|
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
|
|
|
@@ -10,6 +10,7 @@ const FormData = require('../FormData');
|
|
|
10
10
|
* @prop {string} created Authorization creation date
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
/** @class */
|
|
13
14
|
class PinePermManager {
|
|
14
15
|
sessionId;
|
|
15
16
|
|
|
@@ -39,7 +40,7 @@ class PinePermManager {
|
|
|
39
40
|
* @returns {AuthorizationUser[]}
|
|
40
41
|
*/
|
|
41
42
|
async getUsers(limit = 10, order = '-created') {
|
|
42
|
-
const data = await request({
|
|
43
|
+
const { data } = await request({
|
|
43
44
|
method: 'POST',
|
|
44
45
|
host: 'www.tradingview.com',
|
|
45
46
|
path: `/pine_perm/list_users/?limit=${limit}&order_by=${order}`,
|
|
@@ -69,7 +70,7 @@ class PinePermManager {
|
|
|
69
70
|
formData.append('expiration', expiration.toString());
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
const data = await request({
|
|
73
|
+
const { data } = await request({
|
|
73
74
|
method: 'POST',
|
|
74
75
|
host: 'www.tradingview.com',
|
|
75
76
|
path: '/pine_perm/add/',
|
|
@@ -98,7 +99,7 @@ class PinePermManager {
|
|
|
98
99
|
formData.append('expiration', expiration.toISOString());
|
|
99
100
|
}
|
|
100
101
|
|
|
101
|
-
const data = await request({
|
|
102
|
+
const { data } = await request({
|
|
102
103
|
method: 'POST',
|
|
103
104
|
host: 'www.tradingview.com',
|
|
104
105
|
path: '/pine_perm/modify_user_expiration/',
|
|
@@ -123,7 +124,7 @@ class PinePermManager {
|
|
|
123
124
|
formData.append('pine_id', this.pineId);
|
|
124
125
|
formData.append('username_recip', username);
|
|
125
126
|
|
|
126
|
-
const data = await request({
|
|
127
|
+
const { data } = await request({
|
|
127
128
|
method: 'POST',
|
|
128
129
|
host: 'www.tradingview.com',
|
|
129
130
|
path: '/pine_perm/remove/',
|
package/src/client.js
CHANGED
|
@@ -15,9 +15,10 @@ const chartSessionGenerator = require('./chart/session');
|
|
|
15
15
|
/** @typedef {Object<string, Session>} SessionList Session list */
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
* @
|
|
18
|
+
* @callback SendPacket Send a custom packet
|
|
19
19
|
* @param {string} t Packet type
|
|
20
20
|
* @param {string[]} p Packet data
|
|
21
|
+
* @returns {void}
|
|
21
22
|
*/
|
|
22
23
|
|
|
23
24
|
/**
|
|
@@ -33,6 +34,7 @@ const chartSessionGenerator = require('./chart/session');
|
|
|
33
34
|
* } ClientEvent
|
|
34
35
|
*/
|
|
35
36
|
|
|
37
|
+
/** @class */
|
|
36
38
|
module.exports = class Client {
|
|
37
39
|
#ws;
|
|
38
40
|
|
|
@@ -79,7 +81,7 @@ module.exports = class Client {
|
|
|
79
81
|
/**
|
|
80
82
|
* When client is connected
|
|
81
83
|
* @param {() => void} cb Callback
|
|
82
|
-
* @event
|
|
84
|
+
* @event onConnected
|
|
83
85
|
*/
|
|
84
86
|
onConnected(cb) {
|
|
85
87
|
this.#callbacks.connected.push(cb);
|
|
@@ -88,7 +90,7 @@ module.exports = class Client {
|
|
|
88
90
|
/**
|
|
89
91
|
* When client is disconnected
|
|
90
92
|
* @param {() => void} cb Callback
|
|
91
|
-
* @event
|
|
93
|
+
* @event onDisconnected
|
|
92
94
|
*/
|
|
93
95
|
onDisconnected(cb) {
|
|
94
96
|
this.#callbacks.disconnected.push(cb);
|
|
@@ -105,10 +107,12 @@ module.exports = class Client {
|
|
|
105
107
|
* @prop {string} javastudies Javastudies
|
|
106
108
|
* @prop {number} auth_scheme_vsn Auth scheme type
|
|
107
109
|
* @prop {string} via Socket IP
|
|
110
|
+
*/
|
|
108
111
|
|
|
112
|
+
/**
|
|
109
113
|
* When client is logged
|
|
110
114
|
* @param {(SocketSession: SocketSession) => void} cb Callback
|
|
111
|
-
* @event
|
|
115
|
+
* @event onLogged
|
|
112
116
|
*/
|
|
113
117
|
onLogged(cb) {
|
|
114
118
|
this.#callbacks.logged.push(cb);
|
|
@@ -117,7 +121,7 @@ module.exports = class Client {
|
|
|
117
121
|
/**
|
|
118
122
|
* When server is pinging the client
|
|
119
123
|
* @param {(i: number) => void} cb Callback
|
|
120
|
-
* @event
|
|
124
|
+
* @event onPing
|
|
121
125
|
*/
|
|
122
126
|
onPing(cb) {
|
|
123
127
|
this.#callbacks.ping.push(cb);
|
|
@@ -126,7 +130,7 @@ module.exports = class Client {
|
|
|
126
130
|
/**
|
|
127
131
|
* When unparsed data is received
|
|
128
132
|
* @param {(...{}) => void} cb Callback
|
|
129
|
-
* @event
|
|
133
|
+
* @event onData
|
|
130
134
|
*/
|
|
131
135
|
onData(cb) {
|
|
132
136
|
this.#callbacks.data.push(cb);
|
|
@@ -135,7 +139,7 @@ module.exports = class Client {
|
|
|
135
139
|
/**
|
|
136
140
|
* When a client error happens
|
|
137
141
|
* @param {(...{}) => void} cb Callback
|
|
138
|
-
* @event
|
|
142
|
+
* @event onError
|
|
139
143
|
*/
|
|
140
144
|
onError(cb) {
|
|
141
145
|
this.#callbacks.error.push(cb);
|
|
@@ -144,7 +148,7 @@ module.exports = class Client {
|
|
|
144
148
|
/**
|
|
145
149
|
* When a client event happens
|
|
146
150
|
* @param {(...{}) => void} cb Callback
|
|
147
|
-
* @event
|
|
151
|
+
* @event onEvent
|
|
148
152
|
*/
|
|
149
153
|
onEvent(cb) {
|
|
150
154
|
this.#callbacks.event.push(cb);
|
|
@@ -203,6 +207,7 @@ module.exports = class Client {
|
|
|
203
207
|
while (this.isOpen && this.#logged && this.#sendQueue.length > 0) {
|
|
204
208
|
const packet = this.#sendQueue.shift();
|
|
205
209
|
this.#ws.send(packet);
|
|
210
|
+
if (global.TW_DEBUG) console.log('§90§30§107 > §0', packet);
|
|
206
211
|
}
|
|
207
212
|
}
|
|
208
213
|
|
|
@@ -218,7 +223,7 @@ module.exports = class Client {
|
|
|
218
223
|
constructor(clientOptions = {}) {
|
|
219
224
|
if (clientOptions.DEBUG) global.TW_DEBUG = clientOptions.DEBUG;
|
|
220
225
|
|
|
221
|
-
this.#ws = new WebSocket('wss://
|
|
226
|
+
this.#ws = new WebSocket('wss://data.tradingview.com/socket.io/websocket', {
|
|
222
227
|
origin: 'https://s.tradingview.com',
|
|
223
228
|
});
|
|
224
229
|
|
package/src/miscRequests.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
const os = require('os');
|
|
1
2
|
const https = require('https');
|
|
2
3
|
const request = require('./request');
|
|
4
|
+
const FormData = require('./FormData');
|
|
3
5
|
|
|
4
6
|
const PinePermManager = require('./classes/PinePermManager');
|
|
5
7
|
const PineIndicator = require('./classes/PineIndicator');
|
|
@@ -8,7 +10,7 @@ const indicators = ['Recommend.Other', 'Recommend.All', 'Recommend.MA'];
|
|
|
8
10
|
const builtInIndicList = [];
|
|
9
11
|
|
|
10
12
|
async function fetchScanData(tickers = [], type = '', columns = []) {
|
|
11
|
-
let data = await request({
|
|
13
|
+
let { data } = await request({
|
|
12
14
|
method: 'POST',
|
|
13
15
|
hostname: 'scanner.tradingview.com',
|
|
14
16
|
path: `/${type}/scan`,
|
|
@@ -28,11 +30,17 @@ async function fetchScanData(tickers = [], type = '', columns = []) {
|
|
|
28
30
|
return data;
|
|
29
31
|
}
|
|
30
32
|
|
|
33
|
+
/** @typedef {number} advice */
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @typedef {{
|
|
37
|
+
* Other: advice,
|
|
38
|
+
* All: advice,
|
|
39
|
+
* MA: advice
|
|
40
|
+
* }} Period
|
|
41
|
+
*/
|
|
42
|
+
|
|
31
43
|
/**
|
|
32
|
-
* @typedef {number} advice
|
|
33
|
-
*
|
|
34
|
-
* @typedef {{ Other: advice, All: advice, MA: advice }} Period
|
|
35
|
-
*
|
|
36
44
|
* @typedef {{
|
|
37
45
|
* '1': Period,
|
|
38
46
|
* '5': Period,
|
|
@@ -41,21 +49,25 @@ async function fetchScanData(tickers = [], type = '', columns = []) {
|
|
|
41
49
|
* '240': Period,
|
|
42
50
|
* '1D': Period,
|
|
43
51
|
* '1W': Period,
|
|
44
|
-
* '1M': Period
|
|
52
|
+
* '1M': Period
|
|
45
53
|
* }} Periods
|
|
46
|
-
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
/**
|
|
47
57
|
* @typedef {string | 'forex' | 'crypto'
|
|
48
58
|
* | 'america' | 'australia' | 'canada' | 'egypt'
|
|
49
59
|
* | 'germany' | 'india' | 'israel' | 'italy'
|
|
50
60
|
* | 'luxembourg' | 'poland' | 'sweden' | 'turkey'
|
|
51
|
-
* | 'uk' | 'vietnam'}
|
|
61
|
+
* | 'uk' | 'vietnam'} Screener
|
|
52
62
|
* You can use `getScreener(exchange)` function for non-forex and non-crypto markets.
|
|
53
63
|
*/
|
|
54
64
|
|
|
55
65
|
module.exports = {
|
|
56
66
|
/**
|
|
67
|
+
* Get a screener from an exchange
|
|
68
|
+
* @function getScreener
|
|
57
69
|
* @param {string} exchange Example: BINANCE, EURONEXT, NASDAQ
|
|
58
|
-
* @returns {
|
|
70
|
+
* @returns {Screener}
|
|
59
71
|
*/
|
|
60
72
|
getScreener(exchange) {
|
|
61
73
|
const e = exchange.toUpperCase();
|
|
@@ -78,7 +90,8 @@ module.exports = {
|
|
|
78
90
|
|
|
79
91
|
/**
|
|
80
92
|
* Get technical analysis
|
|
81
|
-
* @
|
|
93
|
+
* @function getTA
|
|
94
|
+
* @param {Screener} screener Market screener
|
|
82
95
|
* @param {string} id Full market id (Example: COINBASE:BTCEUR)
|
|
83
96
|
* @returns {Promise<Periods>} results
|
|
84
97
|
*/
|
|
@@ -104,24 +117,28 @@ module.exports = {
|
|
|
104
117
|
|
|
105
118
|
/**
|
|
106
119
|
* @typedef {Object} SearchMarketResult
|
|
107
|
-
* @
|
|
108
|
-
* @
|
|
109
|
-
* @
|
|
110
|
-
* @
|
|
111
|
-
* @
|
|
112
|
-
* @
|
|
113
|
-
* @
|
|
114
|
-
* @
|
|
120
|
+
* @prop {string} id Market full symbol
|
|
121
|
+
* @prop {string} exchange Market exchange name
|
|
122
|
+
* @prop {string} fullExchange Market exchange full name
|
|
123
|
+
* @prop {Screener | 'forex' | 'crypto'} screener Market screener
|
|
124
|
+
* @prop {string} symbol Market symbol
|
|
125
|
+
* @prop {string} description Market name
|
|
126
|
+
* @prop {string} type Market type
|
|
127
|
+
* @prop {() => Promise<Periods>} getTA Get market technical analysis
|
|
115
128
|
*/
|
|
116
129
|
|
|
117
130
|
/**
|
|
118
131
|
* Find a symbol
|
|
132
|
+
* @function searchMarket
|
|
119
133
|
* @param {string} search Keywords
|
|
120
|
-
* @param {'stock'
|
|
134
|
+
* @param {'stock'
|
|
135
|
+
* | 'futures' | 'forex' | 'cfd'
|
|
136
|
+
* | 'crypto' | 'index' | 'economic'
|
|
137
|
+
* } [filter] Caterogy filter
|
|
121
138
|
* @returns {Promise<SearchMarketResult[]>} Search results
|
|
122
139
|
*/
|
|
123
140
|
async searchMarket(search, filter = '') {
|
|
124
|
-
const data = await request({
|
|
141
|
+
const { data } = await request({
|
|
125
142
|
host: 'symbol-search.tradingview.com',
|
|
126
143
|
path: `/symbol_search/?text=${search.replace(/ /g, '%20')}&type=${filter}`,
|
|
127
144
|
origin: 'https://www.tradingview.com',
|
|
@@ -151,34 +168,35 @@ module.exports = {
|
|
|
151
168
|
|
|
152
169
|
/**
|
|
153
170
|
* @typedef {Object} SearchIndicatorResult
|
|
154
|
-
* @
|
|
155
|
-
* @
|
|
156
|
-
* @
|
|
157
|
-
* @
|
|
158
|
-
* @
|
|
159
|
-
* @
|
|
160
|
-
* @
|
|
161
|
-
* @
|
|
171
|
+
* @prop {string} id Script ID
|
|
172
|
+
* @prop {string} version Script version
|
|
173
|
+
* @prop {string} name Script complete name
|
|
174
|
+
* @prop {{ id: number, username: string }} author Author user ID
|
|
175
|
+
* @prop {string} image Image ID https://tradingview.com/i/${image}
|
|
176
|
+
* @prop {string | ''} source Script source (if available)
|
|
177
|
+
* @prop {'study' | 'strategy'} type Script type (study / strategy)
|
|
178
|
+
* @prop {'open_source' | 'closed_source' | 'invite_only'
|
|
162
179
|
* | 'private' | 'other'} access Script access type
|
|
163
|
-
* @
|
|
180
|
+
* @prop {() => Promise<PineIndicator>} get Get the full indicator informations
|
|
164
181
|
*/
|
|
165
182
|
|
|
166
183
|
/**
|
|
167
184
|
* Find an indicator
|
|
185
|
+
* @function searchIndicator
|
|
168
186
|
* @param {string} search Keywords
|
|
169
187
|
* @returns {Promise<SearchIndicatorResult[]>} Search results
|
|
170
188
|
*/
|
|
171
189
|
async searchIndicator(search = '') {
|
|
172
190
|
if (!builtInIndicList.length) {
|
|
173
191
|
await Promise.all(['standard', 'candlestick', 'fundamental'].map(async (type) => {
|
|
174
|
-
builtInIndicList.push(...await request({
|
|
192
|
+
builtInIndicList.push(...(await request({
|
|
175
193
|
host: 'pine-facade.tradingview.com',
|
|
176
194
|
path: `/pine-facade/list/?filter=${type}`,
|
|
177
|
-
}));
|
|
195
|
+
})).data);
|
|
178
196
|
}));
|
|
179
197
|
}
|
|
180
198
|
|
|
181
|
-
const data = await request({
|
|
199
|
+
const { data } = await request({
|
|
182
200
|
host: 'www.tradingview.com',
|
|
183
201
|
path: `/pubscripts-suggest-json/?search=${search.replace(/ /g, '%20')}`,
|
|
184
202
|
});
|
|
@@ -229,6 +247,7 @@ module.exports = {
|
|
|
229
247
|
|
|
230
248
|
/**
|
|
231
249
|
* Get an indicator
|
|
250
|
+
* @function getIndicator
|
|
232
251
|
* @param {string} id Indicator ID (Like: PUB;XXXXXXXXXXXXXXXXXXXXX)
|
|
233
252
|
* @param {'last' | string} [version] Wanted version of the indicator
|
|
234
253
|
* @returns {Promise<PineIndicator>} Indicator
|
|
@@ -236,7 +255,7 @@ module.exports = {
|
|
|
236
255
|
async getIndicator(id, version = 'last') {
|
|
237
256
|
const indicID = id.replace(/ |%/g, '%25');
|
|
238
257
|
|
|
239
|
-
let data = await request({
|
|
258
|
+
let { data } = await request({
|
|
240
259
|
host: 'pine-facade.tradingview.com',
|
|
241
260
|
path: `/pine-facade/translate/${indicID}/${version}`,
|
|
242
261
|
}, true);
|
|
@@ -309,25 +328,74 @@ module.exports = {
|
|
|
309
328
|
|
|
310
329
|
/**
|
|
311
330
|
* @typedef {Object} User Instance of User
|
|
312
|
-
* @
|
|
313
|
-
* @
|
|
314
|
-
* @
|
|
315
|
-
* @
|
|
316
|
-
* @
|
|
317
|
-
* @
|
|
318
|
-
* @
|
|
319
|
-
* @
|
|
320
|
-
* @
|
|
321
|
-
* @
|
|
322
|
-
* @
|
|
323
|
-
* @
|
|
324
|
-
* @
|
|
325
|
-
* @
|
|
326
|
-
* @
|
|
331
|
+
* @prop {number} id User ID
|
|
332
|
+
* @prop {string} username User username
|
|
333
|
+
* @prop {string} firstName User first name
|
|
334
|
+
* @prop {string} lastName User last name
|
|
335
|
+
* @prop {number} reputation User reputation
|
|
336
|
+
* @prop {number} following Number of following accounts
|
|
337
|
+
* @prop {number} followers Number of followers
|
|
338
|
+
* @prop {Object} notifications User's notifications
|
|
339
|
+
* @prop {number} notifications.user User notifications
|
|
340
|
+
* @prop {number} notifications.following Notification from following accounts
|
|
341
|
+
* @prop {string} session User session
|
|
342
|
+
* @prop {string} sessionHash User session hash
|
|
343
|
+
* @prop {string} privateChannel User private channel
|
|
344
|
+
* @prop {string} authToken User auth token
|
|
345
|
+
* @prop {Date} joinDate Account creation date
|
|
327
346
|
*/
|
|
328
347
|
|
|
329
348
|
/**
|
|
330
|
-
* Get
|
|
349
|
+
* Get user and sessionid from username/email and password
|
|
350
|
+
* @function loginUser
|
|
351
|
+
* @param {string} username User username/email
|
|
352
|
+
* @param {string} password User password
|
|
353
|
+
* @param {boolean} [remember] Remember the session (default: false)
|
|
354
|
+
* @param {string} [UA] Custom UserAgent
|
|
355
|
+
* @returns {Promise<User>} Token
|
|
356
|
+
*/
|
|
357
|
+
async loginUser(username, password, remember = true, UA = 'TWAPI/3.0') {
|
|
358
|
+
const formData = new FormData();
|
|
359
|
+
formData.append('username', username);
|
|
360
|
+
formData.append('password', password);
|
|
361
|
+
if (remember) formData.append('remember', 'on');
|
|
362
|
+
|
|
363
|
+
const { data, cookies } = await request({
|
|
364
|
+
method: 'POST',
|
|
365
|
+
host: 'www.tradingview.com',
|
|
366
|
+
path: '/accounts/signin/',
|
|
367
|
+
headers: {
|
|
368
|
+
referer: 'https://www.tradingview.com',
|
|
369
|
+
'Content-Type': `multipart/form-data; boundary=${formData.boundary}`,
|
|
370
|
+
'User-agent': `${UA} (${os.version()}; ${os.platform()}; ${os.arch()})`,
|
|
371
|
+
},
|
|
372
|
+
}, false, formData.toString());
|
|
373
|
+
|
|
374
|
+
if (data.error) throw new Error(data.error);
|
|
375
|
+
|
|
376
|
+
const cookie = cookies.find((c) => c.includes('sessionid='));
|
|
377
|
+
const session = (cookie.match(/sessionid=(.*?);/) ?? [])[1];
|
|
378
|
+
|
|
379
|
+
return {
|
|
380
|
+
id: data.user.id,
|
|
381
|
+
username: data.user.username,
|
|
382
|
+
firstName: data.user.first_name,
|
|
383
|
+
lastName: data.user.last_name,
|
|
384
|
+
reputation: data.user.reputation,
|
|
385
|
+
following: data.user.following,
|
|
386
|
+
followers: data.user.followers,
|
|
387
|
+
notifications: data.user.notification_count,
|
|
388
|
+
session,
|
|
389
|
+
sessionHash: data.user.session_hash,
|
|
390
|
+
privateChannel: data.user.private_channel,
|
|
391
|
+
authToken: data.user.auth_token,
|
|
392
|
+
joinDate: new Date(data.user.date_joined),
|
|
393
|
+
};
|
|
394
|
+
},
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Get user from 'sessionid' cookie
|
|
398
|
+
* @function getUser
|
|
331
399
|
* @param {string} session User 'sessionid' cookie
|
|
332
400
|
* @param {string} [location] Auth page location (For france: https://fr.tradingview.com/)
|
|
333
401
|
* @returns {Promise<User>} Token
|
|
@@ -373,6 +441,7 @@ module.exports = {
|
|
|
373
441
|
|
|
374
442
|
/**
|
|
375
443
|
* Get user's private indicators from a 'sessionid' cookie
|
|
444
|
+
* @function getPrivateIndicators
|
|
376
445
|
* @param {string} session User 'sessionid' cookie
|
|
377
446
|
* @returns {Promise<SearchIndicatorResult[]>} Search results
|
|
378
447
|
*/
|
|
@@ -420,12 +489,13 @@ module.exports = {
|
|
|
420
489
|
/**
|
|
421
490
|
* User credentials
|
|
422
491
|
* @typedef {Object} UserCredentials
|
|
423
|
-
* @
|
|
424
|
-
* @
|
|
492
|
+
* @prop {number} id User ID
|
|
493
|
+
* @prop {string} session User session ('sessionid' cookie)
|
|
425
494
|
*/
|
|
426
495
|
|
|
427
496
|
/**
|
|
428
497
|
* Get a chart token from a layout ID and the user credentials if the layout is not public
|
|
498
|
+
* @function getChartToken
|
|
429
499
|
* @param {string} layout The layout ID found in the layout URL (Like: 'XXXXXXXX')
|
|
430
500
|
* @param {UserCredentials} [credentials] User credentials (id + session)
|
|
431
501
|
* @returns {Promise<string>} Token
|
|
@@ -435,7 +505,7 @@ module.exports = {
|
|
|
435
505
|
const userID = creds ? credentials.id : -1;
|
|
436
506
|
const session = creds ? credentials.session : null;
|
|
437
507
|
|
|
438
|
-
const data = await request({
|
|
508
|
+
const { data } = await request({
|
|
439
509
|
host: 'www.tradingview.com',
|
|
440
510
|
path: `/chart-token/?image_url=${layout}&user_id=${userID}`,
|
|
441
511
|
headers: { cookie: session ? `sessionid=${session}` : '' },
|
|
@@ -448,32 +518,33 @@ module.exports = {
|
|
|
448
518
|
|
|
449
519
|
/**
|
|
450
520
|
* @typedef {Object} DrawingPoint Drawing poitn
|
|
451
|
-
* @
|
|
452
|
-
* @
|
|
453
|
-
* @
|
|
521
|
+
* @prop {number} time_t Point X time position
|
|
522
|
+
* @prop {number} price Point Y price position
|
|
523
|
+
* @prop {number} offset Point offset
|
|
454
524
|
*/
|
|
455
525
|
|
|
456
526
|
/**
|
|
457
527
|
* @typedef {Object} Drawing
|
|
458
|
-
* @
|
|
459
|
-
* @
|
|
460
|
-
* @
|
|
461
|
-
* @
|
|
462
|
-
* @
|
|
463
|
-
* @
|
|
464
|
-
* @
|
|
465
|
-
* @
|
|
466
|
-
* @
|
|
467
|
-
* @
|
|
468
|
-
* @
|
|
528
|
+
* @prop {string} id Drawing ID (Like: 'XXXXXX')
|
|
529
|
+
* @prop {string} symbol Layout market symbol (Like: 'BINANCE:BUCEUR')
|
|
530
|
+
* @prop {string} ownerSource Owner user ID (Like: 'XXXXXX')
|
|
531
|
+
* @prop {string} serverUpdateTime Drawing last update timestamp
|
|
532
|
+
* @prop {string} currencyId Currency ID (Like: 'EUR')
|
|
533
|
+
* @prop {any} unitId Unit ID
|
|
534
|
+
* @prop {string} type Drawing type
|
|
535
|
+
* @prop {DrawingPoint[]} points List of drawing points
|
|
536
|
+
* @prop {number} zorder Drawing Z order
|
|
537
|
+
* @prop {string} linkKey Drawing link key
|
|
538
|
+
* @prop {Object} state Drawing state
|
|
469
539
|
*/
|
|
470
540
|
|
|
471
541
|
/**
|
|
472
542
|
* Get a chart token from a layout ID and the user credentials if the layout is not public
|
|
543
|
+
* @function getDrawings
|
|
473
544
|
* @param {string} layout The layout ID found in the layout URL (Like: 'XXXXXXXX')
|
|
474
545
|
* @param {string | ''} [symbol] Market filter (Like: 'BINANCE:BTCEUR')
|
|
475
546
|
* @param {UserCredentials} [credentials] User credentials (id + session)
|
|
476
|
-
* @param {number} [chartID
|
|
547
|
+
* @param {number} [chartID] Chart ID
|
|
477
548
|
* @returns {Promise<Drawing[]>} Drawings
|
|
478
549
|
*/
|
|
479
550
|
async getDrawings(layout, symbol = '', credentials = {}, chartID = 1) {
|
|
@@ -481,7 +552,7 @@ module.exports = {
|
|
|
481
552
|
const creds = credentials.id && credentials.session;
|
|
482
553
|
const session = creds ? credentials.session : null;
|
|
483
554
|
|
|
484
|
-
const data = await request({
|
|
555
|
+
const { data } = await request({
|
|
485
556
|
host: 'charts-storage.tradingview.com',
|
|
486
557
|
path: `/charts-storage/layout/${layout}/sources?chart_id=${chartID
|
|
487
558
|
}&jwt=${chartToken}${symbol ? `&symbol=${symbol}` : ''}`,
|
package/src/protocol.js
CHANGED
|
@@ -6,14 +6,18 @@ const JSZip = require('jszip');
|
|
|
6
6
|
* @prop {[session: string, {}]} [p] Packet data
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
const cleanerRgx = /~h~/g;
|
|
10
|
+
const splitterRgx = /~m~[0-9]{1,}~m~/g;
|
|
11
|
+
|
|
9
12
|
module.exports = {
|
|
10
13
|
/**
|
|
11
14
|
* Parse websocket packet
|
|
15
|
+
* @function parseWSPacket
|
|
12
16
|
* @param {string} str Websocket raw data
|
|
13
17
|
* @returns {TWPacket[]} TradingView packets
|
|
14
18
|
*/
|
|
15
19
|
parseWSPacket(str) {
|
|
16
|
-
return str.replace(
|
|
20
|
+
return str.replace(cleanerRgx, '').split(splitterRgx)
|
|
17
21
|
.map((p) => {
|
|
18
22
|
if (!p) return false;
|
|
19
23
|
try {
|
|
@@ -28,6 +32,7 @@ module.exports = {
|
|
|
28
32
|
|
|
29
33
|
/**
|
|
30
34
|
* Format websocket packet
|
|
35
|
+
* @function formatWSPacket
|
|
31
36
|
* @param {TWPacket} packet TradingView packet
|
|
32
37
|
* @returns {string} Websocket raw data
|
|
33
38
|
*/
|
|
@@ -40,6 +45,7 @@ module.exports = {
|
|
|
40
45
|
|
|
41
46
|
/**
|
|
42
47
|
* Parse compressed data
|
|
48
|
+
* @function parseCompressed
|
|
43
49
|
* @param {string} data Compressed data
|
|
44
50
|
* @returns {Promise<{}>} Parsed data
|
|
45
51
|
*/
|
package/src/request.js
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
const https = require('https');
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @param {https.RequestOptions} options HTTPS Request options
|
|
5
|
-
* @param {boolean} [raw] Get raw or JSON data
|
|
6
|
-
* @param {string} [content] Request body content
|
|
7
|
-
* @returns {Promise<string | object | array>} Result
|
|
8
|
-
*/
|
|
9
|
-
function request(options = {}, raw = false, content = '') {
|
|
10
|
-
return new Promise((cb, err) => {
|
|
11
|
-
const req = https.request(options, (res) => {
|
|
12
|
-
let data = '';
|
|
13
|
-
res.on('data', (c) => { data += c; });
|
|
14
|
-
res.on('end', () => {
|
|
15
|
-
if (raw) {
|
|
16
|
-
cb(data);
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
try {
|
|
21
|
-
data = JSON.parse(data);
|
|
22
|
-
} catch (error) {
|
|
23
|
-
console.log(data);
|
|
24
|
-
err(new Error('Can\'t parse server response'));
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
cb(data);
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
req.on('error', err);
|
|
33
|
-
req.end(content);
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
module.exports = request;
|
|
1
|
+
const https = require('https');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @param {https.RequestOptions} options HTTPS Request options
|
|
5
|
+
* @param {boolean} [raw] Get raw or JSON data
|
|
6
|
+
* @param {string} [content] Request body content
|
|
7
|
+
* @returns {Promise<{ data: (string | object | array), cookies: string[] }>} Result
|
|
8
|
+
*/
|
|
9
|
+
function request(options = {}, raw = false, content = '') {
|
|
10
|
+
return new Promise((cb, err) => {
|
|
11
|
+
const req = https.request(options, (res) => {
|
|
12
|
+
let data = '';
|
|
13
|
+
res.on('data', (c) => { data += c; });
|
|
14
|
+
res.on('end', () => {
|
|
15
|
+
if (raw) {
|
|
16
|
+
cb({ data, cookies: res.headers['set-cookie'] });
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
data = JSON.parse(data);
|
|
22
|
+
} catch (error) {
|
|
23
|
+
console.log(data);
|
|
24
|
+
err(new Error('Can\'t parse server response'));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
cb({ data, cookies: res.headers['set-cookie'] });
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
req.on('error', err);
|
|
33
|
+
req.end(content);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = request;
|
package/src/types.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef {string} MarketSymbol Market symbol (like: 'BTCEUR' or 'KRAKEN:BTCEUR')
|
|
3
|
-
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
4
6
|
* @typedef {'Etc/UTC' | 'exchange'
|
|
5
7
|
* | 'Pacific/Honolulu' | 'America/Juneau' | 'America/Los_Angeles'
|
|
6
8
|
* | 'America/Phoenix' | 'America/Vancouver' | 'US/Mountain'
|
|
@@ -23,7 +25,9 @@
|
|
|
23
25
|
* | 'Asia/Taipei' | 'Asia/Seoul' | 'Asia/Tokyo' | 'Australia/Brisbane'
|
|
24
26
|
* | 'Australia/Adelaide' | 'Australia/Sydney' | 'Pacific/Norfolk'
|
|
25
27
|
* | 'Pacific/Auckland' | 'Pacific/Fakaofo' | 'Pacific/Chatham'} Timezone (Chart) timezone
|
|
26
|
-
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
27
31
|
* @typedef {'1' | '3' | '5' | '15' | '30'
|
|
28
32
|
* | '45' | '60' | '120' | '180' | '240'
|
|
29
33
|
* | '1D' | '1W' | '1M' | 'D' | 'W' | 'M'} TimeFrame
|
package/src/utils.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
module.exports = {
|
|
2
|
+
/**
|
|
3
|
+
* Generates a session id
|
|
4
|
+
* @function genSessionID
|
|
5
|
+
* @param {String} type Session type
|
|
6
|
+
* @returns {string}
|
|
7
|
+
*/
|
|
2
8
|
genSessionID(type = 'xs') {
|
|
3
9
|
let r = '';
|
|
4
10
|
const c = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
package/test.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable no-continue */
|
|
1
2
|
/* eslint-disable no-await-in-loop */
|
|
2
3
|
/* eslint-disable import/no-dynamic-require */
|
|
3
4
|
/* eslint-disable no-restricted-syntax */
|
|
@@ -37,6 +38,7 @@ function success() {
|
|
|
37
38
|
console.info('§90§30§104 ==== Starting tests ==== §0');
|
|
38
39
|
|
|
39
40
|
for (const file of fs.readdirSync('./tests').filter((f) => f.endsWith('.js'))) {
|
|
41
|
+
if (process.argv[2] && !file.startsWith(process.argv[2])) continue;
|
|
40
42
|
const test = require(`./tests/${file}`);
|
|
41
43
|
const name = file.replace(/\.js/g, '');
|
|
42
44
|
TESTS[name] = { errors: 0, warnings: 0 };
|
package/tests/4. Indicators.js
CHANGED
|
@@ -65,9 +65,10 @@ module.exports = async (log, success, warn, err, cb) => {
|
|
|
65
65
|
|
|
66
66
|
QTY += 10;
|
|
67
67
|
log('TRY WITH', QTY, '%');
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
setTimeout(() => {
|
|
69
|
+
indicator.setOption('default_qty_value', QTY);
|
|
70
|
+
SuperTrend.setIndicator(indicator);
|
|
71
|
+
}, 1000);
|
|
71
72
|
});
|
|
72
73
|
});
|
|
73
74
|
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
const TradingView = require('../main');
|
|
2
|
-
|
|
3
|
-
/*
|
|
4
|
-
This example tests an indicator that sends
|
|
5
|
-
graphic data such as 'lines', 'labels',
|
|
6
|
-
'boxes', 'tables', 'polygons', etc...
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const client = new TradingView.Client();
|
|
10
|
-
|
|
11
|
-
const chart = new client.Session.Chart();
|
|
12
|
-
|
|
13
|
-
let wantedTimeframe = '5';
|
|
14
|
-
let timeframe = null;
|
|
15
|
-
|
|
16
|
-
chart.setMarket('BINANCE:BTCEUR', {
|
|
17
|
-
timeframe: wantedTimeframe,
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
chart.onSymbolLoaded(() => {
|
|
21
|
-
timeframe = wantedTimeframe;
|
|
22
|
-
console.log(`Timeframe is now '${timeframe}'`);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
chart.onUpdate(() => {
|
|
26
|
-
console.log(`Received data on '${timeframe}' timeframe`);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
setTimeout(() => {
|
|
30
|
-
wantedTimeframe = '240';
|
|
31
|
-
chart.setMarket('BINANCE:BTCEUR', {
|
|
32
|
-
timeframe: wantedTimeframe,
|
|
33
|
-
});
|
|
34
|
-
}, 3000);
|
|
@@ -1,41 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
const TradingView = require('../main');
|
|
2
|
-
|
|
3
|
-
module.exports = async (log, success, warn, err, cb) => {
|
|
4
|
-
const client = new TradingView.Client();
|
|
5
|
-
|
|
6
|
-
client.onError((...error) => {
|
|
7
|
-
err('Client error', error);
|
|
8
|
-
throw new Error('Client error');
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
const chart = new client.Session.Chart();
|
|
12
|
-
chart.setMarket('BINANCE:BTCEUR', {
|
|
13
|
-
timeframe: '60',
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
chart.onError((...error) => {
|
|
17
|
-
err('Chart error', error);
|
|
18
|
-
throw new Error('Chart error');
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
chart.delete();
|
|
22
|
-
await client.end();
|
|
23
|
-
cb();
|
|
24
|
-
};
|