`;
function renderChart(chartData) {
if (cycleChart) cycleChart.destroy();
const chartDefaults = { scales: { x: { type: 'time', time: { unit: 'month' }, grid: { color: '#374151' } }, y: { grid: { color: '#374151' } } }, plugins: { legend: { labels: { color: '#d1d5db' } } } };
Chart.defaults.color = '#d1d5db';
cycleChart = new Chart(document.getElementById('cycle-chart'), {
type: 'line',
data: {
labels: state.historicalData.map(d => d.date),
datasets: [
{ label: 'Price', data: state.historicalData.map(d => d.price), borderColor: '#f3f4f6', borderWidth: 2, pointRadius: 0 },
{ label: `SMA(${state.settings.shortMa})`, data: chartData.shortMa, borderColor: '#60a5fa', borderWidth: 1.5, pointRadius: 0 },
{ label: `SMA(${state.settings.longMa})`, data: chartData.longMa, borderColor: '#facc15', borderWidth: 1.5, pointRadius: 0 }
]
},
options: { ...chartDefaults },
plugins: [{ id: 'background', beforeDraw: (chart) => {
const { ctx, chartArea, scales } = chart;
const phaseColors = { 'Accumulation': 'rgba(22, 163, 74, 0.1)', 'Markup': 'rgba(59, 130, 246, 0.1)', 'Distribution': 'rgba(202, 138, 4, 0.1)', 'Markdown': 'rgba(220, 38, 38, 0.1)', 'Undefined': 'rgba(107, 114, 128, 0.1)' };
const phases = state.analysisResult?.chartData?.phases;
if (!phases) return;
ctx.save();
for (let i = 0; i < chart.data.labels.length - 1; i++) {
if (!chart.data.labels[i+1]) continue;
const xStart = scales.x.getPixelForValue(new Date(chart.data.labels[i]).getTime());
const xEnd = scales.x.getPixelForValue(new Date(chart.data.labels[i+1]).getTime());
const phase = phases[i];
const bgColor = phaseColors[phase] || 'transparent';
if (bgColor !== 'transparent') {
ctx.fillStyle = bgColor;
ctx.fillRect(xStart, chartArea.top, xEnd - xStart, chartArea.height);
}
}
ctx.restore();
}}]
});
}
runAnalysisBtn.addEventListener('click', runAnalysis);
// --- PDF GENERATION ---
async function generatePdfReport() {
if (!state.analysisResult) return alert("Please run an analysis first.");
downloadPdfBtn.disabled = true;
downloadPdfBtn.textContent = 'Generating...';
const chartImg = cycleChart.toBase64Image();
const { kpis } = state.analysisResult;
const reportHtml = `
`;
const pdfTemplate = document.getElementById('pdf-template');
pdfTemplate.innerHTML = reportHtml;
pdfTemplate.classList.remove('invisible');
try {
const { jsPDF } = window.jspdf;
const canvas = await html2canvas(pdfTemplate.querySelector('.pdf-report-container'), { scale: 2 });
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF({ orientation: 'p', unit: 'pt', format: 'a4' });
pdf.addImage(imgData, 'PNG', 0, 0, pdf.internal.pageSize.getWidth(), (canvas.height * pdf.internal.pageSize.getWidth()) / canvas.width);
pdf.save(`${state.settings.ticker.replace(/\s+/g, '_')}_Cycle_Analysis.pdf`);
} catch(e) { console.error('PDF Generation Error:', e);
} finally {
downloadPdfBtn.disabled = false;
downloadPdfBtn.textContent = 'Download Analysis Report';
pdfTemplate.classList.add('invisible');
}
}
downloadPdfBtn.addEventListener('click', generatePdfReport);
// --- INITIALIZATION ---
generateSampleData();
populateSettingsUI();
renderDataTable();
attachDataListeners();
runAnalysis();
switchTab(0);
});
Market Cycle Analysis Report
${state.settings.ticker} | As of ${new Date().toLocaleDateString()}
Current Market Assessment
Current Identified Phase
${kpis.currentPhase}
Last Closing Price
$${kpis.price.toLocaleString()}
