Black-Litterman Asset Allocation Model

Asset 1: Stocks

Asset 2: Bonds

Asset 3: REITs

View 1 (Optional)

View 2 (Optional)

Enter inputs to see results.

Portfolio: ${data.portfolioName} | Value: $${data.portfolioValue.toFixed(2)}

Risk-Free Rate: ${data.riskFreeRate.toFixed(2)}% | Risk Aversion: ${data.riskAversion.toFixed(2)}

Portfolio Return: ${data.results.portReturn}% | Portfolio Volatility: ${data.results.portVolatility}%

Sharpe Ratio: ${data.results.sharpeRatio}

Asset Allocations

${tableData.map(row => ` `).join('')}
Asset Benchmark Weight (%) Optimal Weight (%) Equilibrium Return (%) Adjusted Return (%)
${row.asset} ${row.benchmarkWeight} ${row.optimalWeight} ${row.equilibriumReturn} ${row.adjustedReturn}

Views Applied:

${data.views.map((v, i) => v.type !== 'none' && v.confidence > 0 ? `View ${i + 1}: ${v.type === 'absolute' ? `${data.assets[v.assetIdx - 1]} returns ${(v.return * 100).toFixed(2)}%` : `${data.assets[v.assetIdx - 1]} outperforms ${data.assets[v.asset2Idx - 1]} by ${(v.return * 100).toFixed(2)}%`} (Confidence: ${v.confidence.toFixed(2)}%)` : '').filter(v => v).join('; ') || 'None'}

Recommendations:

${data.recommendations}

`; resultsTable.innerHTML = tableHtml; } // Sort Table window.sortTable = function (n) { const table = document.querySelector('.results-table'); if (!table) return; let rows, switching = true, i, x, y, shouldSwitch, dir = 'asc', switchcount = 0; while (switching) { switching = false; rows = table.rows; for (i = 1; i < (rows.length - 1); i++) { shouldSwitch = false; x = rows[i].getElementsByTagName('TD')[n]; y = rows[i + 1].getElementsByTagName('TD')[n]; let xVal = x.innerHTML.toLowerCase(); let yVal = y.innerHTML.toLowerCase(); if (n !== 0) { xVal = parseFloat(xVal) || 0; yVal = parseFloat(yVal) || 0; } if (dir === 'asc') { if (xVal > yVal) { shouldSwitch = true; break; } } else if (dir === 'desc') { if (xVal < yVal) { shouldSwitch = true; break; } } } if (shouldSwitch) { rows[i].parentNode.insertBefore(rows[i + 1], rows[i]); switching = true; switchcount++; } else if (switchcount === 0 && dir === 'asc') { dir = 'desc'; switching = true; } } }; // Tab Navigation window.openTab = function (tabId) { try { const tabs = document.querySelectorAll('.tab-content'); const tabLinks = document.querySelectorAll('.tab-link'); tabs.forEach(tab => tab.classList.remove('active')); tabLinks.forEach(link => link.classList.remove('active')); const activeTab = document.getElementById(tabId); if (activeTab) { activeTab.classList.add('active'); } else { console.error(`Tab with ID ${tabId} not found.`); } const activeLink = document.querySelector(`.tab-link[onclick="openTab('${tabId}')"]`); if (activeLink) { activeLink.classList.add('active'); } else { console.error(`Tab link for ${tabId} not found.`); } } catch (err) { console.error('Tab navigation failed:', err.message); } }; // Download PDF window.downloadPDF = function () { const resultsTable = document.getElementById('results-table'); const pdfContent = resultsTable?.querySelector('.pdf-content'); if (!resultsTable || !pdfContent || !pdfContent.querySelector('.results-table')) { alert('No results available to download. Please calculate results first.'); return; } if (typeof pdfMake === 'undefined' || typeof pdfMake.vfs === 'undefined') { alert('PDF generation library failed to load. Please check your internet connection and try again.'); console.error('pdfMake or vfs is not defined.'); return; } try { const rows = []; const headers = ['Asset', 'Benchmark Weight (%)', 'Optimal Weight (%)', 'Equilibrium Return (%)', 'Adjusted Return (%)']; rows.push(headers.map(header => ({ text: header, style: 'tableHeader' }))); const tableRows = pdfContent.querySelectorAll('.results-table tbody tr'); if (tableRows.length === 0) { throw new Error('No table rows found in results.'); } tableRows.forEach(row => { const cells = row.querySelectorAll('td'); const rowData = Array.from(cells).map(cell => ({ text: cell.textContent })); rows.push(rowData); }); const summaryData = Array.from(pdfContent.querySelectorAll('p:not(.summary p)')).map(p => p.textContent); const recommendations = Array.from(pdfContent.querySelectorAll('.summary p')).map(p => p.textContent).join('\n') || 'N/A'; const docDefinition = { content: [ { text: 'Black-Litterman Asset Allocation Report', style: 'header' }, { text: '\n' }, { text: summaryData.join('\n'), style: 'body' }, { text: '\n' }, { text: 'Asset Allocations', style: 'subheader' }, { table: { headerRows: 1, widths: ['auto', 'auto', 'auto', 'auto', 'auto'], body: rows }, layout: { fillColor: function (rowIndex) { return (rowIndex % 2 === 0 && rowIndex > 0) ? '#f2f2f2' : null; }, hLineColor: '#ddd', vLineColor: '#ddd' } }, { text: '\n' }, { text: 'Recommendations', style: 'subheader' }, { text: recommendations, style: 'body' } ], styles: { header: { fontSize: 18, bold: true, alignment: 'center', color: '#333' }, subheader: { fontSize: 14, bold: true, color: '#333' }, tableHeader: { bold: true, fontSize: 12, color: 'white', fillColor: '#007BFF' }, body: { fontSize: 10, color: '#333' } }, defaultStyle: { fontSize: 10, color: '#333' }, pageMargins: [40, 60, 40, 60] }; pdfMake.createPdf(docDefinition).download('BlackLitterman_Report.pdf'); } catch (err) { console.error('PDF generation failed:', err.message, err.stack); alert('Failed to generate PDF: ' + err.message); } }; });
Scroll to Top