×
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 = `