Portfolio Risk Parity & Beta Neutral Strategy Optimizer
×

Step 1: Define Portfolio Assets

Add the assets you want to include in the portfolio. Provide the expected annual return, volatility (standard deviation), and beta for each.

Asset Name
Exp. Return (%)
Volatility (%)
Beta

Step 2: Select Strategy & Constraints

Choose your optimization strategy and define the total value of the portfolio.

Step 3: Optimized Portfolio

Optimization Results

Portfolio Return

0.00%

Portfolio Volatility

0.00%

Portfolio Beta

0.00

Optimal Asset Allocation

Asset Name Weight (%) Value ($) Risk Contribution (%)

Run the optimization from the 'Strategy & Constraints' tab to see your results here.

Strategy: Beta Neutral

This strategy creates a portfolio with a net beta of zero, making it theoretically immune to broad market movements. It balances assets with positive beta (long positions) against assets with negative beta (short positions). The optimizer solves for weights that drive the portfolio beta to zero, using risk parity principles to allocate capital within the long and short groups.

`; } if (optimizedPortfolio) { displayResults(optimizedPortfolio, portfolioValue, explanation); navigateToTab('results-tab'); } } function calculateRiskParity(assets) { if (!assets || assets.length === 0) return calculatePortfolioMetrics([]); const inverseVolatilities = assets.map(a => 1 / a.volatility); const sumOfInverseVolatilities = inverseVolatilities.reduce((sum, iv) => sum + iv, 0); const weightedAssets = assets.map((asset, i) => ({ ...asset, weight: inverseVolatilities[i] / sumOfInverseVolatilities })); return calculatePortfolioMetrics(weightedAssets); } function calculateBetaNeutral(assets) { const posBetaAssets = assets.filter(a => a.beta > 0); const negBetaAssets = assets.filter(a => a.beta < 0); const zeroBetaAssets = assets.filter(a => a.beta === 0); if (posBetaAssets.length === 0 || negBetaAssets.length === 0) { return null; // Cannot be beta neutral } const posSubPortfolio = calculateRiskParity(posBetaAssets); const negSubPortfolio = calculateRiskParity(negBetaAssets); const beta_p = posSubPortfolio.portfolioBeta; const beta_n = negSubPortfolio.portfolioBeta; if (beta_p - beta_n === 0) return null; // Avoid division by zero const totalWeightPos = -beta_n / (beta_p - beta_n); const totalWeightNeg = 1 - totalWeightPos; if (totalWeightPos < 0 || totalWeightNeg < 0 || totalWeightPos > 1 || totalWeightNeg > 1) { // This combination of assets cannot form a simple long-only beta-neutral portfolio. // It might require shorting or leverage, which is outside the current scope. return null; } posSubPortfolio.assets.forEach(a => a.weight *= totalWeightPos); negSubPortfolio.assets.forEach(a => a.weight *= totalWeightNeg); zeroBetaAssets.forEach(a => a.weight = 0); const finalAssets = [...posSubPortfolio.assets, ...negSubPortfolio.assets, ...zeroBetaAssets]; return calculatePortfolioMetrics(finalAssets); } function calculatePortfolioMetrics(assets) { if (!assets || assets.length === 0) { return { assets: [], portfolioReturn: 0, portfolioVolatility: 0, portfolioBeta: 0 }; } let totalReturn = 0, totalBeta = 0; // Calculate raw risk for risk contribution denominator const totalRawRisk = assets.reduce((sum, a) => sum + (a.weight * a.volatility), 0); // Calculate final metrics and risk contribution assets.forEach(asset => { totalReturn += asset.weight * asset.expReturn; totalBeta += asset.weight * asset.beta; asset.riskContribution = totalRawRisk > 0 ? (asset.weight * asset.volatility) / totalRawRisk : 0; }); // Portfolio volatility: sqrt(w1^2*s1^2 + w2^2*s2^2 + ...). Simplified assuming 0 correlation. const squaredVol = assets.reduce((sum, a) => sum + Math.pow(a.weight * a.volatility, 2), 0); const totalVolatility = Math.sqrt(squaredVol); return { assets: assets.sort((a,b) => b.weight - a.weight), portfolioReturn: totalReturn, portfolioVolatility: totalVolatility, portfolioBeta: totalBeta, }; } function displayResults(portfolio, portfolioValue, explanation) { document.getElementById('results-container').style.display = 'block'; document.getElementById('results-placeholder').style.display = 'none'; document.getElementById('results-title').textContent = `Optimization Results (${document.getElementById('optimization-strategy').selectedOptions[0].text})`; document.getElementById('portfolio-return').textContent = (portfolio.portfolioReturn * 100).toFixed(2) + '%'; document.getElementById('portfolio-volatility').textContent = (portfolio.portfolioVolatility * 100).toFixed(2) + '%'; document.getElementById('portfolio-beta').textContent = portfolio.portfolioBeta.toFixed(2); const tableBody = document.querySelector('#allocation-table tbody'); tableBody.innerHTML = ''; portfolio.assets.forEach(asset => { const row = tableBody.insertRow(); row.innerHTML = ` ${asset.name} ${(asset.weight * 100).toFixed(2)}% ${formatAsUSD(asset.weight * portfolioValue)} ${(asset.riskContribution * 100).toFixed(2)}% `; }); document.getElementById('explanation-section').innerHTML = explanation; } function formatAsUSD(amount) { return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(amount); } function generatePdf() { const { jsPDF } = window.jspdf; const content = document.getElementById('pdf-content'); if (!content) return; const pdfButton = document.getElementById('pdf-download-button'); if (pdfButton) pdfButton.style.display = 'none'; html2canvas(content, { scale: 2, backgroundColor: '#ffffff' }).then(canvas => { if (pdfButton) pdfButton.style.display = 'block'; const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF({ orientation: 'p', unit: 'mm', format: 'a4' }); const pdfWidth = pdf.internal.pageSize.getWidth(); const pdfHeight = pdf.internal.pageSize.getHeight(); const canvasWidth = canvas.width; const canvasHeight = canvas.height; const ratio = canvasHeight / canvasWidth; let imgWidth = pdfWidth - 20; // with margin let imgHeight = imgWidth * ratio; if (imgHeight > pdfHeight - 20) { imgHeight = pdfHeight - 20; imgWidth = imgHeight / ratio; } const x = (pdfWidth - imgWidth) / 2; const y = 10; pdf.addImage(imgData, 'PNG', x, y, imgWidth, imgHeight); pdf.save('Portfolio_Optimization_Results.pdf'); }).catch(err => { console.error('PDF generation error:', err); if (pdfButton) pdfButton.style.display = 'block'; }); }
Scroll to Top