Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| """ | |
| A collection of functions to calculate key health and valuation metrics | |
| for a SaaS / micro-SaaS business based on a provided set of formulas. | |
| """ | |
| def calculate_arr(mrr: float) -> float: | |
| """ | |
| Calculates Annual Recurring Revenue (ARR) from Monthly Recurring Revenue (MRR). | |
| (Formula 1) | |
| Args: | |
| mrr: Monthly Recurring Revenue. | |
| Returns: | |
| The calculated Annual Recurring Revenue. | |
| """ | |
| return mrr * 12 | |
| def calculate_valuation_revenue(arr: float, multiple: float) -> float: | |
| """ | |
| Calculates a simple revenue-based valuation. | |
| (Formula 2) | |
| Args: | |
| arr: Annual Recurring Revenue. | |
| multiple: The valuation multiple (e.g., 3.0 for 3x). | |
| Returns: | |
| The calculated revenue-based valuation. | |
| """ | |
| return arr * multiple | |
| def calculate_sde( | |
| revenue: float, owner_comp: float, cogs: float, op_ex: float | |
| ) -> float: | |
| """ | |
| Calculates Seller's Discretionary Earnings (SDE). | |
| (Formula 3) | |
| Args: | |
| revenue: Total revenue for the period. | |
| owner_comp: Owner compensation to be added back. | |
| cogs: Cost of Goods Sold. | |
| op_ex: Operating Expenses. | |
| Returns: | |
| The calculated Seller's Discretionary Earnings. | |
| """ | |
| return (revenue + owner_comp) - (cogs + op_ex) | |
| def calculate_valuation_sde(sde: float, multiple: float) -> float: | |
| """ | |
| Calculates an SDE-based valuation. | |
| (Formula 3) | |
| Args: | |
| sde: Seller's Discretionary Earnings. | |
| multiple: The SDE valuation multiple. | |
| Returns: | |
| The calculated SDE-based valuation. | |
| """ | |
| return sde * multiple | |
| def calculate_valuation_ebitda(ebitda: float, multiple: float) -> float: | |
| """ | |
| Calculates an EBITDA-based valuation. | |
| (Formula 4) | |
| Args: | |
| ebitda: Earnings Before Interest, Taxes, Depreciation, and Amortization. | |
| multiple: The EBITDA valuation multiple. | |
| Returns: | |
| The calculated EBITDA-based valuation. | |
| """ | |
| return ebitda * multiple | |
| def calculate_customer_churn_rate( | |
| churned_customers: int, start_customers: int | |
| ) -> float: | |
| """ | |
| Calculates the customer churn rate for a period. | |
| (Formula 5) | |
| Args: | |
| churned_customers: Number of customers lost during the period. | |
| start_customers: Number of customers at the start of the period. | |
| Returns: | |
| The customer churn rate as a decimal (e.g., 0.05 for 5%). | |
| """ | |
| if start_customers == 0: | |
| return 0.0 | |
| return churned_customers / start_customers | |
| def calculate_gross_revenue_churn_rate( | |
| churned_revenue: float, mrr_start: float | |
| ) -> float: | |
| """ | |
| Calculates the gross revenue churn rate for a period. | |
| (Formula 6) | |
| Args: | |
| churned_revenue: MRR lost from cancellations and downgrades. | |
| mrr_start: MRR at the start of the period. | |
| Returns: | |
| The gross revenue churn rate as a decimal. | |
| """ | |
| if mrr_start == 0: | |
| return 0.0 | |
| return churned_revenue / mrr_start | |
| def calculate_net_revenue_churn_rate( | |
| churned_revenue: float, expansion_revenue: float, mrr_start: float | |
| ) -> float: | |
| """ | |
| Calculates the net revenue churn rate for a period. Can be negative. | |
| (Formula 7) | |
| Args: | |
| churned_revenue: MRR lost from cancellations and downgrades. | |
| expansion_revenue: MRR gained from upgrades and upsells. | |
| mrr_start: MRR at the start of the period. | |
| Returns: | |
| The net revenue churn rate as a decimal. | |
| """ | |
| if mrr_start == 0: | |
| return 0.0 | |
| return (churned_revenue - expansion_revenue) / mrr_start | |
| def calculate_nrr( | |
| mrr_start: float, expansion_revenue: float, churn_revenue: float | |
| ) -> float: | |
| """ | |
| Calculates Net Revenue Retention (NRR). | |
| Note: 'churn_revenue' here includes all lost revenue (cancellations + contractions). | |
| (Formula 8) | |
| Args: | |
| mrr_start: MRR at the start of the period. | |
| expansion_revenue: MRR gained from existing customers. | |
| churn_revenue: MRR lost from existing customers. | |
| Returns: | |
| The Net Revenue Retention rate as a decimal (e.g., 1.05 for 105%). | |
| """ | |
| if mrr_start == 0: | |
| return 0.0 | |
| return (mrr_start + expansion_revenue - churn_revenue) / mrr_start | |
| def calculate_cac(sm_spend: float, new_customers: int) -> float: | |
| """ | |
| Calculates Customer Acquisition Cost (CAC). | |
| (Formula 9) | |
| Args: | |
| sm_spend: Total sales and marketing spend for the period. | |
| new_customers: Number of new customers acquired in the period. | |
| Returns: | |
| The average Customer Acquisition Cost. | |
| """ | |
| if new_customers == 0: | |
| # Returning infinity is mathematically correct for 0 new customers | |
| # with non-zero spend, representing an infinitely high cost. | |
| return float("inf") if sm_spend > 0 else 0.0 | |
| return sm_spend / new_customers | |
| def calculate_arpa(mrr: float, active_accounts: int) -> float: | |
| """ | |
| Calculates Average Revenue Per Account (ARPA). | |
| (Formula 10) | |
| Args: | |
| mrr: Monthly Recurring Revenue. | |
| active_accounts: Number of active accounts. | |
| Returns: | |
| The Average Revenue Per Account. | |
| """ | |
| if active_accounts == 0: | |
| return 0.0 | |
| return mrr / active_accounts | |
| def calculate_customer_lifetime(customer_churn_rate: float) -> float: | |
| """ | |
| Calculates the approximate customer lifetime in periods. | |
| (Formula 11) | |
| Args: | |
| customer_churn_rate: The per-period customer churn rate as a decimal. | |
| Returns: | |
| The average customer lifetime in number of periods. | |
| """ | |
| if customer_churn_rate <= 0: | |
| # A churn rate of 0 implies infinite lifetime. | |
| return float("inf") | |
| return 1 / customer_churn_rate | |
| def calculate_ltv(arpa: float, gross_margin: float, revenue_churn_rate: float) -> float: | |
| """ | |
| Calculates the margin-adjusted Customer Lifetime Value (LTV). | |
| (Formula 12) | |
| Args: | |
| arpa: Average Revenue Per Account. | |
| gross_margin: The gross margin as a decimal (e.g., 0.80 for 80%). | |
| revenue_churn_rate: The revenue churn rate as a decimal. | |
| Returns: | |
| The margin-adjusted Customer Lifetime Value. | |
| """ | |
| if revenue_churn_rate <= 0: | |
| # A churn rate of 0 implies infinite LTV. | |
| return float("inf") | |
| return (arpa * gross_margin) / revenue_churn_rate | |
| def calculate_ltv_cac_ratio(ltv: float, cac: float) -> float: | |
| """ | |
| Calculates the LTV to CAC ratio. | |
| (Formula 13) | |
| Args: | |
| ltv: Customer Lifetime Value. | |
| cac: Customer Acquisition Cost. | |
| Returns: | |
| The LTV to CAC ratio. | |
| """ | |
| if cac == 0: | |
| # An LTV with zero acquisition cost is technically an infinite return. | |
| return float("inf") if ltv > 0 else 0.0 | |
| return ltv / cac | |
| def calculate_cac_payback_period(cac: float, arpa: float, gross_margin: float) -> float: | |
| """ | |
| Calculates the CAC payback period in months (assuming ARPA is monthly). | |
| (Formula 14) | |
| Args: | |
| cac: Customer Acquisition Cost. | |
| arpa: Average Revenue Per Account (monthly). | |
| gross_margin: Gross margin as a decimal. | |
| Returns: | |
| The number of months to pay back CAC. | |
| """ | |
| monthly_gross_profit_per_customer = arpa * gross_margin | |
| if monthly_gross_profit_per_customer <= 0: | |
| # If there's no gross profit, CAC is never paid back. | |
| return float("inf") | |
| return cac / monthly_gross_profit_per_customer | |
| def calculate_gross_margin(revenue: float, cogs: float) -> float: | |
| """ | |
| Calculates the gross margin. | |
| (Formula 15) | |
| Args: | |
| revenue: Total revenue. | |
| cogs: Cost of Goods Sold. | |
| Returns: | |
| The gross margin as a decimal. | |
| """ | |
| if revenue == 0: | |
| return 0.0 | |
| return (revenue - cogs) / revenue | |
| def calculate_yoy_growth(arr_current: float, arr_prior: float) -> float: | |
| """ | |
| Calculates Year-over-Year (YoY) growth rate. | |
| (Formula 16) | |
| Args: | |
| arr_current: ARR for the current year. | |
| arr_prior: ARR for the prior year. | |
| Returns: | |
| The YoY growth rate as a decimal. | |
| """ | |
| if arr_prior == 0: | |
| return float("inf") if arr_current > 0 else 0.0 | |
| return (arr_current - arr_prior) / arr_prior | |
| def calculate_rule_of_40(growth_rate: float, ebitda_margin: float) -> float: | |
| """ | |
| Calculates the Rule of 40 score. | |
| (Formula 17) | |
| Args: | |
| growth_rate: The growth rate as a percentage (e.g., 30 for 30%). | |
| ebitda_margin: The EBITDA margin as a percentage (e.g., 15 for 15%). | |
| Returns: | |
| The Rule of 40 score. | |
| """ | |
| return growth_rate + ebitda_margin | |
| # Helper function to format outputs nicely, handling inf | |
| def format_output(value: float) -> str: | |
| if value == float("inf"): | |
| return "∞ (Infinite)" | |
| elif value == float("-inf"): | |
| return "-∞ (Negative Infinite)" | |
| else: | |
| return f"{value:.2f}" | |
| # Create Gradio interfaces for each metric | |
| iface_arr = gr.Interface( | |
| fn=lambda mrr: format_output(calculate_arr(mrr)), | |
| inputs=[ | |
| gr.Number(label="Monthly Recurring Revenue (MRR)", value=0.0), | |
| ], | |
| outputs=gr.Textbox(label="Annual Recurring Revenue (ARR)"), | |
| title="Annual Recurring Revenue (ARR)", | |
| description=""" | |
| Annual Recurring Revenue (ARR) is a fundamental metric for SaaS businesses, representing the annualized value of your recurring revenue streams. It provides a clear picture of predictable yearly income from subscriptions, assuming no changes in customer base or pricing. ARR is calculated simply by multiplying the Monthly Recurring Revenue (MRR) by 12. This metric is crucial for forecasting, valuation, and investor reporting, as it normalizes revenue to an annual basis for easier comparison across businesses. | |
| Example: Imagine a SaaS company offering project management software with an MRR of $15,000 from 300 subscribers paying an average of $50/month. The ARR would be $15,000 * 12 = $180,000. This means the company is on track to generate $180,000 in recurring revenue over the next year if everything remains constant. If the company loses customers or gains upgrades, this would affect future MRR and thus ARR, highlighting the importance of retention and expansion strategies. | |
| """, | |
| ) | |
| iface_valuation_revenue = gr.Interface( | |
| fn=lambda arr, multiple: format_output(calculate_valuation_revenue(arr, multiple)), | |
| inputs=[ | |
| gr.Number(label="Annual Recurring Revenue (ARR)", value=0.0), | |
| gr.Number(label="Valuation Multiple (e.g., 3.0 for 3x)", value=3.0), | |
| ], | |
| outputs=gr.Textbox(label="Revenue-Based Valuation"), | |
| title="Revenue-Based Valuation", | |
| description=""" | |
| Revenue-Based Valuation estimates the worth of a SaaS business by multiplying its Annual Recurring Revenue (ARR) by a valuation multiple. The multiple typically ranges from 3x to 10x or more, depending on factors like growth rate, market size, profitability, and competitive landscape. This method is straightforward and commonly used for early-stage SaaS companies where recurring revenue is the primary value driver, but it doesn't account for costs or profitability directly. | |
| Example: A micro-SaaS tool for email automation has an ARR of $100,000 and is growing steadily in a competitive market, warranting a 5x multiple. The valuation would be $100,000 * 5 = $500,000. If the same company were in a high-growth niche with strong moats, the multiple might rise to 8x, valuing it at $800,000. This illustrates how investor perception of future potential influences the multiple, making it essential to benchmark against similar deals in the industry. | |
| """, | |
| ) | |
| iface_sde = gr.Interface( | |
| fn=lambda revenue, owner_comp, cogs, op_ex: format_output( | |
| calculate_sde(revenue, owner_comp, cogs, op_ex) | |
| ), | |
| inputs=[ | |
| gr.Number(label="Total Revenue for the Period", value=0.0), | |
| gr.Number(label="Owner Compensation (to add back)", value=0.0), | |
| gr.Number(label="Cost of Goods Sold (COGS)", value=0.0), | |
| gr.Number(label="Operating Expenses (OpEx)", value=0.0), | |
| ], | |
| outputs=gr.Textbox(label="Seller's Discretionary Earnings (SDE)"), | |
| title="Seller's Discretionary Earnings (SDE)", | |
| description=""" | |
| Seller's Discretionary Earnings (SDE) measures the true cash flow available to a business owner by adding back non-essential expenses, like owner compensation, to the net profit. It's calculated as (Revenue + Owner Compensation) - (COGS + Operating Expenses). SDE is particularly useful for small businesses and micro-SaaS, where the owner plays a central role, as it reflects the earnings potential for a new owner. This metric is often used in valuations for owner-operated businesses. | |
| Example: Consider a solo-developer SaaS app generating $200,000 in annual revenue, with the owner drawing a $50,000 salary (added back), $20,000 in COGS (hosting/server costs), and $30,000 in OpEx (marketing/tools). SDE = ($200,000 + $50,000) - ($20,000 + $30,000) = $200,000. This shows the business's earning power is $200,000, which a buyer could use as salary or reinvest, emphasizing how add-backs like personal expenses (e.g., home office deductions) can inflate SDE to attract buyers. | |
| """, | |
| ) | |
| iface_valuation_sde = gr.Interface( | |
| fn=lambda sde, multiple: format_output(calculate_valuation_sde(sde, multiple)), | |
| inputs=[ | |
| gr.Number(label="Seller's Discretionary Earnings (SDE)", value=0.0), | |
| gr.Number(label="SDE Valuation Multiple (e.g., 2.5)", value=2.5), | |
| ], | |
| outputs=gr.Textbox(label="SDE-Based Valuation"), | |
| title="SDE-Based Valuation", | |
| description=""" | |
| SDE-Based Valuation determines a business's worth by multiplying Seller's Discretionary Earnings (SDE) by a multiple, typically 2x to 4x for small SaaS businesses. The multiple accounts for risks, growth potential, and transferability. This approach is ideal for micro-SaaS or lifestyle businesses where SDE highlights owner benefits, providing a more personalized valuation than revenue multiples. | |
| Example: A content curation SaaS with SDE of $150,000 in a stable market might use a 3x multiple, yielding a valuation of $150,000 * 3 = $450,000. If the business has high customer dependency on the owner, the multiple could drop to 2x ($300,000), showing how factors like documentation and automation can increase the multiple by reducing buyer risk. | |
| """, | |
| ) | |
| iface_valuation_ebitda = gr.Interface( | |
| fn=lambda ebitda, multiple: format_output( | |
| calculate_valuation_ebitda(ebitda, multiple) | |
| ), | |
| inputs=[ | |
| gr.Number(label="EBITDA", value=0.0), | |
| gr.Number(label="EBITDA Valuation Multiple (e.g., 5.0)", value=5.0), | |
| ], | |
| outputs=gr.Textbox(label="EBITDA-Based Valuation"), | |
| title="EBITDA-Based Valuation", | |
| description=""" | |
| EBITDA-Based Valuation multiplies Earnings Before Interest, Taxes, Depreciation, and Amortization (EBITDA) by a multiple (often 4x-8x for SaaS) to estimate company value. EBITDA focuses on operational profitability, excluding non-operating costs, making it suitable for mature businesses. This method is widely used in M&A as it provides a clean view of earnings potential. | |
| Example: A SaaS platform for HR management with EBITDA of $300,000 and strong margins might command a 6x multiple, valuing it at $300,000 * 6 = $1,800,000. In contrast, a high-growth but less profitable startup with $100,000 EBITDA might get an 8x multiple ($800,000) due to future upside, demonstrating how growth prospects can outweigh current earnings in valuation. | |
| """, | |
| ) | |
| iface_customer_churn_rate = gr.Interface( | |
| fn=lambda churned_customers, start_customers: format_output( | |
| calculate_customer_churn_rate(churned_customers, start_customers) | |
| ), | |
| inputs=[ | |
| gr.Number(label="Churned Customers", value=0, precision=0), | |
| gr.Number(label="Customers at Start of Period", value=0, precision=0), | |
| ], | |
| outputs=gr.Textbox(label="Customer Churn Rate (as decimal)"), | |
| title="Customer Churn Rate", | |
| description=""" | |
| Customer Churn Rate measures the percentage of customers lost over a period, calculated as Churned Customers divided by Customers at the Start. Expressed as a decimal (e.g., 0.05 = 5%), it's a key indicator of customer satisfaction and product-market fit. High churn signals issues like poor onboarding or competition, while low churn indicates sticky products. | |
| Example: A fitness app starts the month with 1,000 users and loses 50 to competitors or inactivity. Churn Rate = 50 / 1,000 = 0.05 (5%). If the app improves features and reduces losses to 20 next month, the rate drops to 0.02 (2%), showing how retention efforts like email campaigns or feature updates can directly impact this metric and long-term revenue. | |
| """, | |
| ) | |
| iface_gross_revenue_churn_rate = gr.Interface( | |
| fn=lambda churned_revenue, mrr_start: format_output( | |
| calculate_gross_revenue_churn_rate(churned_revenue, mrr_start) | |
| ), | |
| inputs=[ | |
| gr.Number(label="Churned Revenue (MRR lost)", value=0.0), | |
| gr.Number(label="MRR at Start of Period", value=0.0), | |
| ], | |
| outputs=gr.Textbox(label="Gross Revenue Churn Rate (as decimal)"), | |
| title="Gross Revenue Churn Rate", | |
| description=""" | |
| Gross Revenue Churn Rate quantifies the revenue lost from cancellations and downgrades as a percentage of starting MRR, ignoring expansions. It's Churned Revenue / MRR at Start, as a decimal. This metric highlights revenue leakage, crucial for understanding stability in subscription models. Unlike customer churn, it weights losses by revenue value. | |
| Example: A video streaming SaaS has $50,000 MRR at month start and loses $2,500 from cancellations/downgrades. Gross Churn = $2,500 / $50,000 = 0.05 (5%). If high-value customers (e.g., enterprise plans) churn more, this rate spikes even if customer count churn is low, underscoring the need to focus retention on top-tier accounts. | |
| """, | |
| ) | |
| iface_net_revenue_churn_rate = gr.Interface( | |
| fn=lambda churned_revenue, expansion_revenue, mrr_start: format_output( | |
| calculate_net_revenue_churn_rate(churned_revenue, expansion_revenue, mrr_start) | |
| ), | |
| inputs=[ | |
| gr.Number(label="Churned Revenue (MRR lost)", value=0.0), | |
| gr.Number(label="Expansion Revenue (MRR gained from upgrades)", value=0.0), | |
| gr.Number(label="MRR at Start of Period", value=0.0), | |
| ], | |
| outputs=gr.Textbox(label="Net Revenue Churn Rate (as decimal)"), | |
| title="Net Revenue Churn Rate", | |
| description=""" | |
| Net Revenue Churn Rate accounts for both lost (churned) and gained (expansion) revenue from existing customers, as (Churned - Expansion) / MRR Start. It can be negative if expansions exceed losses, indicating revenue growth from the base. This metric provides a holistic view of customer value dynamics. | |
| Example: An e-learning platform with $40,000 starting MRR loses $3,000 in churn but gains $4,000 from upsells. Net Churn = ($3,000 - $4,000) / $40,000 = -0.025 (-2.5%). This negative rate means the customer base is expanding revenue organically, like when users upgrade to premium courses, offsetting losses and signaling strong product upsell potential. | |
| """, | |
| ) | |
| iface_nrr = gr.Interface( | |
| fn=lambda mrr_start, expansion_revenue, churn_revenue: format_output( | |
| calculate_nrr(mrr_start, expansion_revenue, churn_revenue) | |
| ), | |
| inputs=[ | |
| gr.Number(label="MRR at Start of Period", value=0.0), | |
| gr.Number(label="Expansion Revenue (MRR gained)", value=0.0), | |
| gr.Number(label="Churn Revenue (MRR lost)", value=0.0), | |
| ], | |
| outputs=gr.Textbox(label="Net Revenue Retention (NRR) (as decimal)"), | |
| title="Net Revenue Retention (NRR)", | |
| description=""" | |
| Net Revenue Retention (NRR) shows how much revenue from existing customers is retained and grown over a period, calculated as (Start MRR + Expansion - Churn) / Start MRR. Expressed as a decimal (e.g., 1.10 = 110%), NRR >1 indicates expansion, vital for SaaS scalability as it reduces reliance on new acquisitions. | |
| Example: A CRM SaaS starts with $100,000 MRR, adds $15,000 in expansions (add-ons), but loses $10,000 in churn. NRR = ($100,000 + $15,000 - $10,000) / $100,000 = 1.05 (105%). This means the existing base grew 5% net, like when customers adopt more features, allowing the company to invest less in sales while maintaining growth. | |
| """, | |
| ) | |
| iface_cac = gr.Interface( | |
| fn=lambda sm_spend, new_customers: format_output( | |
| calculate_cac(sm_spend, new_customers) | |
| ), | |
| inputs=[ | |
| gr.Number(label="Sales and Marketing Spend", value=0.0), | |
| gr.Number(label="New Customers Acquired", value=0, precision=0), | |
| ], | |
| outputs=gr.Textbox(label="Customer Acquisition Cost (CAC)"), | |
| title="Customer Acquisition Cost (CAC)", | |
| description=""" | |
| Customer Acquisition Cost (CAC) is the average cost to acquire a new customer, computed as Total Sales & Marketing Spend / New Customers. It includes ads, salaries, tools, etc. Low CAC relative to customer value indicates efficient growth; high CAC can signal marketing inefficiencies. | |
| Example: A social media analytics SaaS spends $20,000 on ads and content, acquiring 100 new users. CAC = $20,000 / 100 = $200. If competitors acquire similar users for $150, this suggests optimizing channels like SEO over paid ads; if spend is $0 but 50 users come organically, CAC = $0, highlighting the power of word-of-mouth. | |
| """, | |
| ) | |
| iface_arpa = gr.Interface( | |
| fn=lambda mrr, active_accounts: format_output(calculate_arpa(mrr, active_accounts)), | |
| inputs=[ | |
| gr.Number(label="Monthly Recurring Revenue (MRR)", value=0.0), | |
| gr.Number(label="Active Accounts", value=0, precision=0), | |
| ], | |
| outputs=gr.Textbox(label="Average Revenue Per Account (ARPA)"), | |
| title="Average Revenue Per Account (ARPA)", | |
| description=""" | |
| Average Revenue Per Account (ARPA) measures the average monthly revenue generated per customer, as MRR / Active Accounts. It reflects pricing effectiveness and customer segmentation. Rising ARPA often comes from upselling or premium tiers, while drops may indicate discounting or low-value users. | |
| Example: A cloud storage SaaS has $30,000 MRR from 500 users. ARPA = $30,000 / 500 = $60. If they introduce enterprise plans and ARPA rises to $75 with the same users, it shows successful tiering; conversely, adding many free-tier users could drop ARPA, prompting strategies to convert them to paid. | |
| """, | |
| ) | |
| iface_customer_lifetime = gr.Interface( | |
| fn=lambda customer_churn_rate: format_output( | |
| calculate_customer_lifetime(customer_churn_rate) | |
| ), | |
| inputs=[ | |
| gr.Number( | |
| label="Customer Churn Rate (as decimal, e.g., 0.05 for 5%)", value=0.0 | |
| ), | |
| ], | |
| outputs=gr.Textbox(label="Customer Lifetime (in periods)"), | |
| title="Customer Lifetime", | |
| description=""" | |
| Customer Lifetime estimates how long, on average, a customer stays with the business, calculated as 1 / Customer Churn Rate (in periods, e.g., months). It's key for predicting revenue stability and informing acquisition strategies. Longer lifetimes amplify lifetime value. | |
| Example: With a monthly churn rate of 0.04 (4%), Lifetime = 1 / 0.04 = 25 months. This means customers stay about 2 years; if churn drops to 0.02 via better support, lifetime doubles to 50 months, allowing more time to recover CAC and upsell, fundamentally improving business economics. | |
| """, | |
| ) | |
| iface_ltv = gr.Interface( | |
| fn=lambda arpa, gross_margin, revenue_churn_rate: format_output( | |
| calculate_ltv(arpa, gross_margin, revenue_churn_rate) | |
| ), | |
| inputs=[ | |
| gr.Number(label="Average Revenue Per Account (ARPA)", value=0.0), | |
| gr.Number(label="Gross Margin (as decimal, e.g., 0.80 for 80%)", value=0.8), | |
| gr.Number(label="Revenue Churn Rate (as decimal)", value=0.0), | |
| ], | |
| outputs=gr.Textbox(label="Customer Lifetime Value (LTV)"), | |
| title="Customer Lifetime Value (LTV)", | |
| description=""" | |
| Customer Lifetime Value (LTV) projects the total margin-adjusted revenue from a customer over their lifetime, as (ARPA * Gross Margin) / Revenue Churn Rate. It helps evaluate acquisition viability and pricing. High LTV supports aggressive growth; low LTV warns of unsustainable models. | |
| Example: A productivity app has ARPA $50, 85% margin, 0.03 revenue churn. LTV = ($50 * 0.85) / 0.03 ≈ $1,416.67. If CAC is $300, it's profitable; but if churn rises to 0.06, LTV halves to $708.33, potentially making acquisition unviable unless margins improve or churn is addressed. | |
| """, | |
| ) | |
| iface_ltv_cac_ratio = gr.Interface( | |
| fn=lambda ltv, cac: format_output(calculate_ltv_cac_ratio(ltv, cac)), | |
| inputs=[ | |
| gr.Number(label="Customer Lifetime Value (LTV)", value=0.0), | |
| gr.Number(label="Customer Acquisition Cost (CAC)", value=0.0), | |
| ], | |
| outputs=gr.Textbox(label="LTV to CAC Ratio"), | |
| title="LTV to CAC Ratio", | |
| description=""" | |
| LTV to CAC Ratio compares Customer Lifetime Value to Acquisition Cost, as LTV / CAC. A ratio >3 is healthy for SaaS, indicating good ROI on marketing. Below 1 signals losses; it's essential for scaling decisions. | |
| Example: With LTV $1,200 and CAC $400, Ratio = 3, meaning $3 revenue per $1 spent—solid for growth. If CAC rises to $600 due to ad competition, ratio drops to 2, prompting efficiency improvements like better targeting to restore balance. | |
| """, | |
| ) | |
| iface_cac_payback_period = gr.Interface( | |
| fn=lambda cac, arpa, gross_margin: format_output( | |
| calculate_cac_payback_period(cac, arpa, gross_margin) | |
| ), | |
| inputs=[ | |
| gr.Number(label="Customer Acquisition Cost (CAC)", value=0.0), | |
| gr.Number(label="Average Revenue Per Account (ARPA, monthly)", value=0.0), | |
| gr.Number(label="Gross Margin (as decimal)", value=0.8), | |
| ], | |
| outputs=gr.Textbox(label="CAC Payback Period (in months)"), | |
| title="CAC Payback Period", | |
| description=""" | |
| CAC Payback Period calculates months to recover acquisition costs from gross profits, as CAC / (ARPA * Gross Margin). Shorter periods (<12 months ideal) enable faster reinvestment. Long periods risk cash flow issues. | |
| Example: CAC $500, ARPA $100, 70% margin: Payback = $500 / ($100 * 0.7) ≈ 7.14 months. If margin drops to 50%, payback extends to 10 months, illustrating how cost control (e.g., reducing server expenses) shortens recovery and supports scaling. | |
| """, | |
| ) | |
| iface_gross_margin = gr.Interface( | |
| fn=lambda revenue, cogs: format_output(calculate_gross_margin(revenue, cogs)), | |
| inputs=[ | |
| gr.Number(label="Total Revenue", value=0.0), | |
| gr.Number(label="Cost of Goods Sold (COGS)", value=0.0), | |
| ], | |
| outputs=gr.Textbox(label="Gross Margin (as decimal)"), | |
| title="Gross Margin", | |
| description=""" | |
| Gross Margin is the percentage of revenue remaining after COGS, as (Revenue - COGS) / Revenue. High margins (70-90% for SaaS) indicate scalability, as fixed costs like software don't rise with users. Low margins suggest high variable costs. | |
| Example: Revenue $100,000, COGS $15,000 (hosting): Margin = ($100,000 - $15,000) / $100,000 = 0.85 (85%). If COGS rises to $30,000 from inefficient scaling, margin drops to 70%, pushing for optimizations like bulk hosting deals to maintain profitability. | |
| """, | |
| ) | |
| iface_yoy_growth = gr.Interface( | |
| fn=lambda arr_current, arr_prior: format_output( | |
| calculate_yoy_growth(arr_current, arr_prior) | |
| ), | |
| inputs=[ | |
| gr.Number(label="Current ARR", value=0.0), | |
| gr.Number(label="Prior ARR", value=0.0), | |
| ], | |
| outputs=gr.Textbox(label="YoY Growth Rate (as decimal)"), | |
| title="Year-over-Year (YoY) Growth Rate", | |
| description=""" | |
| Year-over-Year (YoY) Growth Rate measures annual revenue increase, as (Current ARR - Prior ARR) / Prior ARR. Positive growth attracts investors; stagnation or decline signals market saturation or issues. | |
| Example: Current ARR $240,000, Prior $200,000: Growth = ($240,000 - $200,000) / $200,000 = 0.20 (20%). If next year hits $300,000 (25% growth), it shows acceleration; from $0 to $100,000 is infinite, common in startups, emphasizing relative progress. | |
| """, | |
| ) | |
| iface_rule_of_40 = gr.Interface( | |
| fn=lambda growth_rate, ebitda_margin: format_output( | |
| calculate_rule_of_40(growth_rate, ebitda_margin) | |
| ), | |
| inputs=[ | |
| gr.Number(label="Growth Rate (as percentage, e.g., 30 for 30%)", value=0.0), | |
| gr.Number(label="EBITDA Margin (as percentage, e.g., 15 for 15%)", value=0.0), | |
| ], | |
| outputs=gr.Textbox(label="Rule of 40 Score"), | |
| title="Rule of 40", | |
| description=""" | |
| The Rule of 40 balances growth and profitability: Growth Rate (%) + EBITDA Margin (%). Scores >40 indicate healthy SaaS businesses, trading off high growth (even at losses) with profitability. | |
| Example: 50% growth, -10% EBITDA: Score = 40, acceptable for early-stage. 20% growth, 25% EBITDA: Score = 45, strong for mature. Below 40, like 10% growth and 20% margin (30), suggests needing growth boosts or cost cuts. | |
| """, | |
| ) | |
| # Combine into a tabbed interface | |
| demo = gr.TabbedInterface( | |
| [ | |
| iface_arr, | |
| iface_valuation_revenue, | |
| iface_sde, | |
| iface_valuation_sde, | |
| iface_valuation_ebitda, | |
| iface_customer_churn_rate, | |
| iface_gross_revenue_churn_rate, | |
| iface_net_revenue_churn_rate, | |
| iface_nrr, | |
| iface_cac, | |
| iface_arpa, | |
| iface_customer_lifetime, | |
| iface_ltv, | |
| iface_ltv_cac_ratio, | |
| iface_cac_payback_period, | |
| iface_gross_margin, | |
| iface_yoy_growth, | |
| iface_rule_of_40, | |
| ], | |
| [ | |
| "ARR", | |
| "Revenue Valuation", | |
| "SDE", | |
| "SDE Valuation", | |
| "EBITDA Valuation", | |
| "Customer Churn Rate", | |
| "Gross Revenue Churn", | |
| "Net Revenue Churn", | |
| "NRR", | |
| "CAC", | |
| "ARPA", | |
| "Customer Lifetime", | |
| "LTV", | |
| "LTV:CAC Ratio", | |
| "CAC Payback", | |
| "Gross Margin", | |
| "YoY Growth", | |
| "Rule of 40", | |
| ], | |
| title="SaaS Metrics Explained", | |
| theme=gr.themes.Default(), | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() | |