trading-pulse / index.js
datamk's picture
Upload 16 files
019bfc2 verified
const express = require('express');
const axios = require('axios');
const Parser = require('rss-parser');
const Sentiment = require('sentiment');
const cron = require('node-cron');
const cors = require('cors');
const fs = require('fs');
const path = require('path');
const compression = require('compression');
const app = express();
const parser = new Parser();
const sentiment = new Sentiment();
const PORT = 3001;
app.use(compression());
app.use(cors());
app.use(express.static('public', { maxAge: '1d' }));
const DATA_FILE = path.join(__dirname, 'data', 'news_sentiment.json');
const TREND_FILE = path.join(__dirname, 'data', 'sentiment_trends.json');
const RSS_FEEDS = [
'https://news.google.com/rss/search?q=when:48h+stock+market+india+NSE+BSE+finance&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/headlines/section/topic/BUSINESS?hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+stock+market+india+business&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:moneycontrol.com+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:financialexpress.com+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:business-standard.com+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://economictimes.indiatimes.com/markets/stocks/rssfeeds/2146842.cms',
'https://www.livemint.com/rss/markets',
'https://news.google.com/rss/search?q=when:48h+site:thehindubusinessline.com+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:pulse.zerodha.com&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:in.investing.com+intitle:(stocks+OR+markets+OR+india)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:web.stockedge.com&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:groww.in&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:ndtvprofit.com+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:cnbctv18.com+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:zeebiz.com+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:goodreturns.in+intitle:(stocks+OR+markets+OR+shares)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:ticker.finology.in&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:moneyworks4me.com&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:trendlyne.com&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:tickertape.in&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:equitymaster.com+intitle:(stocks+OR+markets+OR+shares)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:marketsmojo.com&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:fortuneindia.com+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:businessworld.in+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:outlookbusiness.com+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:money.rediff.com+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:informistmedia.com+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:reuters.com+intitle:(india+AND+(stocks+OR+markets+OR+nse+OR+bse))&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:thehindu.com+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:timesofindia.indiatimes.com+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:deccanherald.com+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:bqprime.com+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:forbesindia.com+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:bloomberg.com+intitle:(india+AND+(stocks+OR+markets+OR+nse+OR+bse))&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:dsij.in+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:economictimes.indiatimes.com/et-now+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:firstpost.com+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:capitalmarket.com+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:news18.com+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:news.abplive.com+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:mydigitalfc.com+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:asia.nikkei.com+intitle:(india+AND+(stocks+OR+markets+OR+nse+OR+bse))&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:icicidirect.com+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:hdfcsec.com+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:smallcase.com+intitle:(stocks+OR+markets+OR+investing)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:upstox.com+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:angelone.in+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:motilaloswal.com+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:finshots.in+intitle:(stocks+OR+markets+OR+shares)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:tradebrains.in+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:valueresearchonline.com+intitle:(stocks+OR+markets+OR+shares)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:entrackr.com+intitle:(stocks+OR+markets+OR+ipo+OR+listed)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:inc42.com+intitle:(stocks+OR+markets+OR+ipo+OR+listed)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:ft.com+intitle:(india+AND+(stocks+OR+markets+OR+nse+OR+bse))&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:economist.com+intitle:(india+AND+(business+OR+markets+OR+economy))&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:wsj.com+intitle:(india+AND+(stocks+OR+markets+OR+corporate))&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:thestatesman.com+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:dailypioneer.com+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:freepressjournal.in+intitle:(stocks+OR+markets+OR+nse+OR+bse)&hl=en-IN&gl=IN&ceid=IN:en',
'https://www.businesstoday.in/rss/markets',
'https://news.google.com/rss/search?q=when:48h+site:screener.in&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:capitalmind.in&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:thecore.in&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:bseindia.com+corporate+announcements&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:nseindia.com+corporate+announcements&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+site:alphastreet.com+india&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+intitle:(dividend+OR+buyback+OR+"bonus+issue"+OR+"stock+split")+NSE+BSE&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+intitle:("quarterly+results"+OR+"earnings"+OR+"PAT+growth")+NSE+BSE&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+intitle:("FII+buying"+OR+"DII+selling"+OR+"bulk+deal"+OR+"block+deal")+NSE+BSE&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+intitle:("SEBI"+OR+"RBI+monetary+policy"+OR+"repo+rate")+India&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+intitle:("52-week+high"+OR+"breakout"+OR+"upper+circuit")+NSE+BSE&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+intitle:("IPO+allotment"+OR+"IPO+listing"+OR+"Grey+Market+Premium")+India&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+intitle:("Auto+sales"+OR+"USFDA")+India&hl=en-IN&gl=IN&ceid=IN:en',
'https://news.google.com/rss/search?q=when:48h+intitle:("Management+change"+OR+"CEO+resignation"+OR+"CFO+appointment")+India&hl=en-IN&gl=IN&ceid=IN:en'
];
const BROWSER_HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
'Accept': 'application/rss+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.9'
};
let cachedNews = [];
let feedStatus = {};
let sentimentTrend = [];
let cachedIndices = {};
let isFetching = false;
function getSourceName(url, feedTitle) {
if (url.includes('NSE+BSE+finance')) return 'Google Finance (Markets)';
if (url.includes('site:moneycontrol.com')) return 'Moneycontrol';
if (url.includes('site:financialexpress.com')) return 'Financial Express';
if (url.includes('site:business-standard.com')) return 'Business Standard';
if (url.includes('economictimes')) return 'Economic Times';
if (url.includes('livemint')) return 'LiveMint';
if (url.includes('headlines/section/topic/BUSINESS')) return 'Business News';
if (url.includes('site:thehindubusinessline.com')) return 'The Hindu Business Line';
if (url.includes('site:pulse.zerodha.com')) return 'Zerodha Pulse';
if (url.includes('site:in.investing.com')) return 'Investing.com';
if (url.includes('site:web.stockedge.com')) return 'StockEdge';
if (url.includes('site:groww.in')) return 'Groww';
if (url.includes('site:ndtvprofit.com')) return 'NDTV Profit';
if (url.includes('site:cnbctv18.com')) return 'CNBC TV18';
if (url.includes('site:zeebiz.com')) return 'Zee Business';
if (url.includes('site:goodreturns.in')) return 'GoodReturns';
if (url.includes('site:ticker.finology.in')) return 'Finology Ticker';
if (url.includes('site:moneyworks4me.com')) return 'MoneyWorks4Me';
if (url.includes('site:trendlyne.com')) return 'Trendlyne';
if (url.includes('site:tickertape.in')) return 'Tickertape';
if (url.includes('site:equitymaster.com')) return 'Equitymaster';
if (url.includes('site:marketsmojo.com')) return 'MarketsMojo';
if (url.includes('site:fortuneindia.com')) return 'Fortune India';
if (url.includes('site:businessworld.in')) return 'BusinessWorld';
if (url.includes('site:outlookbusiness.com')) return 'Outlook Business';
if (url.includes('site:money.rediff.com')) return 'Rediff Money';
if (url.includes('site:informistmedia.com')) return 'Informist Media';
if (url.includes('site:reuters.com')) return 'Reuters India';
if (url.includes('site:thehindu.com')) return 'The Hindu';
if (url.includes('site:timesofindia.indiatimes.com')) return 'Times of India';
if (url.includes('site:deccanherald.com')) return 'Deccan Herald';
if (url.includes('site:bqprime.com')) return 'BQ Prime';
if (url.includes('site:forbesindia.com')) return 'Forbes India';
if (url.includes('site:bloomberg.com')) return 'Bloomberg India';
if (url.includes('site:dsij.in')) return 'DSIJ';
if (url.includes('site:economictimes.indiatimes.com/et-now')) return 'ET Now';
if (url.includes('site:firstpost.com')) return 'Firstpost Business';
if (url.includes('site:capitalmarket.com')) return 'Capital Market';
if (url.includes('site:news18.com')) return 'News18 Business';
if (url.includes('site:news.abplive.com')) return 'ABP Business';
if (url.includes('site:mydigitalfc.com')) return 'Financial Chronicle';
if (url.includes('site:asia.nikkei.com')) return 'Nikkei Asia (India)';
if (url.includes('site:icicidirect.com')) return 'ICICIDirect Research';
if (url.includes('site:hdfcsec.com')) return 'HDFC Securities';
if (url.includes('site:smallcase.com')) return 'Smallcase Insights';
if (url.includes('site:upstox.com')) return 'Upstox Updates';
if (url.includes('site:angelone.in')) return 'Angel One';
if (url.includes('businesstoday')) return 'Business Today';
if (url.includes('site:screener.in')) return 'Screener.in';
if (url.includes('site:capitalmind.in')) return 'CapitalMind';
if (url.includes('site:thecore.in')) return 'The Core';
if (url.includes('site:bseindia.com')) return 'BSE Announcements';
if (url.includes('site:nseindia.com')) return 'NSE Announcements';
if (url.includes('site:alphastreet.com')) return 'AlphaStreet India';
if (url.includes('intitle:(dividend')) return 'Corporate Action (Dividend/Buyback)';
if (url.includes('intitle:("quarterly+results"')) return 'Earnings Alert';
if (url.includes('intitle:("FII+buying"')) return 'Institutional Flow Alert';
if (url.includes('intitle:("SEBI"')) return 'Regulatory/Policy Alert';
if (url.includes('intitle:("52-week+high"')) return 'Momentum/Breakout Alert';
if (url.includes('intitle:("IPO+allotment"')) return 'IPO/Listing Alert';
if (url.includes('intitle:("Auto+sales"')) return 'Sector Update';
if (url.includes('intitle:("Management+change"')) return 'Management Update';
if (url.includes('site:motilaloswal.com')) return 'Motilal Oswal';
if (url.includes('site:finshots.in')) return 'Finshots';
if (url.includes('site:tradebrains.in')) return 'Trade Brains';
if (url.includes('site:valueresearchonline.com')) return 'Value Research';
if (url.includes('site:entrackr.com')) return 'Entrackr (Tech Stocks)';
if (url.includes('site:inc42.com')) return 'Inc42 (Tech Stocks)';
if (url.includes('site:ft.com')) return 'Financial Times (India)';
if (url.includes('site:economist.com')) return 'The Economist (India)';
if (url.includes('site:wsj.com')) return 'WSJ (India)';
if (url.includes('site:thestatesman.com')) return 'The Statesman';
if (url.includes('site:dailypioneer.com')) return 'The Pioneer';
if (url.includes('site:freepressjournal.in')) return 'Free Press Journal';
return feedTitle?.replace('Google News - ', '') || 'Market News';
}
async function fetchIndices() {
const tickers = {
'NIFTY 50': '^NSEI',
'BANK NIFTY': '^NSEBANK',
'SENSEX': '^BSESN',
'FINNIFTY': 'NIFTY_FIN_SERVICE.NS',
'INDIA VIX': '^INDIAVIX',
'USD/INR': 'INR=X'
};
const tickerEntries = Object.entries(tickers);
// Fetch in parallel but keep the order
const responses = await Promise.allSettled(tickerEntries.map(async ([name, ticker]) => {
const response = await axios.get(`https://query1.finance.yahoo.com/v8/finance/chart/${ticker}?interval=1m&range=1d`, {
headers: { 'User-Agent': BROWSER_HEADERS['User-Agent'] },
timeout: 5000
});
const meta = response.data.chart.result[0].meta;
return {
price: meta.regularMarketPrice.toFixed(2),
change: (meta.regularMarketPrice - meta.previousClose).toFixed(2),
changePercent: (((meta.regularMarketPrice - meta.previousClose) / meta.previousClose) * 100).toFixed(2)
};
}));
const results = {};
tickerEntries.forEach(([name], index) => {
const res = responses[index];
if (res.status === 'fulfilled') {
results[name] = res.value;
} else {
results[name] = cachedIndices[name] || { price: '---', change: '0', changePercent: '0' };
}
});
cachedIndices = results;
}
async function fetchAndAnalyzeNews() {
if (isFetching) return;
isFetching = true;
const allEntries = [];
const now = new Date();
const fortyEightHoursAgo = new Date(now.getTime() - 48 * 60 * 60 * 1000);
const fetchPromises = RSS_FEEDS.map(async (url) => {
try {
const response = await axios.get(url, { headers: BROWSER_HEADERS, timeout: 10000 });
const feed = await parser.parseString(response.data);
const sourceName = getSourceName(url, feed.title);
feedStatus[url] = { status: 'Success', displayName: sourceName, lastUpdate: new Date().toISOString() };
const items = [];
feed.items.forEach(item => {
const pubDate = new Date(item.pubDate);
if (pubDate >= fortyEightHoursAgo) {
const fullText = `${item.title} ${item.contentSnippet || ''}`;
const analysis = sentiment.analyze(fullText);
items.push({
id: item.guid || item.link,
title: item.title,
link: item.link,
pubDate: item.pubDate,
source: sourceName,
sentiment: {
score: analysis.score,
label: analysis.score > 0 ? 'Positive' : (analysis.score < 0 ? 'Negative' : 'Neutral')
}
});
}
});
return items;
} catch (e) {
feedStatus[url] = { status: 'Failed', displayName: url.split('/')[2], lastUpdate: new Date().toISOString() };
return [];
}
});
const results = await Promise.allSettled(fetchPromises);
let originalCount = 0;
let filteredCount = 0;
results.forEach(result => {
if (result.status === 'fulfilled') {
originalCount += result.value.length;
const filteredItems = result.value.filter(item => {
const words = item.title.trim().split(/\s+/).filter(w => /[a-zA-Z0-9]/.test(w));
return words.length >= 5;
});
filteredCount += filteredItems.length;
allEntries.push(...filteredItems);
}
});
console.log(`News sync: ${originalCount} total items found, ${filteredCount} passed 5-word filter.`);
const uniqueNews = Array.from(new Map(allEntries.map(item => [item.link, item])).values());
uniqueNews.sort((a, b) => new Date(b.pubDate) - new Date(a.pubDate));
cachedNews = uniqueNews;
if (cachedNews.length > 0) {
const avgSentiment = cachedNews.reduce((acc, curr) => acc + curr.sentiment.score, 0) / cachedNews.length;
sentimentTrend.push({ time: new Date().toISOString(), score: avgSentiment });
if (sentimentTrend.length > 50) sentimentTrend.shift();
}
if (!fs.existsSync(path.join(__dirname, 'data'))) fs.mkdirSync(path.join(__dirname, 'data'));
try {
await Promise.all([
fs.promises.writeFile(DATA_FILE, JSON.stringify(cachedNews, null, 2)),
fs.promises.writeFile(TREND_FILE, JSON.stringify(sentimentTrend, null, 2))
]);
} catch (err) {
console.error('Error saving data files:', err);
}
isFetching = false;
}
app.get('/api/news', (req, res) => res.json({ news: cachedNews, status: feedStatus, trend: sentimentTrend, indices: cachedIndices }));
app.get('/api/indices', (req, res) => res.json({ indices: cachedIndices }));
cron.schedule('*/5 * * * *', () => fetchAndAnalyzeNews());
setInterval(fetchIndices, 10000);
if (fs.existsSync(DATA_FILE)) { try { cachedNews = JSON.parse(fs.readFileSync(DATA_FILE)); } catch (e) { } }
if (fs.existsSync(TREND_FILE)) { try { sentimentTrend = JSON.parse(fs.readFileSync(TREND_FILE)); } catch (e) { } }
fetchIndices();
fetchAndAnalyzeNews();
app.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}`));