Skip to content

Commit

Permalink
Merge pull request #252 from Mathieu2301/hotfix/searchmarket-wrong-re…
Browse files Browse the repository at this point in the history
…sults

fix(searchmarket): use of the new /v3/ endpoint
  • Loading branch information
Mathieu2301 authored Aug 15, 2024
2 parents 5544817 + ab14827 commit e8a07f9
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 101 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ module.exports = {
'no-restricted-syntax': 'off',
'no-await-in-loop': 'off',
'no-continue': 'off',
'guard-for-in': 'off',
},
};
9 changes: 5 additions & 4 deletions src/classes/PinePermManager.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const axios = require('axios');
const { genAuthCookies } = require('../utils');

/**
* @typedef {Object} AuthorizationUser
Expand Down Expand Up @@ -50,7 +51,7 @@ class PinePermManager {
headers: {
origin: 'https://www.tradingview.com',
'Content-Type': 'application/x-www-form-urlencoded',
cookie: `sessionid=${this.sessionId};sessionid_sign=${this.signature};`,
cookie: genAuthCookies(this.sessionId, this.signature),
},
},
);
Expand Down Expand Up @@ -84,7 +85,7 @@ class PinePermManager {
headers: {
origin: 'https://www.tradingview.com',
'Content-Type': 'application/x-www-form-urlencoded',
cookie: `sessionid=${this.sessionId};sessionid_sign=${this.signature};`,
cookie: genAuthCookies(this.sessionId, this.signature),
},
},
);
Expand Down Expand Up @@ -118,7 +119,7 @@ class PinePermManager {
headers: {
origin: 'https://www.tradingview.com',
'Content-Type': 'application/x-www-form-urlencoded',
cookie: `sessionid=${this.sessionId};sessionid_sign=${this.signature};`,
cookie: genAuthCookies(this.sessionId, this.signature),
},
},
);
Expand All @@ -143,7 +144,7 @@ class PinePermManager {
headers: {
origin: 'https://www.tradingview.com',
'Content-Type': 'application/x-www-form-urlencoded',
cookie: `sessionid=${this.sessionId};sessionid_sign=${this.signature};`,
cookie: genAuthCookies(this.sessionId, this.signature),
},
},
);
Expand Down
195 changes: 115 additions & 80 deletions src/miscRequests.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,22 @@ const os = require('os');
const axios = require('axios');

const PineIndicator = require('./classes/PineIndicator');
const { genAuthCookies } = require('./utils');

const validateStatus = (status) => status < 500;

const indicators = ['Recommend.Other', 'Recommend.All', 'Recommend.MA'];
const builtInIndicList = [];

async function fetchScanData(tickers = [], type = '', columns = []) {
const { data } = await axios.post(`https://scanner.tradingview.com/${type}/scan`, {
symbols: { tickers },
columns,
}, { validateStatus });
async function fetchScanData(tickers = [], columns = []) {
const { data } = await axios.post(
'https://scanner.tradingview.com/global/scan',
{
symbols: { tickers },
columns,
},
{ validateStatus },
);

return data;
}
Expand Down Expand Up @@ -40,56 +45,21 @@ async function fetchScanData(tickers = [], type = '', columns = []) {
* }} Periods
*/

// /**
// * @typedef {string | 'forex' | 'crypto'
// * | 'america' | 'australia' | 'canada' | 'egypt'
// * | 'germany' | 'india' | 'israel' | 'italy'
// * | 'luxembourg' | 'poland' | 'sweden' | 'turkey'
// * | 'uk' | 'vietnam'} Screener
// * You can use `getScreener(exchange)` function for non-forex and non-crypto markets.
// */

