Alternative Risk Premia (ARP) Strategy Evaluator

Global Parameter

Define Individual ARP Strategies

ARP Portfolio Evaluation

Total Weight: 0%

Portfolio Evaluation Metrics:

Define strategies and allocate weights to see portfolio metrics.

Important Note on Portfolio Volatility & Sharpe Ratio: This tool calculates the weighted average expected return. True portfolio volatility and Sharpe Ratio depend heavily on the correlations between the individual ARP strategies. These correlations are not factored into this simplified evaluator. Therefore, the actual portfolio volatility could be significantly different from a simple average of individual volatilities, and a precise portfolio Sharpe Ratio is not provided. Diversification benefits arise when strategies are not perfectly correlated.

Define strategies and allocate weights, then click "Evaluate Portfolio".

'; document.getElementById('arpDownloadPdfButton').style.display = 'none'; } } tabs.forEach(tab => tab.addEventListener('click', () => switchTab(tab.dataset.tab))); document.getElementById('arpGoToEvaluateTab')?.addEventListener('click', () => { if (validateStrategyInputs()) switchTab('evaluateTab'); }); document.getElementById('arpGoToDefineTab')?.addEventListener('click', () => switchTab('defineTab')); // --- Global State --- let arpStrategies = []; // { id, name, expReturn, volatility, weight (added in tab 2) } let riskFreeRate = 0.02; // Default 2% // --- Tab 1: Define ARP Strategies --- const riskFreeRateInput = document.getElementById('riskFreeRate'); const numArpStrategiesInput = document.getElementById('numArpStrategies'); const generateArpInputsButton = document.getElementById('generateArpInputs'); const arpStrategyDefinitionArea = document.getElementById('arpStrategyDefinitionArea'); riskFreeRateInput?.addEventListener('change', () => { riskFreeRate = parseFloat(riskFreeRateInput.value) / 100 || 0; }); generateArpInputsButton?.addEventListener('click', () => { const count = parseInt(numArpStrategiesInput.value); arpStrategies = []; arpStrategyDefinitionArea.innerHTML = ''; if (count >= 2 && count <= 5) { for (let i = 0; i < count; i++) { const strategyId = `arp_${i}`; // Add to global state with default values arpStrategies.push({ id: strategyId, name: `Strategy ${i+1}`, expReturn: 5.0, // Default 5% volatility: 10.0, // Default 10% weight: 0 // Will be set in tab 2 }); const strategyRow = document.createElement('div'); strategyRow.className = 'arp-strategy-row'; strategyRow.innerHTML = `
`; arpStrategyDefinitionArea.appendChild(strategyRow); } attachStrategyInputListeners(); } else { alert('Number of ARP strategies must be between 2 and 5.'); } }); function attachStrategyInputListeners() { document.querySelectorAll('.arp-strategy-name, .arp-strategy-expReturn, .arp-strategy-volatility').forEach(input => { input.addEventListener('change', (e) => { const strategyId = e.target.dataset.id; const strategy = arpStrategies.find(s => s.id === strategyId); if (strategy) { if (e.target.classList.contains('arp-strategy-name')) strategy.name = e.target.value.trim(); if (e.target.classList.contains('arp-strategy-expReturn')) strategy.expReturn = parseFloat(e.target.value) || 0; if (e.target.classList.contains('arp-strategy-volatility')) strategy.volatility = parseFloat(e.target.value) || 0.01; // Min volatility 0.01% } }); }); } function validateStrategyInputs() { for (const strategy of arpStrategies) { if (!strategy.name || strategy.name.trim() === "") { alert("Please provide a name for all ARP strategies."); return false; } if (isNaN(strategy.expReturn)) { alert(`Please provide a valid Expected Return for ${strategy.name}.`); return false; } if (isNaN(strategy.volatility) || strategy.volatility <= 0) { alert(`Please provide a valid positive Volatility for ${strategy.name}.`); return false; } } return true; } // --- Tab 2: Allocate & Evaluate Portfolio --- const allocationTableContainer = document.getElementById('arpAllocationTableContainer'); const totalWeightDisplayEl = document.getElementById('arpTotalWeightDisplay'); const portfolioMetricsEl = document.getElementById('arpPortfolioMetrics'); const evaluatePortfolioButton = document.getElementById('arpEvaluatePortfolioButton'); const downloadPdfButtonArp = document.getElementById('arpDownloadPdfButton'); function renderAllocationTable() { if (!allocationTableContainer) return; if (arpStrategies.length === 0) { allocationTableContainer.innerHTML = '

Please define ARP strategies on the first tab.

'; return; } let tableHTML = '

Assign Portfolio Weights:

'; arpStrategies.forEach(s => { // Ensure default weight if navigating back and forth or first time if (s.weight === undefined) s.weight = (100 / arpStrategies.length); // Initial equal weight tableHTML += ``; }); tableHTML += '
Strategy NameExp. Return (%)Volatility (%)Weight (%)
${escapeHtml(s.name)} ${s.expReturn.toFixed(1)} ${s.volatility.toFixed(1)}
'; allocationTableContainer.innerHTML = tableHTML; attachWeightInputListeners(); updateTotalWeightDisplay(); } function attachWeightInputListeners() { document.querySelectorAll('.arp-strategy-weight').forEach(input => { input.addEventListener('change', (e) => { const strategyId = e.target.dataset.id; const strategy = arpStrategies.find(s => s.id === strategyId); if (strategy) { strategy.weight = parseFloat(e.target.value) || 0; } updateTotalWeightDisplay(); }); }); } function updateTotalWeightDisplay() { const totalWeight = arpStrategies.reduce((sum, s) => sum + (s.weight || 0), 0); totalWeightDisplayEl.textContent = `Total Weight: ${totalWeight.toFixed(1)}%`; if (Math.abs(totalWeight - 100) < 0.01) { // Allow for small floating point inaccuracies totalWeightDisplayEl.className = 'arp-weight-success'; } else { totalWeightDisplayEl.className = 'arp-weight-error'; } } evaluatePortfolioButton?.addEventListener('click', () => { const totalWeight = arpStrategies.reduce((sum, s) => sum + (s.weight || 0), 0); if (Math.abs(totalWeight - 100) > 0.1) { // Use a slightly larger tolerance for user input alert('Total portfolio weights must sum to 100%. Current total: ' + totalWeight.toFixed(1) + '%'); return; } if (!validateStrategyInputs()){ // Re-validate strategy inputs if user went back switchTab('defineTab'); return; } let weightedAvgReturn = 0; let evaluationHTML = '

Individual ARP Strategy Metrics:

'; riskFreeRate = parseFloat(riskFreeRateInput.value) / 100 || 0; // Ensure riskFreeRate is current arpStrategies.forEach(s => { weightedAvgReturn += (s.expReturn / 100) * (s.weight / 100); let sharpeRatio = 'N/A'; if (s.volatility > 0) { sharpeRatio = ((s.expReturn / 100 - riskFreeRate) / (s.volatility / 100)).toFixed(2); } evaluationHTML += ``; }); evaluationHTML += '
Strategy NameExp. Return (%)Volatility (%)Weight (%)Sharpe Ratio*
${escapeHtml(s.name)} ${s.expReturn.toFixed(1)} ${s.volatility.toFixed(1)} ${s.weight.toFixed(1)} ${sharpeRatio}
'; evaluationHTML += `

