redone investment scaling logic for incentiving higher investments

This commit is contained in:
Tobias Brunner 2025-07-23 19:58:21 +02:00
parent 59c9fff27d
commit 82e362b532
Signed by: tobru
SSH key fingerprint: SHA256:kOXg1R6c11XW3/Pt9dbLdQvOJGFAy+B2K6v6PtRWBGQ
4 changed files with 283 additions and 45 deletions

View file

@ -135,30 +135,60 @@ class ROICalculator {
}
// Enhanced investment scaling factor (only for direct investment)
// Progressive scaling provides clear incentives for larger investments
// Exponential scaling provides strong incentives for larger investments
const baseInvestment = 500000;
let investmentScaleFactor;
let churnReductionFactor;
let revenueMultiplier;
let acceleratedBreakEvenFactor;
if (inputs.investmentModel === 'loan') {
investmentScaleFactor = 1.0;
churnReductionFactor = 1.0;
revenueMultiplier = 1.0;
acceleratedBreakEvenFactor = 1.0;
} else {
// Progressive linear scaling with clear breakpoints
// Exponential scaling for better ROI at higher investments
if (inputs.investmentAmount <= baseInvestment) {
investmentScaleFactor = inputs.investmentAmount / baseInvestment;
} else if (inputs.investmentAmount <= 1000000) {
// 500k to 1M: Scale from 1.0x to 1.5x
investmentScaleFactor = 1.0 + (inputs.investmentAmount - baseInvestment) / 1000000;
// 500k to 1M: Exponential scaling (1.0x to 2.2x)
const ratio = inputs.investmentAmount / baseInvestment;
investmentScaleFactor = Math.pow(ratio, 1.2);
} else if (inputs.investmentAmount <= 1500000) {
// 1M to 1.5M: Enhanced exponential scaling (2.2x to 3.5x)
const ratio = inputs.investmentAmount / baseInvestment;
investmentScaleFactor = Math.pow(ratio, 1.3);
} else {
// 1M to 2M: Scale from 1.5x to 2.0x
investmentScaleFactor = 1.5 + (inputs.investmentAmount - 1000000) / 2000000;
// 1.5M to 2M: Maximum exponential scaling (3.5x to 5.0x)
const ratio = inputs.investmentAmount / baseInvestment;
investmentScaleFactor = Math.pow(ratio, 1.4);
}
// Enhanced churn reduction with better scaling
// Higher investment = better customer success = lower churn
// Revenue multipliers for large investments (economies of scale)
if (inputs.investmentAmount >= 2000000) {
revenueMultiplier = 1.6; // 60% premium per instance
} else if (inputs.investmentAmount >= 1500000) {
revenueMultiplier = 1.4; // 40% premium per instance
} else if (inputs.investmentAmount >= 1000000) {
revenueMultiplier = 1.2; // 20% premium per instance
} else {
revenueMultiplier = 1.0; // No premium
}
// Accelerated break-even for large investments
if (inputs.investmentAmount >= 1500000) {
acceleratedBreakEvenFactor = 1.4; // 40% faster break-even
} else if (inputs.investmentAmount >= 1000000) {
acceleratedBreakEvenFactor = 1.2; // 20% faster break-even
} else {
acceleratedBreakEvenFactor = 1.0; // Standard break-even
}
// Enhanced churn reduction with aggressive scaling
// Higher investment = significantly better customer success = much lower churn
const churnReductionRatio = Math.min((inputs.investmentAmount - baseInvestment) / 1500000, 1.0);
churnReductionFactor = Math.max(0.6, 1 - (churnReductionRatio * 0.4));
churnReductionFactor = Math.max(0.4, 1 - (churnReductionRatio * 0.6)); // Up to 60% churn reduction
}
// Calculate adjusted churn rate with investment-based reduction
@ -171,11 +201,18 @@ class ROICalculator {
let cumulativeServalaRevenue = 0;
let breakEvenMonth = null;
// Calculate dynamic grace period based on investment size
// Calculate enhanced grace period based on investment size
const baseGracePeriod = inputs.gracePeriod;
const gracePeriodBonus = Math.floor((inputs.investmentAmount - baseInvestment) / 250000);
let gracePeriodBonus;
if (inputs.investmentAmount >= 1500000) {
gracePeriodBonus = 12; // 12 months extra for large investments
} else if (inputs.investmentAmount >= 1000000) {
gracePeriodBonus = 6; // 6 months extra for medium investments
} else {
gracePeriodBonus = Math.floor((inputs.investmentAmount - baseInvestment) / 250000);
}
const effectiveGracePeriod = inputs.investmentModel === 'loan' ? 0 :
Math.min(baseGracePeriod + gracePeriodBonus, Math.floor(totalMonths / 2));
Math.min(baseGracePeriod + gracePeriodBonus, Math.floor(totalMonths * 0.6)); // Up to 60% of timeframe
// Track baseline performance for performance bonuses (direct investment only)
let baselineInstances = 0; // Will track expected instances without performance scaling
@ -217,15 +254,31 @@ class ROICalculator {
} else {
// Direct investment model: Revenue based on instances with performance incentives
// Service revenue (shared with Servala) + Core service revenue (100% to CSP)
const serviceRevenue = currentInstances * inputs.revenuePerInstance;
const coreRevenue = currentInstances * inputs.coreServiceRevenue;
const baseServiceRevenue = currentInstances * inputs.revenuePerInstance;
const baseCoreRevenue = currentInstances * inputs.coreServiceRevenue;
// Apply revenue multiplier for large investments (economies of scale)
const serviceRevenue = baseServiceRevenue * revenueMultiplier;
const coreRevenue = baseCoreRevenue * revenueMultiplier;
monthlyRevenue = serviceRevenue;
// Calculate scaled performance bonus based on investment size
let maxPerformanceBonus;
if (inputs.investmentAmount >= 2000000) {
maxPerformanceBonus = 0.35; // 35% max bonus for largest investments
} else if (inputs.investmentAmount >= 1500000) {
maxPerformanceBonus = 0.25; // 25% max bonus for large investments
} else if (inputs.investmentAmount >= 1000000) {
maxPerformanceBonus = 0.20; // 20% max bonus for medium investments
} else {
maxPerformanceBonus = 0.15; // 15% max bonus for base investments
}
// Calculate performance bonus if CSP exceeds baseline expectations
if (baselineInstances > 0 && month > 6) { // Start performance tracking after 6 months
const performanceRatio = currentInstances / Math.max(baselineInstances, 1);
if (performanceRatio > 1.1) { // 10% threshold for performance bonus
performanceBonus = Math.max(0, Math.min(0.15, (performanceRatio - 1.1) * 0.3)); // Up to 15% bonus
performanceBonus = Math.max(0, Math.min(maxPerformanceBonus, (performanceRatio - 1.1) * 0.5));
adjustedServalaShare = Math.max(0.10, inputs.servalaShare - performanceBonus);
}
}
@ -246,15 +299,17 @@ class ROICalculator {
cumulativeCSPRevenue += cspRevenue;
cumulativeServalaRevenue += servalaRevenue;
// Enhanced break-even calculation
// Enhanced break-even calculation with acceleration for large investments
let netPosition; // CSP's net financial position
if (inputs.investmentModel === 'loan') {
// For loan model: net position is cumulative payments received minus loan principal outstanding
const principalPaid = inputs.investmentAmount * (month / totalMonths); // Simplified principal tracking
netPosition = cumulativeCSPRevenue - (inputs.investmentAmount - principalPaid);
} else {
// For direct investment: net position is cumulative revenue minus initial investment
netPosition = cumulativeCSPRevenue - inputs.investmentAmount;
// For direct investment: accelerated break-even for large investments
// Large investments benefit from faster effective cost recovery
const adjustedInvestment = inputs.investmentAmount / acceleratedBreakEvenFactor;
netPosition = cumulativeCSPRevenue - adjustedInvestment;
}
if (breakEvenMonth === null && netPosition >= 0) {
@ -304,10 +359,16 @@ class ROICalculator {
// Calculate final metrics with enhanced business intelligence
const totalRevenue = cumulativeCSPRevenue + cumulativeServalaRevenue;
const finalNetPosition = inputs.investmentModel === 'loan' ?
cumulativeCSPRevenue - inputs.investmentAmount :
cumulativeCSPRevenue - inputs.investmentAmount;
const roi = (finalNetPosition / inputs.investmentAmount) * 100;
let finalNetPosition, roi;
if (inputs.investmentModel === 'loan') {
finalNetPosition = cumulativeCSPRevenue - inputs.investmentAmount;
roi = (finalNetPosition / inputs.investmentAmount) * 100;
} else {
// For direct investment: use accelerated break-even for final ROI calculation
const adjustedInvestment = inputs.investmentAmount / acceleratedBreakEvenFactor;
finalNetPosition = cumulativeCSPRevenue - adjustedInvestment;
roi = (finalNetPosition / inputs.investmentAmount) * 100;
}
// Calculate average performance bonus over the investment period
const performanceBonusMonths = monthlyData.filter(m => m.performanceBonus > 0);
@ -329,6 +390,12 @@ class ROICalculator {
avgPerformanceBonus,
monthlyData,
investmentScaleFactor: investmentScaleFactor,
revenueMultiplier: revenueMultiplier,
acceleratedBreakEvenFactor: acceleratedBreakEvenFactor,
maxPerformanceBonus: inputs.investmentModel === 'direct' ?
(inputs.investmentAmount >= 2000000 ? 35 :
inputs.investmentAmount >= 1500000 ? 25 :
inputs.investmentAmount >= 1000000 ? 20 : 15) : 0,
adjustedChurnRate: adjustedChurnRate * 100,
performanceMultiplier: baselineInstances > 0 ? (currentInstances / baselineInstances) : 1.0
};

View file

@ -122,6 +122,97 @@ class ROICalculatorApp {
updateCalculations() {
if (this.calculator) {
this.calculator.updateCalculations();
this.updateInvestmentBenefits();
}
}
updateInvestmentBenefits() {
try {
const inputs = this.calculator.getInputValues();
const investmentAmount = inputs.investmentAmount;
const baseInvestment = 500000;
// Calculate instance scaling factor
let scalingFactor;
if (investmentAmount <= baseInvestment) {
scalingFactor = investmentAmount / baseInvestment;
} else if (investmentAmount <= 1000000) {
const ratio = investmentAmount / baseInvestment;
scalingFactor = Math.pow(ratio, 1.2);
} else if (investmentAmount <= 1500000) {
const ratio = investmentAmount / baseInvestment;
scalingFactor = Math.pow(ratio, 1.3);
} else {
const ratio = investmentAmount / baseInvestment;
scalingFactor = Math.pow(ratio, 1.4);
}
// Calculate revenue premium
let revenuePremium;
if (investmentAmount >= 2000000) {
revenuePremium = 60;
} else if (investmentAmount >= 1500000) {
revenuePremium = 40;
} else if (investmentAmount >= 1000000) {
revenuePremium = 20;
} else {
revenuePremium = 0;
}
// Calculate grace period
const baseGracePeriod = inputs.gracePeriod;
let gracePeriodBonus;
if (investmentAmount >= 1500000) {
gracePeriodBonus = 12;
} else if (investmentAmount >= 1000000) {
gracePeriodBonus = 6;
} else {
gracePeriodBonus = Math.floor((investmentAmount - baseInvestment) / 250000);
}
const totalGracePeriod = Math.max(0, baseGracePeriod + gracePeriodBonus);
// Calculate max performance bonus
let maxBonus;
if (investmentAmount >= 2000000) {
maxBonus = 35;
} else if (investmentAmount >= 1500000) {
maxBonus = 25;
} else if (investmentAmount >= 1000000) {
maxBonus = 20;
} else {
maxBonus = 15;
}
// Update UI elements
const instanceScaling = document.getElementById('instance-scaling');
if (instanceScaling) {
instanceScaling.textContent = scalingFactor.toFixed(1) + 'x';
instanceScaling.className = scalingFactor >= 2.0 ? 'benefit-value text-success fw-bold' :
scalingFactor >= 1.5 ? 'benefit-value text-primary fw-bold' : 'benefit-value text-secondary fw-bold';
}
const revenuePremiumEl = document.getElementById('revenue-premium');
if (revenuePremiumEl) {
revenuePremiumEl.textContent = '+' + revenuePremium + '%';
revenuePremiumEl.className = revenuePremium >= 40 ? 'benefit-value text-success fw-bold' :
revenuePremium >= 20 ? 'benefit-value text-warning fw-bold' : 'benefit-value text-secondary fw-bold';
}
const gracePeriodEl = document.getElementById('grace-period-display');
if (gracePeriodEl) {
gracePeriodEl.textContent = totalGracePeriod + ' months';
gracePeriodEl.className = totalGracePeriod >= 12 ? 'benefit-value text-success fw-bold' :
totalGracePeriod >= 9 ? 'benefit-value text-info fw-bold' : 'benefit-value text-secondary fw-bold';
}
const maxBonusEl = document.getElementById('max-bonus');
if (maxBonusEl) {
maxBonusEl.textContent = maxBonus + '%';
maxBonusEl.className = maxBonus >= 30 ? 'benefit-value text-success fw-bold' :
maxBonus >= 20 ? 'benefit-value text-warning fw-bold' : 'benefit-value text-secondary fw-bold';
}
} catch (error) {
console.error('Error updating investment benefits:', error);
}
}