module.exports = {
// /**
// * Get a screener from an exchange
// * @function getScreener
// * @param {string} exchange Example: BINANCE, EURONEXT, NASDAQ
// * @returns {Screener}
// */
// getScreener(exchange) {
// const e = exchange.toUpperCase();
// if (['NASDAQ', 'NYSE', 'NYSE ARCA', 'OTC'].includes(e)) return 'america';
// if (['ASX'].includes(e)) return 'australia';
// if (['TSX', 'TSXV', 'CSE', 'NEO'].includes(e)) return 'canada';
// if (['EGX'].includes(e)) return 'egypt';
// if (['FWB', 'SWB', 'XETR'].includes(e)) return 'germany';
// if (['BSE', 'NSE'].includes(e)) return 'india';
// if (['TASE'].includes(e)) return 'israel';
// if (['MIL', 'MILSEDEX'].includes(e)) return 'italy';
// if (['LUXSE'].includes(e)) return 'luxembourg';
// if (['NEWCONNECT'].includes(e)) return 'poland';
// if (['NGM'].includes(e)) return 'sweden';
// if (['BIST'].includes(e)) return 'turkey';
// if (['LSE', 'LSIN'].includes(e)) return 'uk';
// if (['HNX'].includes(e)) return 'vietnam';
// return 'global';
// },

/**
* Get technical analysis
* @function getTA
* @param {Screener} screener Market screener
* @param {string} id Full market id (Example: COINBASE:BTCEUR)
* @returns {Promise<Periods>} results
*/
async getTA(screener, id) {
async getTA(id) {
const advice = {};

const cols = ['1', '5', '15', '60', '240', '1D', '1W', '1M']
.map((t) => indicators.map((i) => (t !== '1D' ? `${i}|${t}` : i)))
.flat();

const rs = await fetchScanData([id], screener, cols);
const rs = await fetchScanData([id], cols);
if (!rs.data || !rs.data[0]) return false;

rs.data[0].d.forEach((val, i) => {
Expand All @@ -107,53 +77,99 @@ module.exports = {
* @prop {string} id Market full symbol
* @prop {string} exchange Market exchange name
* @prop {string} fullExchange Market exchange full name
* @prop {Screener | 'forex' | 'crypto'} screener Market screener
* @prop {string} symbol Market symbol
* @prop {string} description Market name
* @prop {string} type Market type
* @prop {() => Promise<Periods>} getTA Get market technical analysis
*/

/**
* Find a symbol
* Find a symbol (deprecated)
* @function searchMarket
* @param {string} search Keywords
* @param {'stock'
* | 'futures' | 'forex' | 'cfd'
* | 'crypto' | 'index' | 'economic'
* } [filter] Caterogy filter
* @returns {Promise<SearchMarketResult[]>} Search results
* @deprecated Use searchMarketV3 instead
*/
async searchMarket(search, filter = '') {
const { data } = await axios.get(
`https://symbol-search.tradingview.com/symbol_search/?text=${search.replace(/ /g, '%20')}&type=${filter}`,
'https://symbol-search.tradingview.com/symbol_search',
{
validateStatus,
params: {
text: search.replace(/ /g, '%20'),
type: filter,
},
headers: {
origin: 'https://www.tradingview.com',
},
validateStatus,
},
);

return data.map((s) => {
const exchange = s.exchange.split(' ')[0];
const id = `${exchange}:${s.symbol}`;

// const screener = (['forex', 'crypto'].includes(s.type)
// ? s.type
// : this.getScreener(exchange)
// );
const screener = 'global';
return {
id,
exchange,
fullExchange: s.exchange,
symbol: s.symbol,
description: s.description,
type: s.type,
getTA: () => this.getTA(id),
};
});
},

/**
* Find a symbol
* @function searchMarketV3
* @param {string} search Keywords
* @param {'stock'
* | 'futures' | 'forex' | 'cfd'
* | 'crypto' | 'index' | 'economic'
* } [filter] Caterogy filter
* @returns {Promise<SearchMarketResult[]>} Search results
*/
async searchMarketV3(search, filter = '') {
const splittedSearch = search.toUpperCase().replace(/ /g, '+').split(':');

const request = await axios.get(
'https://symbol-search.tradingview.com/symbol_search/v3',
{
params: {
exchange: (splittedSearch.length === 2
? splittedSearch[0]
: undefined
),
text: splittedSearch.pop(),
search_type: filter,
},
headers: {
origin: 'https://www.tradingview.com',
},
validateStatus,
},
);

const { data } = request;

return data.symbols.map((s) => {
const exchange = s.exchange.split(' ')[0];
const id = `${exchange.toUpperCase()}:${s.symbol}`;

return {
id,
exchange,
fullExchange: s.exchange,
screener,
symbol: s.symbol,
description: s.description,
type: s.type,
getTA: () => this.getTA(screener, id),
getTA: () => this.getTA(id),
};
});
},
Expand Down Expand Up @@ -182,16 +198,26 @@ module.exports = {
if (!builtInIndicList.length) {
await Promise.all(['standard', 'candlestick', 'fundamental'].map(async (type) => {
const { data } = await axios.get(
`https://pine-facade.tradingview.com/pine-facade/list/?filter=${type}`,
{ validateStatus },
'https://pine-facade.tradingview.com/pine-facade/list',
{
params: {
filter: type,
},
validateStatus,
},
);
builtInIndicList.push(...data);
}));
}

const { data } = await axios.get(
`https://www.tradingview.com/pubscripts-suggest-json/?search=${search.replace(/ /g, '%20')}`,
{ validateStatus },
'https://www.tradingview.com/pubscripts-suggest-json',
{
params: {
search: search.replace(/ /g, '%20'),
},
validateStatus,
},
);

function norm(str = '') {
Expand Down Expand Up @@ -253,10 +279,10 @@ module.exports = {
const { data } = await axios.get(
`https://pine-facade.tradingview.com/pine-facade/translate/${indicID}/${version}`,
{
validateStatus,
headers: {
cookie: `${session ? `sessionid=${session};` : ''}${signature ? `sessionid_sign=${signature};` : ''}`,
cookie: genAuthCookies(session, signature),
},
validateStatus,
},
);

Expand Down Expand Up @@ -356,12 +382,12 @@ module.exports = {
'https://www.tradingview.com/accounts/signin/',
`username=${username}&password=${password}${remember ? '&remember=on' : ''}`,
{
validateStatus,
headers: {
referer: 'https://www.tradingview.com',
'Content-Type': 'application/x-www-form-urlencoded',
'User-agent': `${UA} (${os.version()}; ${os.platform()}; ${os.arch()})`,
},
validateStatus,
},
);

Expand Down Expand Up @@ -403,10 +429,10 @@ module.exports = {
*/
async getUser(session, signature = '', location = 'https://www.tradingview.com/') {
const { data } = await axios.get(location, {
validateStatus,
headers: {
cookie: `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}`,
cookie: genAuthCookies(session, signature),
},
validateStatus,
});

if (data.includes('auth_token')) {
Expand Down Expand Up @@ -442,12 +468,18 @@ module.exports = {
* @returns {Promise<SearchIndicatorResult[]>} Search results
*/
async getPrivateIndicators(session, signature = '') {
const { data } = await axios.get('https://pine-facade.tradingview.com/pine-facade/list?filter=saved', {
validateStatus,
headers: {
cookie: `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}`,
const { data } = await axios.get(
'https://pine-facade.tradingview.com/pine-facade/list',
{
headers: {
cookie: genAuthCookies(session, signature),
},
params: {
filter: 'saved',
},
validateStatus,
},
});
);

return data.map((ind) => ({
id: ind.scriptIdPart,
Expand Down Expand Up @@ -495,14 +527,16 @@ module.exports = {
);

const { data } = await axios.get(
`https://www.tradingview.com/chart-token/?image_url=${layout}&user_id=${id}`,
'https://www.tradingview.com/chart-token',
{
validateStatus,
headers: {
cookie: session
? `sessionid=${session}${signature ? `;sessionid_sign=${signature};` : ''}`
: '',
cookie: genAuthCookies(session, signature),
},
params: {
image_url: layout,
user_id: id,
},
validateStatus,
},
);

Expand Down Expand Up @@ -548,14 +582,15 @@ module.exports = {
const { data } = await axios.get(
`https://charts-storage.tradingview.com/charts-storage/get/layout/${
layout
}/sources?chart_id=${
chartID
}&jwt=${
chartToken
}${
(symbol ? `&symbol=${symbol}` : '')
}`,
{ validateStatus },
}/sources`,
{
params: {
chart_id: chartID,
jwt: chartToken,
symbol,
},
validateStatus,
},
);

if (!data.payload) throw new Error('Wrong layout, user credentials, or chart id.');
Expand Down
6 changes: 6 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,10 @@ module.exports = {
for (let i = 0; i < 12; i += 1) r += c.charAt(Math.floor(Math.random() * c.length));
return `${type}_${r}`;
},

genAuthCookies(sessionId = '', signature = '') {
if (!sessionId) return '';
if (!signature) return `sessionid=${sessionId}`;
return `sessionid=${sessionId};sessionid_sign=${signature}`;
},
};
Loading

0 comments on commit e8a07f9

Please sign in to comment.