triforce-strategies: add S&P 500 vs anythingn calculator
claude helped with this
This commit is contained in:
parent
81b79a225f
commit
d19f970d48
6 changed files with 969 additions and 2 deletions
567
static/js/fundComparison.js
Normal file
567
static/js/fundComparison.js
Normal file
|
|
@ -0,0 +1,567 @@
|
|||
(function() {
|
||||
'use strict';
|
||||
|
||||
// Box-Muller transform for normal distribution
|
||||
function boxMuller() {
|
||||
let u1, u2;
|
||||
do {
|
||||
u1 = Math.random();
|
||||
u2 = Math.random();
|
||||
} while (u1 === 0);
|
||||
return Math.sqrt(-2.0 * Math.log(u1)) * Math.cos(2.0 * Math.PI * u2);
|
||||
}
|
||||
|
||||
// Convert annual rate to monthly
|
||||
function annualToMonthly(annualRate) {
|
||||
return Math.pow(1 + annualRate, 1/12) - 1;
|
||||
}
|
||||
|
||||
// Run a single simulation
|
||||
function runSimulation(params, months) {
|
||||
const monthlyReturn = annualToMonthly(params.annualReturn);
|
||||
const monthlyVol = params.annualVol / Math.sqrt(12);
|
||||
const monthlyExpense = params.expenseRatio / 12;
|
||||
|
||||
let value = 0;
|
||||
const values = [0];
|
||||
|
||||
for (let m = 1; m <= months; m++) {
|
||||
// Add monthly deposit at start of month
|
||||
value += params.monthlyDeposit;
|
||||
|
||||
// Apply log-normal return
|
||||
const randomReturn = monthlyReturn + monthlyVol * boxMuller();
|
||||
value *= Math.exp(randomReturn);
|
||||
|
||||
// Deduct expense ratio
|
||||
value *= (1 - monthlyExpense);
|
||||
|
||||
values.push(value);
|
||||
}
|
||||
|
||||
// Track pre-tax value
|
||||
const preTaxValue = value;
|
||||
|
||||
// Apply capital gains tax at withdrawal if specified
|
||||
const totalDeposits = params.monthlyDeposit * months;
|
||||
const gains = value - totalDeposits;
|
||||
let taxPaid = 0;
|
||||
if (params.capitalGainsTax > 0 && gains > 0) {
|
||||
taxPaid = gains * params.capitalGainsTax;
|
||||
value -= taxPaid;
|
||||
}
|
||||
|
||||
return { values, finalValue: value, preTaxValue, taxPaid };
|
||||
}
|
||||
|
||||
// Run Monte Carlo simulation
|
||||
function runMonteCarlo(params, months, numSimulations) {
|
||||
const allFinalValues = [];
|
||||
const allPreTaxValues = [];
|
||||
const allTaxPaid = [];
|
||||
const allValuesByMonth = [];
|
||||
|
||||
for (let i = 0; i <= months; i++) {
|
||||
allValuesByMonth.push([]);
|
||||
}
|
||||
|
||||
for (let sim = 0; sim < numSimulations; sim++) {
|
||||
const result = runSimulation(params, months);
|
||||
allFinalValues.push(result.finalValue);
|
||||
allPreTaxValues.push(result.preTaxValue);
|
||||
allTaxPaid.push(result.taxPaid);
|
||||
for (let m = 0; m <= months; m++) {
|
||||
allValuesByMonth[m].push(result.values[m]);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort values at each month for percentile calculation
|
||||
for (let m = 0; m <= months; m++) {
|
||||
allValuesByMonth[m].sort((a, b) => a - b);
|
||||
}
|
||||
allFinalValues.sort((a, b) => a - b);
|
||||
allPreTaxValues.sort((a, b) => a - b);
|
||||
allTaxPaid.sort((a, b) => a - b);
|
||||
|
||||
// Calculate percentiles
|
||||
function getPercentile(sortedArr, p) {
|
||||
const idx = Math.floor(sortedArr.length * p);
|
||||
return sortedArr[Math.min(idx, sortedArr.length - 1)];
|
||||
}
|
||||
|
||||
const percentiles = {
|
||||
p10: getPercentile(allFinalValues, 0.10),
|
||||
p25: getPercentile(allFinalValues, 0.25),
|
||||
p50: getPercentile(allFinalValues, 0.50),
|
||||
p75: getPercentile(allFinalValues, 0.75),
|
||||
p90: getPercentile(allFinalValues, 0.90)
|
||||
};
|
||||
|
||||
const preTaxPercentiles = {
|
||||
p50: getPercentile(allPreTaxValues, 0.50)
|
||||
};
|
||||
|
||||
const taxPaidPercentiles = {
|
||||
p50: getPercentile(allTaxPaid, 0.50)
|
||||
};
|
||||
|
||||
// Get percentile curves for chart
|
||||
const curves = {
|
||||
p10: [],
|
||||
p50: [],
|
||||
p90: []
|
||||
};
|
||||
|
||||
for (let m = 0; m <= months; m++) {
|
||||
curves.p10.push(getPercentile(allValuesByMonth[m], 0.10));
|
||||
curves.p50.push(getPercentile(allValuesByMonth[m], 0.50));
|
||||
curves.p90.push(getPercentile(allValuesByMonth[m], 0.90));
|
||||
}
|
||||
|
||||
return { percentiles, preTaxPercentiles, taxPaidPercentiles, curves };
|
||||
}
|
||||
|
||||
// Run deterministic simulation (no volatility)
|
||||
function runDeterministic(params, months) {
|
||||
const monthlyReturn = annualToMonthly(params.annualReturn);
|
||||
const monthlyExpense = params.expenseRatio / 12;
|
||||
|
||||
let value = 0;
|
||||
const values = [0];
|
||||
|
||||
for (let m = 1; m <= months; m++) {
|
||||
value += params.monthlyDeposit;
|
||||
value *= (1 + monthlyReturn);
|
||||
value *= (1 - monthlyExpense);
|
||||
values.push(value);
|
||||
}
|
||||
|
||||
const preTaxValue = value;
|
||||
const totalDeposits = params.monthlyDeposit * months;
|
||||
const gains = value - totalDeposits;
|
||||
let taxPaid = 0;
|
||||
if (params.capitalGainsTax > 0 && gains > 0) {
|
||||
taxPaid = gains * params.capitalGainsTax;
|
||||
value -= taxPaid;
|
||||
}
|
||||
|
||||
return {
|
||||
percentiles: { p10: value, p25: value, p50: value, p75: value, p90: value },
|
||||
preTaxPercentiles: { p50: preTaxValue },
|
||||
taxPaidPercentiles: { p50: taxPaid },
|
||||
curves: { p10: values, p50: values, p90: values }
|
||||
};
|
||||
}
|
||||
|
||||
// Format currency
|
||||
function formatCurrency(value) {
|
||||
if (value >= 1000000) {
|
||||
return '$' + (value / 1000000).toFixed(2) + 'M';
|
||||
} else if (value >= 1000) {
|
||||
return '$' + (value / 1000).toFixed(1) + 'K';
|
||||
}
|
||||
return '$' + value.toFixed(0);
|
||||
}
|
||||
|
||||
// Chart renderer
|
||||
const ChartRenderer = {
|
||||
canvas: null,
|
||||
ctx: null,
|
||||
|
||||
init: function(canvasId) {
|
||||
this.canvas = document.getElementById(canvasId);
|
||||
this.ctx = this.canvas.getContext('2d');
|
||||
this.resize();
|
||||
},
|
||||
|
||||
resize: function() {
|
||||
const container = this.canvas.parentElement;
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
const rect = container.getBoundingClientRect();
|
||||
|
||||
this.canvas.width = rect.width * dpr;
|
||||
this.canvas.height = 400 * dpr;
|
||||
this.canvas.style.width = rect.width + 'px';
|
||||
this.canvas.style.height = '400px';
|
||||
|
||||
this.ctx.scale(dpr, dpr);
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
const width = this.canvas.width / (window.devicePixelRatio || 1);
|
||||
const height = this.canvas.height / (window.devicePixelRatio || 1);
|
||||
this.ctx.clearRect(0, 0, width, height);
|
||||
},
|
||||
|
||||
draw: function(sp500Result, comparedResult, years, monthlyDeposit, useMonteCarlo) {
|
||||
this.clear();
|
||||
|
||||
const width = this.canvas.width / (window.devicePixelRatio || 1);
|
||||
const height = this.canvas.height / (window.devicePixelRatio || 1);
|
||||
const padding = { top: 30, right: 100, bottom: 50, left: 80 };
|
||||
const chartWidth = width - padding.left - padding.right;
|
||||
const chartHeight = height - padding.top - padding.bottom;
|
||||
|
||||
const months = years * 12;
|
||||
const totalDeposits = [];
|
||||
for (let m = 0; m <= months; m++) {
|
||||
totalDeposits.push(monthlyDeposit * m);
|
||||
}
|
||||
|
||||
// Find max value for Y axis
|
||||
let maxVal = 0;
|
||||
const allCurves = [
|
||||
sp500Result.curves.p90,
|
||||
comparedResult.curves.p90,
|
||||
totalDeposits
|
||||
];
|
||||
for (const curve of allCurves) {
|
||||
for (const val of curve) {
|
||||
if (val > maxVal) maxVal = val;
|
||||
}
|
||||
}
|
||||
maxVal *= 1.1; // Add 10% padding
|
||||
|
||||
// Get computed styles for colors
|
||||
const style = getComputedStyle(document.documentElement);
|
||||
const textColor = style.getPropertyValue('--text-color').trim() || '#D4D4D4';
|
||||
const dividerColor = style.getPropertyValue('--divider-color').trim() || '#4a4a4a';
|
||||
const primaryColor = style.getPropertyValue('--primary-color').trim() || '#91e0ee';
|
||||
|
||||
// Draw grid
|
||||
this.ctx.strokeStyle = dividerColor;
|
||||
this.ctx.lineWidth = 0.5;
|
||||
this.ctx.setLineDash([5, 5]);
|
||||
|
||||
// Y-axis grid lines
|
||||
const numYLines = 5;
|
||||
for (let i = 0; i <= numYLines; i++) {
|
||||
const y = padding.top + (chartHeight / numYLines) * i;
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(padding.left, y);
|
||||
this.ctx.lineTo(width - padding.right, y);
|
||||
this.ctx.stroke();
|
||||
}
|
||||
|
||||
// X-axis grid lines
|
||||
for (let yr = 0; yr <= years; yr++) {
|
||||
const x = padding.left + (chartWidth / years) * yr;
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(x, padding.top);
|
||||
this.ctx.lineTo(x, height - padding.bottom);
|
||||
this.ctx.stroke();
|
||||
}
|
||||
|
||||
this.ctx.setLineDash([]);
|
||||
|
||||
// Helper to map data to canvas coordinates
|
||||
const mapX = (month) => padding.left + (month / months) * chartWidth;
|
||||
const mapY = (value) => padding.top + chartHeight - (value / maxVal) * chartHeight;
|
||||
|
||||
// Draw confidence bands if Monte Carlo
|
||||
if (useMonteCarlo) {
|
||||
// S&P 500 band (green)
|
||||
this.ctx.fillStyle = 'rgba(76, 175, 80, 0.15)';
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(mapX(0), mapY(sp500Result.curves.p10[0]));
|
||||
for (let m = 0; m <= months; m++) {
|
||||
this.ctx.lineTo(mapX(m), mapY(sp500Result.curves.p90[m]));
|
||||
}
|
||||
for (let m = months; m >= 0; m--) {
|
||||
this.ctx.lineTo(mapX(m), mapY(sp500Result.curves.p10[m]));
|
||||
}
|
||||
this.ctx.closePath();
|
||||
this.ctx.fill();
|
||||
|
||||
// Compared fund band (blue)
|
||||
this.ctx.fillStyle = 'rgba(33, 150, 243, 0.15)';
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(mapX(0), mapY(comparedResult.curves.p10[0]));
|
||||
for (let m = 0; m <= months; m++) {
|
||||
this.ctx.lineTo(mapX(m), mapY(comparedResult.curves.p90[m]));
|
||||
}
|
||||
for (let m = months; m >= 0; m--) {
|
||||
this.ctx.lineTo(mapX(m), mapY(comparedResult.curves.p10[m]));
|
||||
}
|
||||
this.ctx.closePath();
|
||||
this.ctx.fill();
|
||||
}
|
||||
|
||||
// Draw total deposits line (dashed gray)
|
||||
this.ctx.strokeStyle = '#888888';
|
||||
this.ctx.lineWidth = 2;
|
||||
this.ctx.setLineDash([8, 4]);
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(mapX(0), mapY(totalDeposits[0]));
|
||||
for (let m = 1; m <= months; m++) {
|
||||
this.ctx.lineTo(mapX(m), mapY(totalDeposits[m]));
|
||||
}
|
||||
this.ctx.stroke();
|
||||
this.ctx.setLineDash([]);
|
||||
|
||||
// Draw S&P 500 median line (green)
|
||||
this.ctx.strokeStyle = '#4CAF50';
|
||||
this.ctx.lineWidth = 2.5;
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(mapX(0), mapY(sp500Result.curves.p50[0]));
|
||||
for (let m = 1; m <= months; m++) {
|
||||
this.ctx.lineTo(mapX(m), mapY(sp500Result.curves.p50[m]));
|
||||
}
|
||||
this.ctx.stroke();
|
||||
|
||||
// Draw compared fund median line (blue)
|
||||
this.ctx.strokeStyle = '#2196F3';
|
||||
this.ctx.lineWidth = 2.5;
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(mapX(0), mapY(comparedResult.curves.p50[0]));
|
||||
for (let m = 1; m <= months; m++) {
|
||||
this.ctx.lineTo(mapX(m), mapY(comparedResult.curves.p50[m]));
|
||||
}
|
||||
this.ctx.stroke();
|
||||
|
||||
// Draw capital gains tax loss on compared fund (red segment)
|
||||
const taxPaid = comparedResult.taxPaidPercentiles.p50;
|
||||
const preTaxValue = comparedResult.preTaxPercentiles.p50;
|
||||
const postTaxValue = comparedResult.percentiles.p50;
|
||||
const endX = mapX(months);
|
||||
|
||||
if (taxPaid > 0) {
|
||||
const preTaxY = mapY(preTaxValue);
|
||||
const postTaxY = mapY(postTaxValue);
|
||||
|
||||
// Draw red line from pre-tax to post-tax
|
||||
this.ctx.strokeStyle = '#F44336';
|
||||
this.ctx.lineWidth = 3;
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(endX, preTaxY);
|
||||
this.ctx.lineTo(endX, postTaxY);
|
||||
this.ctx.stroke();
|
||||
|
||||
// Draw red circle at post-tax point
|
||||
this.ctx.fillStyle = '#F44336';
|
||||
this.ctx.beginPath();
|
||||
this.ctx.arc(endX, postTaxY, 4, 0, Math.PI * 2);
|
||||
this.ctx.fill();
|
||||
|
||||
// Label the tax loss
|
||||
this.ctx.fillStyle = '#F44336';
|
||||
this.ctx.font = '10px monospace';
|
||||
this.ctx.textAlign = 'left';
|
||||
this.ctx.textBaseline = 'middle';
|
||||
this.ctx.fillText('-' + formatCurrency(taxPaid) + ' tax', endX + 8, (preTaxY + postTaxY) / 2);
|
||||
}
|
||||
|
||||
// Draw delta annotation between funds
|
||||
const sp500Final = sp500Result.percentiles.p50;
|
||||
const comparedFinal = comparedResult.percentiles.p50;
|
||||
const delta = sp500Final - comparedFinal;
|
||||
|
||||
// Draw delta bracket/line
|
||||
const sp500Y = mapY(sp500Final);
|
||||
const comparedY = mapY(comparedFinal);
|
||||
const bracketX = endX + 6;
|
||||
|
||||
this.ctx.strokeStyle = textColor;
|
||||
this.ctx.lineWidth = 1;
|
||||
this.ctx.setLineDash([]);
|
||||
this.ctx.beginPath();
|
||||
// Horizontal ticks
|
||||
this.ctx.moveTo(endX + 2, sp500Y);
|
||||
this.ctx.lineTo(bracketX, sp500Y);
|
||||
this.ctx.moveTo(endX + 2, comparedY);
|
||||
this.ctx.lineTo(bracketX, comparedY);
|
||||
// Vertical line
|
||||
this.ctx.moveTo(bracketX, sp500Y);
|
||||
this.ctx.lineTo(bracketX, comparedY);
|
||||
this.ctx.stroke();
|
||||
|
||||
// Delta label
|
||||
this.ctx.fillStyle = delta >= 0 ? '#4CAF50' : '#F44336';
|
||||
this.ctx.font = '11px monospace';
|
||||
this.ctx.textAlign = 'left';
|
||||
this.ctx.textBaseline = 'middle';
|
||||
const deltaLabel = 'Δ ' + (delta >= 0 ? '+' : '') + formatCurrency(delta);
|
||||
this.ctx.fillText(deltaLabel, bracketX + 4, (sp500Y + comparedY) / 2);
|
||||
|
||||
// Draw axes
|
||||
this.ctx.strokeStyle = textColor;
|
||||
this.ctx.lineWidth = 1;
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(padding.left, padding.top);
|
||||
this.ctx.lineTo(padding.left, height - padding.bottom);
|
||||
this.ctx.lineTo(width - padding.right, height - padding.bottom);
|
||||
this.ctx.stroke();
|
||||
|
||||
// Y-axis labels
|
||||
this.ctx.fillStyle = textColor;
|
||||
this.ctx.font = '12px monospace';
|
||||
this.ctx.textAlign = 'right';
|
||||
this.ctx.textBaseline = 'middle';
|
||||
for (let i = 0; i <= numYLines; i++) {
|
||||
const value = maxVal * (1 - i / numYLines);
|
||||
const y = padding.top + (chartHeight / numYLines) * i;
|
||||
this.ctx.fillText(formatCurrency(value), padding.left - 10, y);
|
||||
}
|
||||
|
||||
// X-axis labels
|
||||
this.ctx.textAlign = 'center';
|
||||
this.ctx.textBaseline = 'top';
|
||||
for (let yr = 0; yr <= years; yr++) {
|
||||
const x = padding.left + (chartWidth / years) * yr;
|
||||
this.ctx.fillText(yr + 'y', x, height - padding.bottom + 10);
|
||||
}
|
||||
|
||||
// Legend
|
||||
const legendY = padding.top - 15;
|
||||
const legendItems = [
|
||||
{ color: '#4CAF50', label: 'S&P 500' },
|
||||
{ color: '#2196F3', label: 'Compared' },
|
||||
{ color: '#888888', label: 'Deposits', dashed: true },
|
||||
{ color: '#F44336', label: 'Tax Loss' }
|
||||
];
|
||||
|
||||
let legendX = padding.left;
|
||||
this.ctx.font = '11px monospace';
|
||||
this.ctx.textAlign = 'left';
|
||||
this.ctx.textBaseline = 'middle';
|
||||
|
||||
for (const item of legendItems) {
|
||||
// Draw line sample
|
||||
this.ctx.strokeStyle = item.color;
|
||||
this.ctx.lineWidth = 2;
|
||||
if (item.dashed) {
|
||||
this.ctx.setLineDash([4, 2]);
|
||||
}
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(legendX, legendY);
|
||||
this.ctx.lineTo(legendX + 20, legendY);
|
||||
this.ctx.stroke();
|
||||
this.ctx.setLineDash([]);
|
||||
|
||||
// Draw label
|
||||
this.ctx.fillStyle = textColor;
|
||||
this.ctx.fillText(item.label, legendX + 25, legendY);
|
||||
|
||||
legendX += this.ctx.measureText(item.label).width + 50;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Main calculator
|
||||
const Calculator = {
|
||||
form: null,
|
||||
chartRenderer: null,
|
||||
|
||||
init: function() {
|
||||
this.form = document.getElementById('fund-comparison-form');
|
||||
this.chartRenderer = Object.create(ChartRenderer);
|
||||
this.chartRenderer.init('fund-comparison-chart');
|
||||
|
||||
this.form.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
this.calculate();
|
||||
});
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
this.chartRenderer.resize();
|
||||
this.calculate();
|
||||
});
|
||||
|
||||
// Initial calculation
|
||||
this.calculate();
|
||||
},
|
||||
|
||||
getInputs: function() {
|
||||
return {
|
||||
sp500: {
|
||||
annualReturn: parseFloat(document.getElementById('sp500-return').value) / 100,
|
||||
annualVol: parseFloat(document.getElementById('sp500-volatility').value) / 100,
|
||||
expenseRatio: parseFloat(document.getElementById('sp500-expense').value) / 100,
|
||||
capitalGainsTax: 0,
|
||||
monthlyDeposit: parseFloat(document.getElementById('monthly-deposit').value)
|
||||
},
|
||||
compared: {
|
||||
annualReturn: parseFloat(document.getElementById('compared-return').value) / 100,
|
||||
annualVol: parseFloat(document.getElementById('compared-volatility').value) / 100,
|
||||
expenseRatio: parseFloat(document.getElementById('compared-admin-fee').value) / 100,
|
||||
capitalGainsTax: parseFloat(document.getElementById('compared-capital-gains').value) / 100,
|
||||
monthlyDeposit: parseFloat(document.getElementById('monthly-deposit').value)
|
||||
},
|
||||
years: parseInt(document.getElementById('time-frame').value),
|
||||
useMonteCarlo: document.getElementById('use-monte-carlo').checked
|
||||
};
|
||||
},
|
||||
|
||||
calculate: function() {
|
||||
const inputs = this.getInputs();
|
||||
const months = inputs.years * 12;
|
||||
const numSimulations = 1000;
|
||||
|
||||
let sp500Result, comparedResult;
|
||||
|
||||
if (inputs.useMonteCarlo) {
|
||||
sp500Result = runMonteCarlo(inputs.sp500, months, numSimulations);
|
||||
comparedResult = runMonteCarlo(inputs.compared, months, numSimulations);
|
||||
} else {
|
||||
sp500Result = runDeterministic(inputs.sp500, months);
|
||||
comparedResult = runDeterministic(inputs.compared, months);
|
||||
}
|
||||
|
||||
// Draw chart
|
||||
this.chartRenderer.draw(
|
||||
sp500Result,
|
||||
comparedResult,
|
||||
inputs.years,
|
||||
inputs.sp500.monthlyDeposit,
|
||||
inputs.useMonteCarlo
|
||||
);
|
||||
|
||||
// Update summary
|
||||
this.updateSummary(sp500Result, comparedResult, inputs);
|
||||
},
|
||||
|
||||
updateSummary: function(sp500Result, comparedResult, inputs) {
|
||||
const totalDeposits = inputs.sp500.monthlyDeposit * inputs.years * 12;
|
||||
|
||||
// Summary table
|
||||
document.getElementById('total-deposits').textContent = formatCurrency(totalDeposits);
|
||||
|
||||
document.getElementById('sp500-final').textContent = formatCurrency(sp500Result.percentiles.p50);
|
||||
const sp500Gain = sp500Result.percentiles.p50 - totalDeposits;
|
||||
document.getElementById('sp500-gain').textContent = formatCurrency(sp500Gain);
|
||||
document.getElementById('sp500-multiple').textContent = (sp500Result.percentiles.p50 / totalDeposits).toFixed(2) + 'x';
|
||||
|
||||
document.getElementById('compared-final').textContent = formatCurrency(comparedResult.percentiles.p50);
|
||||
const comparedGain = comparedResult.percentiles.p50 - totalDeposits;
|
||||
document.getElementById('compared-gain').textContent = formatCurrency(comparedGain);
|
||||
document.getElementById('compared-multiple').textContent = (comparedResult.percentiles.p50 / totalDeposits).toFixed(2) + 'x';
|
||||
|
||||
// Percentiles section
|
||||
const percentilesSection = document.getElementById('percentiles-section');
|
||||
if (inputs.useMonteCarlo) {
|
||||
percentilesSection.style.display = 'block';
|
||||
|
||||
document.getElementById('sp500-p10').textContent = formatCurrency(sp500Result.percentiles.p10);
|
||||
document.getElementById('sp500-p25').textContent = formatCurrency(sp500Result.percentiles.p25);
|
||||
document.getElementById('sp500-p50').textContent = formatCurrency(sp500Result.percentiles.p50);
|
||||
document.getElementById('sp500-p75').textContent = formatCurrency(sp500Result.percentiles.p75);
|
||||
document.getElementById('sp500-p90').textContent = formatCurrency(sp500Result.percentiles.p90);
|
||||
|
||||
document.getElementById('compared-p10').textContent = formatCurrency(comparedResult.percentiles.p10);
|
||||
document.getElementById('compared-p25').textContent = formatCurrency(comparedResult.percentiles.p25);
|
||||
document.getElementById('compared-p50').textContent = formatCurrency(comparedResult.percentiles.p50);
|
||||
document.getElementById('compared-p75').textContent = formatCurrency(comparedResult.percentiles.p75);
|
||||
document.getElementById('compared-p90').textContent = formatCurrency(comparedResult.percentiles.p90);
|
||||
} else {
|
||||
percentilesSection.style.display = 'none';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize on DOM ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', () => Calculator.init());
|
||||
} else {
|
||||
Calculator.init();
|
||||
}
|
||||
})();
|
||||
Loading…
Add table
Add a link
Reference in a new issue