Historical Stock Price Seasonality Analyzer

Historical Stock Price Seasonality Analyzer

Enter a stock ticker to analyze simulated historical monthly performance. (Examples: SIM1, SIM2)

Simulated historical data not available or insufficient for ticker: ${ticker}. Try SIM1 or SIM2.

`; resultsArea.style.display = 'block'; return; } // Calculate monthly percentage changes const monthlyChanges = Array(12).fill(null).map(() => []); // Array of 12 arrays (one for each month) // Group data by year and month for easier processing const yearlyData = {}; data.forEach(d => { if (!yearlyData[d.year]) yearlyData[d.year] = Array(12).fill(null); yearlyData[d.year][d.month - 1] = d.close; }); Object.keys(yearlyData).sort().forEach(year => { const yearPrices = yearlyData[year]; for (let i = 0; i < 12; i++) { // Iterate through months 0-11 let prevMonthClose; if (i === 0) { // January // Look for December of previous year const prevYear = parseInt(year) - 1; if (yearlyData[prevYear] && yearlyData[prevYear][11] !== null) { prevMonthClose = yearlyData[prevYear][11]; } } else { prevMonthClose = yearPrices[i-1]; } const currentMonthClose = yearPrices[i]; if (prevMonthClose !== null && currentMonthClose !== null && prevMonthClose > 0) { const change = ((currentMonthClose - prevMonthClose) / prevMonthClose) * 100; monthlyChanges[i].push(change); } } }); const averageMonthlyChanges = monthlyChanges.map(changes => { if (changes.length === 0) return 0; // Or null if prefer to skip months with no data return changes.reduce((sum, val) => sum + val, 0) / changes.length; }); // Find strongest and weakest months let maxChange = -Infinity, minChange = Infinity; let strongestMonthIdx = -1, weakestMonthIdx = -1; averageMonthlyChanges.forEach((change, idx) => { if (change > maxChange) { maxChange = change; strongestMonthIdx = idx; } if (change < minChange) { minChange = change; weakestMonthIdx = idx; } }); const summaryText = `Analysis for ${ticker}: Strongest performing month on average is ${monthNames[strongestMonthIdx]} (${maxChange.toFixed(2)}%). Weakest performing month on average is ${monthNames[weakestMonthIdx]} (${minChange.toFixed(2)}%).`; // Generate Chart HTML let maxAbsChangeForScale = 0; averageMonthlyChanges.forEach(change => { if (Math.abs(change) > maxAbsChangeForScale) { maxAbsChangeForScale = Math.abs(change); } }); // Add a small buffer to maxAbsChangeForScale to prevent bars from hitting the top/bottom exactly maxAbsChangeForScale = maxAbsChangeForScale > 0 ? maxAbsChangeForScale * 1.1 : 5; // Default to 5% if all changes are 0 const chartHtml = averageMonthlyChanges.map((change, idx) => { const isPositive = change >= 0; const barHeightPercentage = maxAbsChangeForScale > 0 ? (Math.abs(change) / maxAbsChangeForScale) * 50 : 0; // Max 50% of chart height for one direction let barStyle = `height: ${barHeightPercentage}%;`; if (isPositive) { barStyle += `bottom: 25px;`; // Starts from the zero line upwards } else { barStyle += `top: calc(50% + 12.5px); transform: scaleY(-1);`; // Starts from zero line downwards, flip } return `
${change.toFixed(1)}%
${monthNames[idx]}
`; }).join(''); // Generate Table HTML const tableHtml = averageMonthlyChanges.map((change, idx) => ` ${monthNames[idx]} ${change.toFixed(2)}% `).join(''); resultsArea.innerHTML = `

Historical Stock Price Seasonality Analysis

For Ticker: ${ticker}

Generated: ${new Date().toLocaleString()}

Seasonality Analysis for ${ticker}

${summaryText}

Average Monthly Performance (%)

${chartHtml}
${tableHtml}
Month Average % Change

