Investment Risk Analytics System

Investment Risk Analytics System

Evaluate portfolio risk and return with key metrics and visualizations.

Portfolio Composition

Asset Volatility (Risk)

The portfolio's expected annual return is ${formatPercent(metrics.expectedReturn)}, with a calculated volatility (risk) of ${formatPercent(metrics.volatility)}. This results in a Sharpe Ratio of ${formatNumber(metrics.sharpeRatio)}, which measures risk-adjusted return. A higher Sharpe Ratio (typically > 1.0) is considered good.

Key Observations

  • Highest Return Asset: ${highReturnAsset.name} (${formatPercent(highReturnAsset.expectedReturn)})
  • Highest Risk Asset: ${highRiskAsset.name} (${formatPercent(highRiskAsset.volatility)})
  • The overall portfolio risk (${formatPercent(metrics.volatility)}) is lower than that of its riskiest component due to diversification.
`; }; // CALCULATION LOGIC const calculateMetrics = () => { if (portfolioData.length === 0) return null; const totalValue = portfolioData.reduce((sum, a) => sum + a.value, 0); if (totalValue === 0) return null; const assets = portfolioData.map(a => ({...a, weight: a.value / totalValue })); const expectedReturn = assets.reduce((sum, a) => sum + a.weight * a.expectedReturn, 0); // Portfolio Volatility Calculation let variance = 0; for (let i = 0; i < assets.length; i++) { // Add variance part variance += Math.pow(assets[i].weight, 2) * Math.pow(assets[i].volatility / 100, 2); for (let j = 0; j < assets.length; j++) { if (i !== j) { // Add covariance part variance += assets[i].weight * assets[j].weight * (assets[i].volatility / 100) * (assets[j].volatility / 100) * ASSET_CORRELATION; } } } const volatility = Math.sqrt(variance) * 100; const sharpeRatio = (expectedReturn - RISK_FREE_RATE) / volatility; return { totalValue, expectedReturn, volatility, sharpeRatio, assets }; }; // EVENT HANDLERS const handleRecalculate = () => { const metrics = calculateMetrics(); if(metrics) { renderKPIs(metrics); renderCharts(metrics); renderAnalysis(metrics); switchTab('dashboard'); } else { alert("No data available to calculate. Please add asset data with a total value greater than zero."); } }; const handleAddRow = () => { const newId = portfolioData.length > 0 ? Math.max(...portfolioData.map(p => p.id)) + 1 : 1; portfolioData.push({ id: newId, name: 'New Asset', value: 1000, expectedReturn: 5.0, volatility: 10.0 }); renderDataTable(); }; dataTableBody.addEventListener('change', (e) => { if(e.target.matches('.data-input')) { const id = parseInt(e.target.dataset.id); const key = e.target.dataset.key; const value = e.target.type === 'number' ? parseFloat(e.target.value) : e.target.value; const assetIndex = portfolioData.findIndex(p => p.id === id); if (assetIndex !== -1) { portfolioData[assetIndex][key] = value; } } }); dataTableBody.addEventListener('click', (e) => { if (e.target.matches('button')) { const id = parseInt(e.target.dataset.id); portfolioData = portfolioData.filter(p => p.id !== id); renderDataTable(); } }); const generatePDF = () => { const metrics = calculateMetrics(); if (!metrics) { alert("No data to generate PDF."); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF(); const pageWidth = doc.internal.pageSize.width; const margin = 15; let y = margin; doc.setFont('helvetica', 'bold'); doc.setFontSize(22); doc.text('Investment Risk Analytics Report', pageWidth / 2, y, { align: 'center' }); y += 10; doc.setFontSize(12); doc.setFont('helvetica', 'normal'); doc.text(`Report Generated: ${new Date('2025-09-19T14:40:00Z').toLocaleDateString('en-US')}`, pageWidth / 2, y, { align: 'center' }); y += 15; doc.setFontSize(16); doc.setFont('helvetica', 'bold'); doc.text('Portfolio Summary', margin, y); y += 8; const kpiText = [ `Total Portfolio Value: ${formatCurrency(metrics.totalValue)}`, `Expected Return: ${formatPercent(metrics.expectedReturn)}`, `Portfolio Volatility (Risk): ${formatPercent(metrics.volatility)}`, `Sharpe Ratio (Risk-Adjusted Return): ${formatNumber(metrics.sharpeRatio)}` ]; doc.setFontSize(12); doc.setFont('helvetica', 'normal'); kpiText.forEach(text => { doc.text(text, margin, y); y+= 7; }); y += 10; doc.setFontSize(16); doc.setFont('helvetica', 'bold'); doc.text('Portfolio Visualizations', margin, y); y+= 5; try { const compositionImg = charts.composition.toBase64Image(); const volatilityImg = charts.volatility.toBase64Image(); doc.addImage(compositionImg, 'PNG', margin, y, 80, 80); doc.addImage(volatilityImg, 'PNG', margin + 90, y, 95, 80); } catch(e) { console.error("Error adding charts to PDF:", e); doc.text("Could not render charts.", margin, y); } doc.save('Investment_Risk_Report.pdf'); }; // APP NAVIGATION const switchTab = (tabName) => { if (!tabs.includes(tabName)) return; currentTab = tabName; Object.values(tabContent).forEach(content => content.classList.add('hidden')); tabContent[tabName].classList.remove('hidden'); Object.values(tabButtons).forEach(button => button.classList.remove('active')); tabButtons[tabName].classList.add('active'); updateNavButtons(); }; const updateNavButtons = () => { const currentIndex = tabs.indexOf(currentTab); prevBtn.disabled = currentIndex === 0; nextBtn.disabled = currentIndex === tabs.length - 1; }; const navigateTabs = (direction) => { const currentIndex = tabs.indexOf(currentTab); let newIndex = direction === 'next' ? Math.min(currentIndex + 1, tabs.length - 1) : Math.max(currentIndex - 1, 0); switchTab(tabs[newIndex]); }; // INITIALIZATION document.getElementById('recalculate-btn').addEventListener('click', handleRecalculate); document.getElementById('add-row-btn').addEventListener('click', handleAddRow); document.getElementById('download-pdf-btn-dashboard').addEventListener('click', generatePDF); window.app = { switchTab, navigateTabs, }; renderDataTable(); handleRecalculate(); updateNavButtons(); });
Scroll to Top