Portfolio Summary:

Weighted Average Expected Portfolio Return: ${(weightedAvgReturn * 100).toFixed(2)}%

*Sharpe Ratio calculated using a risk-free rate of ${(riskFreeRate*100).toFixed(1)}%. Assumes individual strategy inputs are net of fees.

`; portfolioMetricsEl.innerHTML = evaluationHTML; downloadPdfButtonArp.style.display = 'block'; }); // --- PDF Download --- downloadPdfButtonArp?.addEventListener('click', function () { const { jsPDF } = window.jspdf; const pdfOutputArea = document.getElementById('arpPdfOutputArea'); if (!pdfOutputArea || arpStrategies.length === 0 || portfolioMetricsEl.innerHTML.includes("Define strategies")) { alert('Please define strategies, allocate weights, and evaluate before downloading PDF.'); return; } html2canvas(pdfOutputArea, { scale: 1.5, useCORS: true, backgroundColor: '#ffffff' }) // Slightly increased scale for PDF clarity .then(canvas => { const imgData = canvas.toDataURL('image/jpeg', 0.85); // JPEG for smaller size const pdf = new jsPDF({ orientation: 'portrait', unit: 'pt', format: 'a4' }); const pdfWidth = pdf.internal.pageSize.getWidth(); const pdfHeight = pdf.internal.pageSize.getHeight(); const margin = 30; const contentWidth = pdfWidth - 2 * margin; const canvasWidth = canvas.width; const canvasHeight = canvas.height; const ratio = canvasWidth / canvasHeight; let imgWidth = contentWidth; let imgHeight = imgWidth / ratio; if (imgHeight > pdfHeight - 2 * margin) { imgHeight = pdfHeight - 2 * margin; imgWidth = imgHeight * ratio; } pdf.addImage(imgData, 'JPEG', margin, margin, imgWidth, imgHeight, undefined, 'MEDIUM'); pdf.save('ARP_Strategy_Evaluation.pdf'); }) .catch(err => { console.error("Error generating PDF:", err); alert("Error generating PDF. See console for details."); }); }); // --- Utility --- function escapeHtml(unsafe) { if (typeof unsafe !== 'string') return unsafe === undefined || unsafe === null ? '' : String(unsafe); return unsafe.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'"); } // --- Initial Setup --- riskFreeRateInput.value = (riskFreeRate * 100).toFixed(1); // Set initial display value generateArpInputsButton.click(); // Auto-generate inputs for default number of strategies switchTab('defineTab'); // Start on the first tab });
Scroll to Top