Disclaimer: This analysis is based on simulated historical data and does not guarantee future performance. For illustrative purposes only.

`; resultsArea.style.display = 'block'; resultsArea.scrollIntoView({ behavior: 'smooth' }); } async function generateSeasonalityPdf() { const pdfContent = document.getElementById('pdf-content'); const downloadBtn = event.target; if (!pdfContent) { console.error("PDF content area not found."); if(typeof alert === 'function') alert("Error: Could not find content to generate PDF."); return; } if (!html2canvas || !jsPDF) { console.error("html2canvas or jsPDF library not found."); if(typeof alert === 'function') alert("Error: PDF generation library not loaded."); return; } const originalButtonText = downloadBtn.textContent; downloadBtn.textContent = "Generating..."; downloadBtn.disabled = true; const pdfHeaderElement = pdfContent.querySelector('.pdf-header'); const screenHeaderElement = pdfContent.querySelector('.screen-only'); if (pdfHeaderElement) pdfHeaderElement.style.display = 'block'; if (screenHeaderElement) screenHeaderElement.style.display = 'none'; try { const canvas = await html2canvas(pdfContent, { scale: 1.5, // Adjusted for file size useCORS: true, logging: false, onclone: (doc) => { const clonedHeader = doc.querySelector('.pdf-header'); if (clonedHeader) clonedHeader.style.display = 'block'; const clonedScreenHeader = doc.querySelector('.screen-only'); if (clonedScreenHeader) clonedScreenHeader.style.display = 'none'; } }); if (pdfHeaderElement) pdfHeaderElement.style.display = 'none'; if (screenHeaderElement) screenHeaderElement.style.display = 'block'; const imgData = canvas.toDataURL('image/jpeg', 0.8); // Use JPEG for smaller size const pdf = new jsPDF({ orientation: 'p', unit: 'mm', format: 'a4' }); const pdfWidth = pdf.internal.pageSize.getWidth(); const pdfHeight = pdf.internal.pageSize.getHeight(); const imgProps = pdf.getImageProperties(imgData); const margin = 10; const contentWidth = pdfWidth - 2 * margin; const contentHeight = (imgProps.height * contentWidth) / imgProps.width; if (contentHeight <= pdfHeight - 2 * margin) { pdf.addImage(imgData, 'JPEG', margin, margin, contentWidth, contentHeight); } else { let remainingImgHeight = imgProps.height; let sourceY = 0; const sourceWidth = imgProps.width; const pageCanvasHeight = ((pdfHeight - 2 * margin) / contentWidth) * sourceWidth; while (remainingImgHeight > 0) { const currentChunkHeight = Math.min(remainingImgHeight, pageCanvasHeight); const tempCanvas = document.createElement('canvas'); tempCanvas.width = sourceWidth; tempCanvas.height = currentChunkHeight; const tempCtx = tempCanvas.getContext('2d'); tempCtx.drawImage(canvas, 0, sourceY, sourceWidth, currentChunkHeight, 0, 0, sourceWidth, currentChunkHeight); const chunkImgData = tempCanvas.toDataURL('image/jpeg', 0.8); pdf.addImage(chunkImgData, 'JPEG', margin, margin, contentWidth, (currentChunkHeight * contentWidth) / sourceWidth); remainingImgHeight -= currentChunkHeight; sourceY += currentChunkHeight; if (remainingImgHeight > 0) { pdf.addPage(); } } } const tickerSymbol = document.getElementById('tickerInput').value.toUpperCase() || "REPORT"; pdf.save(`${tickerSymbol}_Seasonality_Analysis.pdf`); } catch (error) { console.error("Error generating PDF:", error); if(typeof alert === 'function') alert("An error occurred while generating the PDF: " + error.message); } finally { downloadBtn.textContent = originalButtonText; downloadBtn.disabled = false; if (pdfHeaderElement) pdfHeaderElement.style.display = 'none'; if (screenHeaderElement) screenHeaderElement.style.display = 'block'; } }
Scroll to Top