show both models at the same time
This commit is contained in:
parent
aa57082a1b
commit
4746cfac25
6 changed files with 488 additions and 326 deletions
|
|
@ -175,98 +175,98 @@ class ChartManager {
|
|||
aggressive: '#dc3545'
|
||||
};
|
||||
|
||||
const modelColors = {
|
||||
direct: { border: '', background: '80' },
|
||||
loan: { border: '', background: '40' }
|
||||
};
|
||||
|
||||
// Get month labels
|
||||
const maxMonths = Math.max(...scenarios.map(s => this.calculator.monthlyData[s].length));
|
||||
const monthLabels = Array.from({ length: maxMonths }, (_, i) => `M${i + 1}`);
|
||||
|
||||
// Update ROI Progression Chart
|
||||
// Update ROI Progression Chart with both models
|
||||
this.charts.roiProgression.data.labels = monthLabels;
|
||||
this.charts.roiProgression.data.datasets = scenarios.filter(s => this.calculator.results[s]).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,
|
||||
pointBackgroundColor: this.calculator.monthlyData[scenario].map(d =>
|
||||
d.roiPercent >= 0 ? colors[scenario] : '#dc3545'
|
||||
)
|
||||
}));
|
||||
this.charts.roiProgression.data.datasets = scenarios.filter(s => this.calculator.results[s]).map(scenario => {
|
||||
const scenarioBase = scenario.replace('_direct', '').replace('_loan', '');
|
||||
const model = scenario.includes('_loan') ? 'loan' : 'direct';
|
||||
const isDirect = model === 'direct';
|
||||
const scenarioName = this.calculator.scenarios[scenarioBase]?.name || scenarioBase;
|
||||
|
||||
return {
|
||||
label: `${scenarioName} (${model.charAt(0).toUpperCase() + model.slice(1)})`,
|
||||
data: this.calculator.monthlyData[scenario].map(d => d.roiPercent),
|
||||
borderColor: colors[scenarioBase],
|
||||
backgroundColor: colors[scenarioBase] + (isDirect ? '30' : '15'),
|
||||
borderDash: isDirect ? [] : [5, 5],
|
||||
borderWidth: isDirect ? 3 : 2,
|
||||
tension: 0.4,
|
||||
pointBackgroundColor: this.calculator.monthlyData[scenario].map(d =>
|
||||
d.roiPercent >= 0 ? colors[scenarioBase] : '#dc3545'
|
||||
)
|
||||
};
|
||||
});
|
||||
this.charts.roiProgression.update();
|
||||
|
||||
// Update Net Position Chart (Break-Even Analysis)
|
||||
// Update Net Position Chart (Break-Even Analysis) with both models
|
||||
this.charts.netPosition.data.labels = monthLabels;
|
||||
this.charts.netPosition.data.datasets = scenarios.filter(s => this.calculator.results[s]).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,
|
||||
fill: {
|
||||
target: 'origin',
|
||||
above: colors[scenario] + '10',
|
||||
below: '#dc354510'
|
||||
}
|
||||
}));
|
||||
this.charts.netPosition.data.datasets = scenarios.filter(s => this.calculator.results[s]).map(scenario => {
|
||||
const scenarioBase = scenario.replace('_direct', '').replace('_loan', '');
|
||||
const model = scenario.includes('_loan') ? 'loan' : 'direct';
|
||||
const isDirect = model === 'direct';
|
||||
const scenarioName = this.calculator.scenarios[scenarioBase]?.name || scenarioBase;
|
||||
|
||||
return {
|
||||
label: `${scenarioName} (${model.charAt(0).toUpperCase() + model.slice(1)}) Net Position`,
|
||||
data: this.calculator.monthlyData[scenario].map(d => d.netPosition),
|
||||
borderColor: colors[scenarioBase],
|
||||
backgroundColor: colors[scenarioBase] + (isDirect ? '30' : '15'),
|
||||
borderDash: isDirect ? [] : [5, 5],
|
||||
borderWidth: isDirect ? 3 : 2,
|
||||
tension: 0.4,
|
||||
fill: {
|
||||
target: 'origin',
|
||||
above: colors[scenarioBase] + (isDirect ? '20' : '10'),
|
||||
below: '#dc354510'
|
||||
}
|
||||
};
|
||||
});
|
||||
this.charts.netPosition.update();
|
||||
|
||||
// Update Model Comparison Chart - Side-by-side comparison of both models
|
||||
// Update Model Comparison Chart - Direct comparison of both models
|
||||
const inputs = this.calculator.getInputValues();
|
||||
|
||||
// Calculate loan model net profit (fixed return regardless of scenario)
|
||||
const loanMonthlyPayment = this.calculateLoanPayment(inputs.investmentAmount, inputs.loanInterestRate, inputs.timeframe);
|
||||
const loanTotalPayments = loanMonthlyPayment * (inputs.timeframe * 12);
|
||||
const loanNetProfit = loanTotalPayments - inputs.investmentAmount;
|
||||
// Get unique scenario names (without model suffix)
|
||||
const baseScenarios = ['conservative', 'moderate', 'aggressive'].filter(s =>
|
||||
this.calculator.scenarios[s].enabled
|
||||
);
|
||||
const comparisonLabels = baseScenarios.map(s => this.calculator.scenarios[s].name);
|
||||
|
||||
// Prepare scenario-based comparison
|
||||
const enabledScenarios = scenarios.filter(s => this.calculator.scenarios[s].enabled);
|
||||
const comparisonLabels = enabledScenarios.map(s => this.calculator.scenarios[s].name);
|
||||
|
||||
// Loan model data (same profit for all scenarios since it's fixed)
|
||||
const loanModelData = enabledScenarios.map(() => loanNetProfit);
|
||||
|
||||
// Direct investment data (varies by scenario performance)
|
||||
const directInvestmentData = enabledScenarios.map(scenario => {
|
||||
const scenarioResult = this.calculator.results[scenario];
|
||||
if (!scenarioResult) return 0;
|
||||
return scenarioResult.cspRevenue - inputs.investmentAmount;
|
||||
// Get net profit data for both models
|
||||
const directInvestmentData = baseScenarios.map(scenario => {
|
||||
const scenarioResult = this.calculator.results[scenario + '_direct'];
|
||||
return scenarioResult ? scenarioResult.netPosition : 0;
|
||||
});
|
||||
|
||||
// Performance bonus data (shows the additional revenue from performance bonuses)
|
||||
const performanceBonusData = enabledScenarios.map(scenario => {
|
||||
const monthlyData = this.calculator.monthlyData[scenario] || [];
|
||||
const totalPerformanceBonus = monthlyData.reduce((sum, month) => {
|
||||
// Calculate the bonus revenue (difference from standard share)
|
||||
const standardRevenue = month.monthlyRevenue * inputs.servalaShare;
|
||||
const actualServalaRevenue = month.servalaRevenue;
|
||||
const bonusAmount = Math.max(0, standardRevenue - actualServalaRevenue); // CSP gets this as bonus
|
||||
return sum + bonusAmount;
|
||||
}, 0);
|
||||
return totalPerformanceBonus;
|
||||
const loanInvestmentData = baseScenarios.map(scenario => {
|
||||
const scenarioResult = this.calculator.results[scenario + '_loan'];
|
||||
return scenarioResult ? scenarioResult.netPosition : 0;
|
||||
});
|
||||
|
||||
this.charts.modelComparison.data.labels = comparisonLabels;
|
||||
this.charts.modelComparison.data.datasets = [
|
||||
{
|
||||
label: `Loan Model (${(inputs.loanInterestRate * 100).toFixed(1)}% fixed return)`,
|
||||
data: loanModelData,
|
||||
backgroundColor: '#ffc107',
|
||||
label: 'Direct Investment Model',
|
||||
data: directInvestmentData,
|
||||
backgroundColor: baseScenarios.map(scenario => colors[scenario] + '80'),
|
||||
borderColor: baseScenarios.map(scenario => colors[scenario]),
|
||||
borderWidth: 2
|
||||
},
|
||||
{
|
||||
label: `Loan Model (${(inputs.loanInterestRate * 100).toFixed(1)}% fixed rate)`,
|
||||
data: loanInvestmentData,
|
||||
backgroundColor: '#ffc10780',
|
||||
borderColor: '#e0a800',
|
||||
borderWidth: 2
|
||||
},
|
||||
{
|
||||
label: 'Direct Investment (base return)',
|
||||
data: directInvestmentData,
|
||||
backgroundColor: enabledScenarios.map(scenario => colors[scenario] + '80'),
|
||||
borderColor: enabledScenarios.map(scenario => colors[scenario]),
|
||||
borderWidth: 2
|
||||
},
|
||||
{
|
||||
label: 'Performance Bonus Impact',
|
||||
data: performanceBonusData,
|
||||
backgroundColor: enabledScenarios.map(scenario => colors[scenario] + '40'),
|
||||
borderColor: enabledScenarios.map(scenario => colors[scenario]),
|
||||
borderWidth: 2,
|
||||
borderDash: [5, 5]
|
||||
}
|
||||
];
|
||||
|
||||
|
|
@ -275,29 +275,30 @@ class ChartManager {
|
|||
// Update Performance Comparison Chart (ROI comparison for both models)
|
||||
this.charts.performance.data.labels = comparisonLabels;
|
||||
|
||||
// Calculate ROI for loan model (same for all scenarios)
|
||||
const loanROI = (loanNetProfit / inputs.investmentAmount) * 100;
|
||||
const loanROIData = enabledScenarios.map(() => loanROI);
|
||||
// Get ROI data for both models
|
||||
const directROIData = baseScenarios.map(scenario => {
|
||||
const result = this.calculator.results[scenario + '_direct'];
|
||||
return result ? result.roi : 0;
|
||||
});
|
||||
|
||||
// Get ROI for direct investment (varies by scenario)
|
||||
const directROIData = enabledScenarios.map(scenario => {
|
||||
const result = this.calculator.results[scenario];
|
||||
const loanROIData = baseScenarios.map(scenario => {
|
||||
const result = this.calculator.results[scenario + '_loan'];
|
||||
return result ? result.roi : 0;
|
||||
});
|
||||
|
||||
this.charts.performance.data.datasets = [
|
||||
{
|
||||
label: `Loan Model ROI (${(inputs.loanInterestRate * 100).toFixed(1)}% fixed)`,
|
||||
data: loanROIData,
|
||||
backgroundColor: '#ffc107',
|
||||
borderColor: '#e0a800',
|
||||
label: 'Direct Investment ROI',
|
||||
data: directROIData,
|
||||
backgroundColor: baseScenarios.map(scenario => colors[scenario] + '80'),
|
||||
borderColor: baseScenarios.map(scenario => colors[scenario]),
|
||||
borderWidth: 2
|
||||
},
|
||||
{
|
||||
label: 'Direct Investment ROI',
|
||||
data: directROIData,
|
||||
backgroundColor: enabledScenarios.map(scenario => colors[scenario] + '80'),
|
||||
borderColor: enabledScenarios.map(scenario => colors[scenario]),
|
||||
label: `Loan Model ROI (${(inputs.loanInterestRate * 100).toFixed(1)}% fixed)`,
|
||||
data: loanROIData,
|
||||
backgroundColor: '#ffc10780',
|
||||
borderColor: '#e0a800',
|
||||
borderWidth: 2
|
||||
}
|
||||
];
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue