From 626badffe9ad4f9869eacfdc700ec09275feec62 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 21 Jul 2025 17:06:29 +0200 Subject: [PATCH] introduce investment models --- hub/services/static/js/roi-calculator.js | 111 +++++++++++++++--- .../calculator/csp_roi_calculator.html | 97 +++++++++++++-- 2 files changed, 181 insertions(+), 27 deletions(-) diff --git a/hub/services/static/js/roi-calculator.js b/hub/services/static/js/roi-calculator.js index 0b21760..4a3b36f 100644 --- a/hub/services/static/js/roi-calculator.js +++ b/hub/services/static/js/roi-calculator.js @@ -48,9 +48,12 @@ class ROICalculator { } getInputValues() { + const investmentModel = document.querySelector('input[name="investment-model"]:checked').value; return { investmentAmount: parseFloat(document.getElementById('investment-amount').getAttribute('data-value')), timeframe: parseInt(document.getElementById('timeframe').value), + investmentModel: investmentModel, + loanInterestRate: parseFloat(document.getElementById('loan-interest-rate').value) / 100, revenuePerInstance: parseFloat(document.getElementById('revenue-per-instance').value), servalaShare: parseFloat(document.getElementById('servala-share').value) / 100, gracePeriod: parseInt(document.getElementById('grace-period').value) @@ -61,15 +64,30 @@ class ROICalculator { const scenario = this.scenarios[scenarioKey]; if (!scenario.enabled) return null; - // Calculate investment scaling factor + // Calculate loan payment if using loan model + let monthlyLoanPayment = 0; + if (inputs.investmentModel === 'loan') { + const monthlyRate = inputs.loanInterestRate / 12; + const numPayments = inputs.timeframe * 12; + // Calculate fixed monthly payment using amortization formula + if (monthlyRate > 0) { + monthlyLoanPayment = inputs.investmentAmount * + (monthlyRate * Math.pow(1 + monthlyRate, numPayments)) / + (Math.pow(1 + monthlyRate, numPayments) - 1); + } else { + monthlyLoanPayment = inputs.investmentAmount / numPayments; + } + } + + // Calculate investment scaling factor (only for direct investment) // Base investment of CHF 500,000 = 1.0x multiplier // Higher investments get multiplicative benefits for instance acquisition const baseInvestment = 500000; - const investmentScaleFactor = Math.sqrt(inputs.investmentAmount / baseInvestment); + const investmentScaleFactor = inputs.investmentModel === 'loan' ? 1.0 : Math.sqrt(inputs.investmentAmount / baseInvestment); - // Calculate churn reduction factor based on investment + // Calculate churn reduction factor based on investment (only for direct investment) // Higher investment = better customer success = lower churn - const churnReductionFactor = Math.max(0.7, 1 - (inputs.investmentAmount - baseInvestment) / 2000000 * 0.3); + const churnReductionFactor = inputs.investmentModel === 'loan' ? 1.0 : Math.max(0.7, 1 - (inputs.investmentAmount - baseInvestment) / 2000000 * 0.3); // Calculate adjusted churn rate with investment-based reduction const adjustedChurnRate = scenario.churnRate * churnReductionFactor; @@ -102,17 +120,26 @@ class ROICalculator { // Update total instances currentInstances = currentInstances + newInstances - churnedInstances; - // Calculate revenue - const monthlyRevenue = currentInstances * inputs.revenuePerInstance; - - // Determine revenue split based on grace period - let cspRevenue, servalaRevenue; - if (month <= inputs.gracePeriod) { - cspRevenue = monthlyRevenue; + // Calculate revenue based on investment model + let cspRevenue, servalaRevenue, monthlyRevenue; + + if (inputs.investmentModel === 'loan') { + // Loan model: CSP receives fixed monthly loan payment + cspRevenue = monthlyLoanPayment; servalaRevenue = 0; + monthlyRevenue = monthlyLoanPayment; } else { - cspRevenue = monthlyRevenue * (1 - inputs.servalaShare); - servalaRevenue = monthlyRevenue * inputs.servalaShare; + // Direct investment model: Revenue based on instances + monthlyRevenue = currentInstances * inputs.revenuePerInstance; + + // Determine revenue split based on grace period + if (month <= inputs.gracePeriod) { + cspRevenue = monthlyRevenue; + servalaRevenue = 0; + } else { + cspRevenue = monthlyRevenue * (1 - inputs.servalaShare); + servalaRevenue = monthlyRevenue * inputs.servalaShare; + } } // Update cumulative revenue @@ -148,6 +175,7 @@ class ROICalculator { return { scenario: scenario.name, + investmentModel: inputs.investmentModel, finalInstances: currentInstances, totalRevenue, cspRevenue: cumulativeCSPRevenue, @@ -341,9 +369,14 @@ class ROICalculator { tbody.innerHTML = ''; Object.values(this.results).forEach(result => { + const modelLabel = result.investmentModel === 'loan' ? + 'Loan' : + 'Direct'; + const row = tbody.insertRow(); row.innerHTML = ` ${result.scenario} + ${modelLabel} ${result.finalInstances.toLocaleString()} ${this.formatCurrencyDetailed(result.totalRevenue)} ${this.formatCurrencyDetailed(result.cspRevenue)} @@ -690,9 +723,13 @@ function exportToPDF() { const params = [ ['Investment Amount:', calculator.formatCurrencyDetailed(inputs.investmentAmount)], ['Investment Timeframe:', `${inputs.timeframe} years`], + ['Investment Model:', inputs.investmentModel === 'loan' ? 'Loan Model' : 'Direct Investment'], + ...(inputs.investmentModel === 'loan' ? [['Loan Interest Rate:', `${(inputs.loanInterestRate * 100).toFixed(1)}%`]] : []), ['Revenue per Instance:', calculator.formatCurrencyDetailed(inputs.revenuePerInstance)], - ['Servala Revenue Share:', `${(inputs.servalaShare * 100).toFixed(0)}%`], - ['Grace Period:', `${inputs.gracePeriod} months`] + ...(inputs.investmentModel === 'direct' ? [ + ['Servala Revenue Share:', `${(inputs.servalaShare * 100).toFixed(0)}%`], + ['Grace Period:', `${inputs.gracePeriod} months`] + ] : []) ]; params.forEach(([label, value]) => { @@ -808,16 +845,24 @@ function exportToCSV() { const inputs = calculator.getInputValues(); csvContent += `Investment Amount,${inputs.investmentAmount}\n`; csvContent += `Timeframe (years),${inputs.timeframe}\n`; + csvContent += `Investment Model,${inputs.investmentModel === 'loan' ? 'Loan Model' : 'Direct Investment'}\n`; + if (inputs.investmentModel === 'loan') { + csvContent += `Loan Interest Rate (%),${(inputs.loanInterestRate * 100).toFixed(1)}\n`; + } csvContent += `Revenue per Instance,${inputs.revenuePerInstance}\n`; - csvContent += `Servala Share (%),${(inputs.servalaShare * 100).toFixed(0)}\n`; - csvContent += `Grace Period (months),${inputs.gracePeriod}\n\n`; + if (inputs.investmentModel === 'direct') { + csvContent += `Servala Share (%),${(inputs.servalaShare * 100).toFixed(0)}\n`; + csvContent += `Grace Period (months),${inputs.gracePeriod}\n`; + } + csvContent += '\n'; // Add scenario summary csvContent += 'SCENARIO SUMMARY\n'; - csvContent += 'Scenario,Final Instances,Total Revenue,CSP Revenue,Servala Revenue,ROI (%),Break-even (months)\n'; + csvContent += 'Scenario,Investment Model,Final Instances,Total Revenue,CSP Revenue,Servala Revenue,ROI (%),Break-even (months)\n'; Object.values(calculator.results).forEach(result => { - csvContent += `${result.scenario},${result.finalInstances},${result.totalRevenue.toFixed(2)},${result.cspRevenue.toFixed(2)},${result.servalaRevenue.toFixed(2)},${result.roi.toFixed(2)},${result.breakEvenMonth || 'N/A'}\n`; + const modelText = result.investmentModel === 'loan' ? 'Loan' : 'Direct'; + csvContent += `${result.scenario},${modelText},${result.finalInstances},${result.totalRevenue.toFixed(2)},${result.cspRevenue.toFixed(2)},${result.servalaRevenue.toFixed(2)},${result.roi.toFixed(2)},${result.breakEvenMonth || 'N/A'}\n`; }); csvContent += '\n'; @@ -872,6 +917,9 @@ function resetCalculator() { investmentInput.value = '500,000'; document.getElementById('investment-slider').value = 500000; document.getElementById('timeframe').value = 3; + document.getElementById('direct-model').checked = true; + document.getElementById('loan-interest-rate').value = 5.0; + document.getElementById('loan-rate-slider').value = 5.0; document.getElementById('revenue-per-instance').value = 50; document.getElementById('revenue-slider').value = 50; document.getElementById('servala-share').value = 25; @@ -890,11 +938,36 @@ function resetCalculator() { // Reset advanced parameters resetAdvancedParameters(); + // Reset investment model toggle + toggleInvestmentModel(); + // Recalculate (this will be called by resetAdvancedParameters, but we ensure it happens) updateCalculations(); } } +// Investment model toggle functions +function toggleInvestmentModel() { + const selectedModel = document.querySelector('input[name="investment-model"]:checked').value; + const loanSection = document.getElementById('loan-rate-section'); + const modelDescription = document.getElementById('model-description'); + + if (selectedModel === 'loan') { + loanSection.style.display = 'block'; + modelDescription.textContent = 'Loan Model: Guaranteed returns with fixed monthly payments'; + } else { + loanSection.style.display = 'none'; + modelDescription.textContent = 'Direct Investment: Higher potential returns based on your sales performance'; + } + + updateCalculations(); +} + +function updateLoanRate(value) { + document.getElementById('loan-interest-rate').value = value; + updateCalculations(); +} + // Logout function function logout() { if (confirm('Are you sure you want to logout?')) { diff --git a/hub/services/templates/calculator/csp_roi_calculator.html b/hub/services/templates/calculator/csp_roi_calculator.html index c918bae..c2a7975 100644 --- a/hub/services/templates/calculator/csp_roi_calculator.html +++ b/hub/services/templates/calculator/csp_roi_calculator.html @@ -56,14 +56,22 @@
Calculator Overview
-

This ROI calculator models your investment in the Servala platform by simulating cloud service provider (CSP) business growth over time. It calculates potential returns based on instance growth, churn rates, and revenue sharing with Servala.

+

This ROI calculator models your investment in the Servala platform by simulating cloud service provider (CSP) business growth over time. Choose between lending to Servala (guaranteed returns) or direct platform investment (higher potential returns based on your sales performance).

Key Parameters
-

Investment Amount: Your initial capital investment in the Servala platform and infrastructure.

+

Investment Model: Loan to Servala (fixed returns) vs Direct Investment (performance-based returns).

+

Investment Amount: Your capital provided to Servala for platform development and infrastructure.

Monthly Revenue per Instance: The recurring revenue generated from each managed service instance (excl. compute).

-

Servala Revenue Share: Percentage of revenue shared with Servala after the grace period. This is Servala's platform fee.

-

Grace Period: Initial months where you keep 100% of revenue before sharing begins with Servala.

+

Servala Revenue Share: Percentage of revenue shared with Servala after the grace period (Direct Investment only).

+

Grace Period: Initial months where you keep 100% of revenue before sharing begins with Servala (Direct Investment only).

+

Loan Interest Rate: Annual interest rate on loan to Servala (Loan Model only).

+
+ +
Investment Models
+
+

Loan Model: Lend to Servala at fixed interest rate. Lower risk, guaranteed returns, but limited upside.

+

Direct Investment: Invest directly in platform operations. Higher risk, but unlimited upside based on your sales performance during grace period and beyond.

Growth Scenarios
@@ -79,6 +87,7 @@

ROI: Return on Investment as a percentage of your initial investment.

Break-even: Month when cumulative revenue equals your initial investment.

Churn: Monthly percentage of instances that stop generating revenue (customer loss).

+

Grace Period Advantage: Direct Investment model benefits significantly from aggressive sales during grace period (100% revenue retention).

@@ -147,6 +156,52 @@ + +
+ +
+ + + + +
+ + Direct Investment: Higher potential returns based on your sales performance + +
+ + + +
@@ -164,7 +219,14 @@
- + @@ -173,11 +235,18 @@ min="10" max="40" step="1" value="25" onchange="updateServalaShare(this.value)">
- 10% - 40% + 10% - 40% (Direct Investment only)
- + @@ -186,7 +255,7 @@ min="0" max="24" step="1" value="6" onchange="updateGracePeriod(this.value)">
- 0 - 24 months (100% revenue to CSP) + 0 - 24 months (100% revenue to CSP - Direct Investment only) @@ -482,6 +551,17 @@ + +
+
+
+
Investment Model Comparison
+

Compare guaranteed returns from loan model vs performance-based returns from direct investment. Grace period highlighted to show advantage of aggressive sales during 100% revenue retention period.

+ +
+
+
+
@@ -492,6 +572,7 @@ Scenario + Investment Model Final Instances Total Revenue CSP Revenue