Twitter Sentiment Analysis for Stocks

Twitter Sentiment Analysis for Stocks

Note: Tweet data and sentiment analysis are simulated by AI for illustrative purposes and do not represent real-time Twitter data or actual investment advice.

${escapeHtml(tweet.tweet_text)}

Sentiment: ${capitalizeFirstLetter(tweet.sentiment)}

`; tweetsListDiv.appendChild(tweetCard); }); } resultsSection.style.display = 'block'; } /** * Escapes HTML special characters to prevent XSS. * @param {string} unsafe - The string to escape. * @returns {string} The escaped string. */ function escapeHtml(unsafe) { if (typeof unsafe !== 'string') return ''; return unsafe .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } /** * Capitalizes the first letter of a string. * @param {string} string - The input string. * @returns {string} The string with the first letter capitalized. */ function capitalizeFirstLetter(string) { if (!string) return ''; return string.charAt(0).toUpperCase() + string.slice(1); } /** * Generates a PDF of the current analysis. */ function generatePdf() { if (!currentAnalysisData || !currentAnalysisData.tweets) { // In a real app, you might use a more user-friendly notification console.warn("No analysis data to generate PDF."); alert("Please perform an analysis first before downloading the PDF."); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF({ orientation: 'portrait', unit: 'pt', format: 'letter' }); let yPos = 40; const pageHeight = doc.internal.pageSize.getHeight(); const pageWidth = doc.internal.pageSize.getWidth(); const margin = 40; // Title doc.setFontSize(18); doc.text(`Twitter Sentiment Analysis: ${currentAnalysisData.ticker}`, pageWidth / 2, yPos, { align: 'center' }); yPos += 25; doc.setFontSize(10); doc.text(`Analysis Date: ${currentAnalysisData.date}`, pageWidth / 2, yPos, { align: 'center' }); yPos += 30; doc.setFontSize(10); doc.text("Disclaimer: Tweet data and sentiment analysis are simulated by AI for illustrative purposes.", margin, yPos); yPos += 15; doc.text("This does not represent real-time Twitter data or actual investment advice.", margin, yPos); yPos += 30; // Overall Sentiment & Distribution doc.setFontSize(14); doc.text("Sentiment Summary", margin, yPos); yPos += 20; doc.setFontSize(10); const sentiments = currentAnalysisData.tweets.map(t => t.sentiment); const positiveCount = sentiments.filter(s => s === 'positive').length; const negativeCount = sentiments.filter(s => s === 'negative').length; const neutralCount = sentiments.filter(s => s === 'neutral').length; const totalTweets = sentiments.length; let overall = 'Neutral'; if (totalTweets > 0) { if (positiveCount > negativeCount && positiveCount > neutralCount) overall = 'Positive'; else if (negativeCount > positiveCount && negativeCount > neutralCount) overall = 'Negative'; else if (positiveCount === negativeCount && positiveCount > neutralCount) overall = 'Mixed'; } const summaryBody = [ ["Overall Sentiment:", overall], ["Positive Tweets:", `${positiveCount} (${(totalTweets > 0 ? positiveCount/totalTweets*100 : 0).toFixed(1)}%)`], ["Negative Tweets:", `${negativeCount} (${(totalTweets > 0 ? negativeCount/totalTweets*100 : 0).toFixed(1)}%)`], ["Neutral Tweets:", `${neutralCount} (${(totalTweets > 0 ? neutralCount/totalTweets*100 : 0).toFixed(1)}%)`], ["Total Simulated Tweets Analyzed:", `${totalTweets}`] ]; doc.autoTable({ startY: yPos, head: [['Metric', 'Value']], body: summaryBody, theme: 'striped', headStyles: { fillColor: [59, 130, 246] }, // Tailwind blue-500 margin: { left: margin, right: margin } }); yPos = doc.lastAutoTable.finalY + 25; // Simulated Tweets Table doc.setFontSize(14); doc.text("Simulated Tweets & Sentiments", margin, yPos); yPos += 20; const tableHeaders = ["Simulated Tweet Text", "Sentiment"]; const tableBody = currentAnalysisData.tweets.map(tweet => [ tweet.tweet_text, capitalizeFirstLetter(tweet.sentiment) ]); doc.autoTable({ startY: yPos, head: [tableHeaders], body: tableBody, theme: 'grid', headStyles: { fillColor: [59, 130, 246], fontSize: 10 }, styles: { fontSize: 9, cellPadding: 3, overflow: 'linebreak' }, columnStyles: { 0: { cellWidth: pageWidth - (2 * margin) - 100 }, // Text column width 1: { cellWidth: 80, halign: 'center' } // Sentiment column width }, margin: { left: margin, right: margin } }); const safeFilename = (currentAnalysisData.ticker.replace(/[^a-z0-9]/gi, '_').toLowerCase() || 'stock') + '_sentiment_analysis.pdf'; doc.save(safeFilename); } });
Scroll to Top