@@ -2,17 +2,22 @@ const os = require('os');
22const axios = require ( 'axios' ) ;
33
44const PineIndicator = require ( './classes/PineIndicator' ) ;
5+ const { genAuthCookies } = require ( './utils' ) ;
56
67const validateStatus = ( status ) => status < 500 ;
78
89const indicators = [ 'Recommend.Other' , 'Recommend.All' , 'Recommend.MA' ] ;
910const builtInIndicList = [ ] ;
1011
11- async function fetchScanData ( tickers = [ ] , type = '' , columns = [ ] ) {
12- const { data } = await axios . post ( `https://scanner.tradingview.com/${ type } /scan` , {
13- symbols : { tickers } ,
14- columns,
15- } , { validateStatus } ) ;
12+ async function fetchScanData ( tickers = [ ] , columns = [ ] ) {
13+ const { data } = await axios . post (
14+ 'https://scanner.tradingview.com/global/scan' ,
15+ {
16+ symbols : { tickers } ,
17+ columns,
18+ } ,
19+ { validateStatus } ,
20+ ) ;
1621
1722 return data ;
1823}
@@ -40,56 +45,21 @@ async function fetchScanData(tickers = [], type = '', columns = []) {
4045 * }} Periods
4146 */
4247
43- // /**
44- // * @typedef {string | 'forex' | 'crypto'
45- // * | 'america' | 'australia' | 'canada' | 'egypt'
46- // * | 'germany' | 'india' | 'israel' | 'italy'
47- // * | 'luxembourg' | 'poland' | 'sweden' | 'turkey'
48- // * | 'uk' | 'vietnam' } Screener
49- // * You can use `getScreener(exchange)` function for non-forex and non-crypto markets.
50- // */
51-
5248module . exports = {
53- // /**
54- // * Get a screener from an exchange
55- // * @function getScreener
56- // * @param {string } exchange Example: BINANCE, EURONEXT, NASDAQ
57- // * @returns {Screener }
58- // */
59- // getScreener(exchange) {
60- // const e = exchange.toUpperCase();
61- // if (['NASDAQ', 'NYSE', 'NYSE ARCA', 'OTC'].includes(e)) return 'america';
62- // if (['ASX'].includes(e)) return 'australia';
63- // if (['TSX', 'TSXV', 'CSE', 'NEO'].includes(e)) return 'canada';
64- // if (['EGX'].includes(e)) return 'egypt';
65- // if (['FWB', 'SWB', 'XETR'].includes(e)) return 'germany';
66- // if (['BSE', 'NSE'].includes(e)) return 'india';
67- // if (['TASE'].includes(e)) return 'israel';
68- // if (['MIL', 'MILSEDEX'].includes(e)) return 'italy';
69- // if (['LUXSE'].includes(e)) return 'luxembourg';
70- // if (['NEWCONNECT'].includes(e)) return 'poland';
71- // if (['NGM'].includes(e)) return 'sweden';
72- // if (['BIST'].includes(e)) return 'turkey';
73- // if (['LSE', 'LSIN'].includes(e)) return 'uk';
74- // if (['HNX'].includes(e)) return 'vietnam';
75- // return 'global';
76- // },
77-
7849 /**
7950 * Get technical analysis
8051 * @function getTA
81- * @param {Screener } screener Market screener
8252 * @param {string } id Full market id (Example: COINBASE:BTCEUR)
8353 * @returns {Promise<Periods> } results
8454 */
85- async getTA ( screener , id ) {
55+ async getTA ( id ) {
8656 const advice = { } ;
8757
8858 const cols = [ '1' , '5' , '15' , '60' , '240' , '1D' , '1W' , '1M' ]
8959 . map ( ( t ) => indicators . map ( ( i ) => ( t !== '1D' ? `${ i } |${ t } ` : i ) ) )
9060 . flat ( ) ;
9161
92- const rs = await fetchScanData ( [ id ] , screener , cols ) ;
62+ const rs = await fetchScanData ( [ id ] , cols ) ;
9363 if ( ! rs . data || ! rs . data [ 0 ] ) return false ;
9464
9565 rs . data [ 0 ] . d . forEach ( ( val , i ) => {
@@ -107,53 +77,99 @@ module.exports = {
10777 * @prop {string } id Market full symbol
10878 * @prop {string } exchange Market exchange name
10979 * @prop {string } fullExchange Market exchange full name
110- * @prop {Screener | 'forex' | 'crypto' } screener Market screener
11180 * @prop {string } symbol Market symbol
11281 * @prop {string } description Market name
11382 * @prop {string } type Market type
11483 * @prop {() => Promise<Periods> } getTA Get market technical analysis
11584 */
11685
11786 /**
118- * Find a symbol
87+ * Find a symbol (deprecated)
11988 * @function searchMarket
12089 * @param {string } search Keywords
12190 * @param {'stock'
12291 * | 'futures' | 'forex' | 'cfd'
12392 * | 'crypto' | 'index' | 'economic'
12493 * } [filter] Caterogy filter
12594 * @returns {Promise<SearchMarketResult[]> } Search results
95+ * @deprecated Use searchMarketV3 instead
12696 */
12797 async searchMarket ( search , filter = '' ) {
12898 const { data } = await axios . get (
129- ` https://symbol-search.tradingview.com/symbol_search/?text= ${ search . replace ( / / g , '%20' ) } &type= ${ filter } ` ,
99+ ' https://symbol-search.tradingview.com/symbol_search' ,
130100 {
131- validateStatus,
101+ params : {
102+ text : search . replace ( / / g, '%20' ) ,
103+ type : filter ,
104+ } ,
132105 headers : {
133106 origin : 'https://www.tradingview.com' ,
134107 } ,
108+ validateStatus,
135109 } ,
136110 ) ;
137111
138112 return data . map ( ( s ) => {
139113 const exchange = s . exchange . split ( ' ' ) [ 0 ] ;
140114 const id = `${ exchange } :${ s . symbol } ` ;
141115
142- // const screener = (['forex', 'crypto'].includes(s.type)
143- // ? s.type
144- // : this.getScreener(exchange)
145- // );
146- const screener = 'global' ;
116+ return {
117+ id,
118+ exchange,
119+ fullExchange : s . exchange ,
120+ symbol : s . symbol ,
121+ description : s . description ,
122+ type : s . type ,
123+ getTA : ( ) => this . getTA ( id ) ,
124+ } ;
125+ } ) ;
126+ } ,
127+
128+ /**
129+ * Find a symbol
130+ * @function searchMarketV3
131+ * @param {string } search Keywords
132+ * @param {'stock'
133+ * | 'futures' | 'forex' | 'cfd'
134+ * | 'crypto' | 'index' | 'economic'
135+ * } [filter] Caterogy filter
136+ * @returns {Promise<SearchMarketResult[]> } Search results
137+ */
138+ async searchMarketV3 ( search , filter = '' ) {
139+ const splittedSearch = search . toUpperCase ( ) . replace ( / / g, '+' ) . split ( ':' ) ;
140+
141+ const request = await axios . get (
142+ 'https://symbol-search.tradingview.com/symbol_search/v3' ,
143+ {
144+ params : {
145+ exchange : ( splittedSearch . length === 2
146+ ? splittedSearch [ 0 ]
147+ : undefined
148+ ) ,
149+ text : splittedSearch . pop ( ) ,
150+ search_type : filter ,
151+ } ,
152+ headers : {
153+ origin : 'https://www.tradingview.com' ,
154+ } ,
155+ validateStatus,
156+ } ,
157+ ) ;
158+
159+ const { data } = request ;
160+
161+ return data . symbols . map ( ( s ) => {
162+ const exchange = s . exchange . split ( ' ' ) [ 0 ] ;
163+ const id = `${ exchange . toUpperCase ( ) } :${ s . symbol } ` ;
147164
148165 return {
149166 id,
150167 exchange,
151168 fullExchange : s . exchange ,
152- screener,
153169 symbol : s . symbol ,
154170 description : s . description ,
155171 type : s . type ,
156- getTA : ( ) => this . getTA ( screener , id ) ,
172+ getTA : ( ) => this . getTA ( id ) ,
157173 } ;
158174 } ) ;
159175 } ,
@@ -182,16 +198,26 @@ module.exports = {
182198 if ( ! builtInIndicList . length ) {
183199 await Promise . all ( [ 'standard' , 'candlestick' , 'fundamental' ] . map ( async ( type ) => {
184200 const { data } = await axios . get (
185- `https://pine-facade.tradingview.com/pine-facade/list/?filter=${ type } ` ,
186- { validateStatus } ,
201+ 'https://pine-facade.tradingview.com/pine-facade/list' ,
202+ {
203+ params : {
204+ filter : type ,
205+ } ,
206+ validateStatus,
207+ } ,
187208 ) ;
188209 builtInIndicList . push ( ...data ) ;
189210 } ) ) ;
190211 }
191212
192213 const { data } = await axios . get (
193- `https://www.tradingview.com/pubscripts-suggest-json/?search=${ search . replace ( / / g, '%20' ) } ` ,
194- { validateStatus } ,
214+ 'https://www.tradingview.com/pubscripts-suggest-json' ,
215+ {
216+ params : {
217+ search : search . replace ( / / g, '%20' ) ,
218+ } ,
219+ validateStatus,
220+ } ,
195221 ) ;
196222
197223 function norm ( str = '' ) {
@@ -253,10 +279,10 @@ module.exports = {
253279 const { data } = await axios . get (
254280 `https://pine-facade.tradingview.com/pine-facade/translate/${ indicID } /${ version } ` ,
255281 {
256- validateStatus,
257282 headers : {
258- cookie : ` ${ session ? `sessionid= ${ session } ;` : '' } ${ signature ? `sessionid_sign= ${ signature } ;` : '' } ` ,
283+ cookie : genAuthCookies ( session , signature ) ,
259284 } ,
285+ validateStatus,
260286 } ,
261287 ) ;
262288
@@ -356,12 +382,12 @@ module.exports = {
356382 'https://www.tradingview.com/accounts/signin/' ,
357383 `username=${ username } &password=${ password } ${ remember ? '&remember=on' : '' } ` ,
358384 {
359- validateStatus,
360385 headers : {
361386 referer : 'https://www.tradingview.com' ,
362387 'Content-Type' : 'application/x-www-form-urlencoded' ,
363388 'User-agent' : `${ UA } (${ os . version ( ) } ; ${ os . platform ( ) } ; ${ os . arch ( ) } )` ,
364389 } ,
390+ validateStatus,
365391 } ,
366392 ) ;
367393
@@ -403,10 +429,10 @@ module.exports = {
403429 */
404430 async getUser ( session , signature = '' , location = 'https://www.tradingview.com/' ) {
405431 const { data } = await axios . get ( location , {
406- validateStatus,
407432 headers : {
408- cookie : `sessionid= ${ session } ${ signature ? `;sessionid_sign= ${ signature } ;` : '' } ` ,
433+ cookie : genAuthCookies ( session , signature ) ,
409434 } ,
435+ validateStatus,
410436 } ) ;
411437
412438 if ( data . includes ( 'auth_token' ) ) {
@@ -442,12 +468,18 @@ module.exports = {
442468 * @returns {Promise<SearchIndicatorResult[]> } Search results
443469 */
444470 async getPrivateIndicators ( session , signature = '' ) {
445- const { data } = await axios . get ( 'https://pine-facade.tradingview.com/pine-facade/list?filter=saved' , {
446- validateStatus,
447- headers : {
448- cookie : `sessionid=${ session } ${ signature ? `;sessionid_sign=${ signature } ;` : '' } ` ,
471+ const { data } = await axios . get (
472+ 'https://pine-facade.tradingview.com/pine-facade/list' ,
473+ {
474+ headers : {
475+ cookie : genAuthCookies ( session , signature ) ,
476+ } ,
477+ params : {
478+ filter : 'saved' ,
479+ } ,
480+ validateStatus,
449481 } ,
450- } ) ;
482+ ) ;
451483
452484 return data . map ( ( ind ) => ( {
453485 id : ind . scriptIdPart ,
@@ -495,14 +527,16 @@ module.exports = {
495527 ) ;
496528
497529 const { data } = await axios . get (
498- ` https://www.tradingview.com/chart-token/?image_url= ${ layout } &user_id= ${ id } ` ,
530+ ' https://www.tradingview.com/chart-token' ,
499531 {
500- validateStatus,
501532 headers : {
502- cookie : session
503- ? `sessionid=${ session } ${ signature ? `;sessionid_sign=${ signature } ;` : '' } `
504- : '' ,
533+ cookie : genAuthCookies ( session , signature ) ,
505534 } ,
535+ params : {
536+ image_url : layout ,
537+ user_id : id ,
538+ } ,
539+ validateStatus,
506540 } ,
507541 ) ;
508542
@@ -548,14 +582,15 @@ module.exports = {
548582 const { data } = await axios . get (
549583 `https://charts-storage.tradingview.com/charts-storage/get/layout/${
550584 layout
551- } /sources?chart_id=${
552- chartID
553- } &jwt=${
554- chartToken
555- } ${
556- ( symbol ? `&symbol=${ symbol } ` : '' )
557- } `,
558- { validateStatus } ,
585+ } /sources`,
586+ {
587+ params : {
588+ chart_id : chartID ,
589+ jwt : chartToken ,
590+ symbol,
591+ } ,
592+ validateStatus,
593+ } ,
559594 ) ;
560595
561596 if ( ! data . payload ) throw new Error ( 'Wrong layout, user credentials, or chart id.' ) ;
0 commit comments