rework investment model
This commit is contained in:
parent
22bea2c53d
commit
6f6c80480f
6 changed files with 633 additions and 141 deletions
|
|
@ -17,26 +17,31 @@ class ChartManager {
|
|||
}
|
||||
|
||||
try {
|
||||
// Instance Growth Chart
|
||||
const instanceCanvas = document.getElementById('instanceGrowthChart');
|
||||
if (!instanceCanvas) {
|
||||
console.error('Instance growth chart canvas not found');
|
||||
// ROI Progression Chart (replaces Instance Growth Chart)
|
||||
const roiCanvas = document.getElementById('instanceGrowthChart');
|
||||
if (!roiCanvas) {
|
||||
console.error('ROI progression chart canvas not found');
|
||||
return;
|
||||
}
|
||||
const instanceCtx = instanceCanvas.getContext('2d');
|
||||
this.charts.instanceGrowth = new Chart(instanceCtx, {
|
||||
const roiCtx = roiCanvas.getContext('2d');
|
||||
this.charts.roiProgression = new Chart(roiCtx, {
|
||||
type: 'line',
|
||||
data: { labels: [], datasets: [] },
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: { position: 'top' }
|
||||
legend: { position: 'top' },
|
||||
title: { display: true, text: 'ROI Progression Over Time' }
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
title: { display: true, text: 'Total Instances' }
|
||||
title: { display: true, text: 'ROI (%)' },
|
||||
grid: {
|
||||
color: function(context) {
|
||||
return context.tick.value === 0 ? 'rgba(0,0,0,0.5)' : 'rgba(0,0,0,0.1)';
|
||||
}
|
||||
}
|
||||
},
|
||||
x: {
|
||||
title: { display: true, text: 'Month' }
|
||||
|
|
@ -45,26 +50,31 @@ class ChartManager {
|
|||
}
|
||||
});
|
||||
|
||||
// Revenue Chart
|
||||
const revenueCanvas = document.getElementById('revenueChart');
|
||||
if (!revenueCanvas) {
|
||||
console.error('Revenue chart canvas not found');
|
||||
// Net Position Chart (replaces simple Revenue Chart)
|
||||
const netPositionCanvas = document.getElementById('revenueChart');
|
||||
if (!netPositionCanvas) {
|
||||
console.error('Net position chart canvas not found');
|
||||
return;
|
||||
}
|
||||
const revenueCtx = revenueCanvas.getContext('2d');
|
||||
this.charts.revenue = new Chart(revenueCtx, {
|
||||
const netPositionCtx = netPositionCanvas.getContext('2d');
|
||||
this.charts.netPosition = new Chart(netPositionCtx, {
|
||||
type: 'line',
|
||||
data: { labels: [], datasets: [] },
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: { position: 'top' }
|
||||
legend: { position: 'top' },
|
||||
title: { display: true, text: 'Net Financial Position (Break-Even Analysis)' }
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
title: { display: true, text: 'Cumulative Revenue (CHF)' }
|
||||
title: { display: true, text: 'Net Position (CHF)' },
|
||||
grid: {
|
||||
color: function(context) {
|
||||
return context.tick.value === 0 ? 'rgba(0,0,0,0.8)' : 'rgba(0,0,0,0.1)';
|
||||
}
|
||||
}
|
||||
},
|
||||
x: {
|
||||
title: { display: true, text: 'Month' }
|
||||
|
|
@ -73,28 +83,30 @@ class ChartManager {
|
|||
}
|
||||
});
|
||||
|
||||
// Cash Flow Chart
|
||||
const cashFlowCanvas = document.getElementById('cashFlowChart');
|
||||
if (!cashFlowCanvas) {
|
||||
console.error('Cash flow chart canvas not found');
|
||||
// Model Comparison Chart (replaces generic Cash Flow Chart)
|
||||
const modelComparisonCanvas = document.getElementById('cashFlowChart');
|
||||
if (!modelComparisonCanvas) {
|
||||
console.error('Model comparison chart canvas not found');
|
||||
return;
|
||||
}
|
||||
const cashFlowCtx = cashFlowCanvas.getContext('2d');
|
||||
this.charts.cashFlow = new Chart(cashFlowCtx, {
|
||||
const modelComparisonCtx = modelComparisonCanvas.getContext('2d');
|
||||
this.charts.modelComparison = new Chart(modelComparisonCtx, {
|
||||
type: 'bar',
|
||||
data: { labels: [], datasets: [] },
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: { position: 'top' }
|
||||
legend: { position: 'top' },
|
||||
title: { display: true, text: 'Investment Model Performance Comparison' }
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
title: { display: true, text: 'Monthly Cash Flow (CHF)' }
|
||||
beginAtZero: true,
|
||||
title: { display: true, text: 'Total Return (CHF)' }
|
||||
},
|
||||
x: {
|
||||
title: { display: true, text: 'Month' }
|
||||
title: { display: true, text: 'Growth Scenario' }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -126,11 +138,11 @@ class ChartManager {
|
|||
updateCharts() {
|
||||
try {
|
||||
const scenarios = Object.keys(this.calculator.results);
|
||||
if (scenarios.length === 0 || !this.charts.instanceGrowth) return;
|
||||
if (scenarios.length === 0 || !this.charts.roiProgression) return;
|
||||
|
||||
const colors = {
|
||||
conservative: '#28a745',
|
||||
moderate: '#ffc107',
|
||||
moderate: '#ffc107',
|
||||
aggressive: '#dc3545'
|
||||
};
|
||||
|
||||
|
|
@ -138,43 +150,65 @@ class ChartManager {
|
|||
const maxMonths = Math.max(...scenarios.map(s => this.calculator.monthlyData[s].length));
|
||||
const monthLabels = Array.from({ length: maxMonths }, (_, i) => `M${i + 1}`);
|
||||
|
||||
// Update Instance Growth Chart
|
||||
this.charts.instanceGrowth.data.labels = monthLabels;
|
||||
this.charts.instanceGrowth.data.datasets = scenarios.map(scenario => ({
|
||||
label: this.calculator.scenarios[scenario].name,
|
||||
data: this.calculator.monthlyData[scenario].map(d => d.totalInstances),
|
||||
// Update ROI Progression Chart
|
||||
this.charts.roiProgression.data.labels = monthLabels;
|
||||
this.charts.roiProgression.data.datasets = scenarios.map(scenario => ({
|
||||
label: `${this.calculator.scenarios[scenario].name} (${this.calculator.results[scenario].investmentModel})`,
|
||||
data: this.calculator.monthlyData[scenario].map(d => d.roiPercent),
|
||||
borderColor: colors[scenario],
|
||||
backgroundColor: colors[scenario] + '20',
|
||||
tension: 0.4
|
||||
tension: 0.4,
|
||||
pointBackgroundColor: this.calculator.monthlyData[scenario].map(d =>
|
||||
d.roiPercent >= 0 ? colors[scenario] : '#dc3545'
|
||||
)
|
||||
}));
|
||||
this.charts.instanceGrowth.update();
|
||||
this.charts.roiProgression.update();
|
||||
|
||||
// Update Revenue Chart
|
||||
this.charts.revenue.data.labels = monthLabels;
|
||||
this.charts.revenue.data.datasets = scenarios.map(scenario => ({
|
||||
label: this.calculator.scenarios[scenario].name + ' (CSP)',
|
||||
data: this.calculator.monthlyData[scenario].map(d => d.cumulativeCSPRevenue),
|
||||
// Update Net Position Chart (Break-Even Analysis)
|
||||
this.charts.netPosition.data.labels = monthLabels;
|
||||
this.charts.netPosition.data.datasets = scenarios.map(scenario => ({
|
||||
label: `${this.calculator.scenarios[scenario].name} Net Position`,
|
||||
data: this.calculator.monthlyData[scenario].map(d => d.netPosition),
|
||||
borderColor: colors[scenario],
|
||||
backgroundColor: colors[scenario] + '20',
|
||||
tension: 0.4
|
||||
tension: 0.4,
|
||||
fill: {
|
||||
target: 'origin',
|
||||
above: colors[scenario] + '10',
|
||||
below: '#dc354510'
|
||||
}
|
||||
}));
|
||||
this.charts.revenue.update();
|
||||
this.charts.netPosition.update();
|
||||
|
||||
// Update Cash Flow Chart (show average across scenarios)
|
||||
const avgCashFlow = monthLabels.map((_, monthIndex) => {
|
||||
const monthData = scenarios.map(scenario =>
|
||||
this.calculator.monthlyData[scenario][monthIndex]?.cspRevenue || 0
|
||||
);
|
||||
return monthData.reduce((sum, val) => sum + val, 0) / monthData.length;
|
||||
});
|
||||
|
||||
this.charts.cashFlow.data.labels = monthLabels;
|
||||
this.charts.cashFlow.data.datasets = [{
|
||||
label: 'Average Monthly CSP Revenue',
|
||||
data: avgCashFlow,
|
||||
backgroundColor: '#007bff'
|
||||
// Update Model Comparison Chart
|
||||
const scenarioLabels = scenarios.map(s => this.calculator.scenarios[s].name);
|
||||
const currentInvestmentModel = Object.values(this.calculator.results)[0]?.investmentModel || 'direct';
|
||||
|
||||
// Show comparison with both models for the same scenarios
|
||||
this.charts.modelComparison.data.labels = scenarioLabels;
|
||||
this.charts.modelComparison.data.datasets = [{
|
||||
label: `${currentInvestmentModel === 'loan' ? 'Loan Model' : 'Direct Investment'} - Final Return`,
|
||||
data: scenarios.map(scenario => this.calculator.results[scenario].cspRevenue),
|
||||
backgroundColor: scenarios.map(scenario => colors[scenario] + '80'),
|
||||
borderColor: scenarios.map(scenario => colors[scenario]),
|
||||
borderWidth: 2
|
||||
}];
|
||||
this.charts.cashFlow.update();
|
||||
|
||||
// Add performance metrics for direct investment
|
||||
if (currentInvestmentModel === 'direct') {
|
||||
this.charts.modelComparison.data.datasets.push({
|
||||
label: 'Performance Bonus Impact',
|
||||
data: scenarios.map(scenario =>
|
||||
this.calculator.results[scenario].avgPerformanceBonus *
|
||||
this.calculator.results[scenario].cspRevenue
|
||||
),
|
||||
backgroundColor: '#17a2b8',
|
||||
borderColor: '#17a2b8',
|
||||
borderWidth: 2
|
||||
});
|
||||
}
|
||||
|
||||
this.charts.modelComparison.update();
|
||||
} catch (error) {
|
||||
console.error('Error updating charts:', error);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue