Revenue Forecasting for ChatGPT Apps: Financial Planning Guide

Revenue forecasting transforms your ChatGPT app from a development project into a predictable business engine. While traditional SaaS forecasting relies on historical trends, ChatGPT apps face unique dynamics: usage-based pricing, API cost variability, and rapid customer growth from 800 million ChatGPT users. This comprehensive guide provides production-ready systems for tracking MRR/ARR, analyzing cohorts, modeling expansion revenue, and building financial dashboards that investors and stakeholders demand.

Building a ChatGPT app without revenue forecasting is like flying blind. You can't optimize pricing, predict cash flow, or make strategic decisions about marketing spend without understanding your unit economics. Whether you're pre-revenue planning your first launch or scaling to $100K MRR, the forecasting models and analytics systems in this guide give you the financial visibility required for sustainable growth.

MakeAIHQ provides the fastest path from idea to deployed ChatGPT app, but monetization success requires rigorous financial planning. This guide shows you how to implement enterprise-grade revenue forecasting specifically designed for ChatGPT apps with subscription models, usage-based pricing, and multi-tier plans. Every code example is production-ready and battle-tested at scale.

SaaS Metrics Foundation

Monthly Recurring Revenue (MRR) is the heartbeat of your ChatGPT app business. It represents normalized monthly subscription revenue, excluding one-time fees and usage overages. Calculate MRR by summing all active subscriptions normalized to 30 days. For annual plans, divide by 12. For usage-based revenue, use trailing 30-day average. MRR becomes your north star metric, tracking business health more reliably than total revenue because it isolates recurring patterns from seasonal spikes.

Annual Recurring Revenue (ARR) is MRR multiplied by 12, used primarily for strategic planning and investor reporting. ARR helps you model long-term growth, calculate runway, and set annual targets. However, ARR can be misleading for early-stage apps with high churn. Focus on MRR until you achieve $1M ARR with stable retention.

Average Revenue Per User (ARPU) measures how much each customer pays monthly. Calculate as MRR divided by total active customers. ARPU reveals pricing effectiveness and segmentation opportunities. If ARPU is $49 but your highest tier is $299, you're undermonetizing. Track ARPU by cohort, acquisition channel, and customer segment to identify expansion opportunities.

Customer Lifetime Value (LTV) predicts total revenue from a customer over their entire relationship with your app. The simple formula is ARPU divided by churn rate. If ARPU is $149 and monthly churn is 5%, LTV is $2,980. However, this assumes constant churn and no expansion revenue. The advanced LTV model accounts for contraction, expansion, and changing retention curves over time.

Customer Acquisition Cost (CAC) is total sales and marketing spend divided by new customers acquired. Include all costs: advertising, content marketing, sales salaries, tools, and attribution. If you spend $10,000 on marketing and acquire 50 customers, CAC is $200. The golden rule is LTV should be at least 3x CAC. If LTV is $2,980 and CAC is $200, your ratio is 14.9x, indicating highly efficient customer acquisition. Learn more about SaaS metrics best practices for comprehensive benchmarks.

Churn rate measures the percentage of customers who cancel each month. Calculate as customers lost divided by customers at period start. A 5% monthly churn rate means 60% annual churn, requiring aggressive growth just to maintain revenue. Best-in-class SaaS apps achieve under 3% monthly churn through excellent onboarding, continuous value delivery, and proactive retention programs.

Understanding these metrics creates the foundation for accurate forecasting. Track them daily in your analytics dashboard and review trends weekly. When metrics move unexpectedly, investigate immediately. A sudden ARPU drop might indicate downgrade trends. Rising CAC with flat LTV signals acquisition inefficiency. These metrics tell your business story in numbers.

Cohort Analysis

Cohort analysis groups customers by shared characteristics, typically signup month, and tracks their behavior over time. This reveals patterns invisible in aggregate metrics. A customer who signs up in January 2026 belongs to the January cohort. Track that cohort's retention, revenue contribution, and expansion rate monthly to understand long-term customer value trajectories.

Cohort retention curves show the percentage of each cohort still active over time. Month 0 is 100% by definition. Month 1 might be 85%, Month 3 might be 70%, Month 12 might be 45%. Plot multiple cohorts on the same chart to identify improving retention (newer cohorts stay higher longer) or degrading retention (newer cohorts drop faster). Improving retention indicates product-market fit strengthening. Degrading retention signals quality issues, poor onboarding, or misaligned customer expectations.

Revenue cohorts track MRR contribution by signup month. This reveals which acquisition periods generated the most valuable customers. The January 2026 cohort might contribute $5,000 MRR in Month 0, $4,500 in Month 1 (churn), but $5,200 in Month 6 (expansion revenue from upgrades). Revenue cohorts show whether customers expand, contract, or maintain spend over time.

Expansion revenue is the holy grail of SaaS economics. When existing customers increase spending through upgrades, add-ons, or usage growth, you acquire revenue at zero CAC. Track expansion MRR separately from new MRR. If 20% of customers upgrade from Starter ($49) to Professional ($149) within 6 months, you generate $100 expansion MRR per upgraded customer. Cohort analysis reveals which segments expand most aggressively, informing product and marketing strategies.

Build cohort dashboards that update daily. Monitor 30-day, 60-day, and 90-day retention as leading indicators of long-term health. If 30-day retention drops from 90% to 85%, investigate onboarding friction immediately. If 90-day retention improves from 65% to 72%, you've successfully increased customer stickiness. Cohort analysis transforms lagging indicators (annual retention) into actionable leading indicators (30-day retention).

Create customer segments beyond time-based cohorts. Analyze by acquisition channel (organic vs. paid), industry (fitness vs. restaurants), plan tier (Starter vs. Professional), and app type (booking vs. e-commerce). Multi-dimensional cohort analysis identifies your most valuable customer segments, allowing targeted acquisition and retention strategies. For advanced cohort techniques, explore cohort analysis frameworks from leading analytics platforms.

Forecasting Models

Linear regression provides the simplest forecasting approach: fit a straight line to historical MRR and project forward. This works well for stable, mature businesses but fails to capture seasonality, growth acceleration, or market saturation. Use linear regression only for short-term forecasts (1-3 months) in established businesses with consistent growth rates.

Time series models account for trends, seasonality, and cyclical patterns. ARIMA (AutoRegressive Integrated Moving Average) models are industry standard for financial forecasting. They analyze historical patterns, identify seasonality (holiday spikes, end-of-quarter surges), and project future values with confidence intervals. Time series models excel at 3-12 month forecasts when you have at least 24 months of historical data.

Machine learning predictions leverage algorithms like gradient boosting, random forests, and neural networks to identify complex patterns in multidimensional data. Feed in MRR history, customer acquisition trends, churn rates, expansion rates, marketing spend, seasonality indicators, and external factors (economic indicators, competitor launches). ML models can predict 12+ months with reasonable accuracy when trained on diverse, high-quality datasets. However, they require significant data engineering, ongoing tuning, and interpretation expertise.

Scenario planning acknowledges uncertainty by modeling multiple futures. Create base case (expected growth), bull case (aggressive expansion), and bear case (market contraction) scenarios. Model how changing variables affect outcomes: "If CAC increases 50% but retention improves 10%, what happens to LTV/CAC ratio and cash flow?" Scenario planning informs strategic decisions like fundraising timing, hiring pace, and market expansion priorities. Learn effective financial forecasting methods used by CFOs at high-growth SaaS companies.

Combine approaches for robust forecasting. Use time series for base MRR trends, overlay cohort-based expansion models, and adjust with scenario assumptions. This hybrid approach balances statistical rigor with business judgment. Always forecast with confidence intervals, not point estimates. Saying "MRR will reach $50,000-$70,000 in Q4" is more honest and useful than claiming exactly $60,000.

Review forecast accuracy monthly. Compare predictions to actuals, identify systematic errors (consistently optimistic or pessimistic), and refine models. Forecasting is not about perfect predictions but about understanding drivers, planning resources, and making informed decisions under uncertainty. The goal is not accuracy but insight.

Subscription Analytics

New MRR comes from first-time customers subscribing to any paid plan. Track new MRR by acquisition channel, signup source, and plan tier. If organic search generates $2,000 new MRR monthly while paid ads generate $800, shift marketing investment accordingly. New MRR growth rate is your primary growth metric during early-stage customer acquisition.

Expansion MRR occurs when existing customers increase spending through plan upgrades, add-on purchases, or usage growth beyond base plan limits. This is the most profitable revenue because it has zero acquisition cost. Track expansion rate (expansion MRR as percentage of existing MRR) as a key health metric. Best-in-class SaaS companies achieve 5-10% monthly expansion rates through strategic upselling, feature releases, and usage-based pricing models.

Contraction MRR happens when customers downgrade from higher to lower tiers without fully churning. A Professional customer ($149) downgrading to Starter ($49) creates $100 contraction MRR. High contraction rates signal value perception issues, over-promising during sales, or product gaps in premium tiers. Monitor contraction closely and conduct exit surveys to understand drivers.

Churn MRR represents revenue lost from complete cancellations. Calculate as (cancelled subscription MRR / total MRR at period start) × 100%. A 5% monthly churn rate is sustainable but requires strong new MRR growth. Under 2% is world-class. Over 8% indicates serious retention problems requiring immediate intervention. Track churn reasons systematically through cancellation surveys and exit interviews.

Net MRR growth combines all movements: New MRR + Expansion MRR - Contraction MRR - Churn MRR. This single metric captures overall revenue momentum. Positive net MRR growth means your business is expanding. Negative net MRR growth means you're shrinking despite new customer acquisition. For sustainable growth, target net MRR growth of 10-20% monthly in early stages, 5-10% in growth stage, and 3-5% at maturity.

Build real-time subscription analytics dashboards showing these metrics by day, week, and month. Set up alerts when metrics breach thresholds: new MRR drops 20% week-over-week, churn MRR spikes above 7%, or expansion rate falls below 3%. Early detection enables rapid response before small issues become existential crises. Integrate with your pricing strategy for ChatGPT apps to optimize tier structure and expansion paths.

Financial Dashboards

Real-time metrics provide operational visibility into revenue health. Your executive dashboard should display current MRR, net MRR change today, active customers, ARPU, LTV/CAC ratio, churn rate, and runway (months of cash remaining at current burn rate). Update these metrics hourly through automated data pipelines from Stripe, Firebase, and analytics platforms. Real-time visibility enables rapid response to anomalies.

Trend analysis reveals patterns over weeks and months. Plot MRR growth rate, customer acquisition trends, ARPU evolution, and churn rate curves. Overlay marketing spend, product releases, and competitor events to identify correlations. Did MRR growth spike after that feature launch? Did churn increase when competitors slashed prices? Trend analysis transforms data into strategic insights.

Cohort visualizations show retention curves, revenue trajectories, and expansion patterns by customer segment. Use heatmaps to display month-by-month retention rates across cohorts. Color-code cells from green (high retention) to red (poor retention) for instant pattern recognition. Add drill-down capabilities to explore specific cohorts and identify success patterns.

Forecasting views display projected MRR, ARR, customer count, and revenue mix (new vs. expansion vs. existing) for the next 12 months. Show confidence intervals (P10, P50, P90 scenarios) to acknowledge uncertainty. Include key assumptions (churn rate, expansion rate, monthly customer acquisition) and allow interactive what-if analysis. Forecasting dashboards guide strategic planning, fundraising conversations, and resource allocation decisions.

Executive reports summarize key metrics, trends, insights, and recommendations in executive-friendly formats. Generate automated monthly board reports showing MRR growth, customer metrics, unit economics, cash position, and strategic initiatives. Include commentary explaining variances from plan and actions taken to address issues. Great financial reporting combines quantitative rigor with qualitative context.

Build dashboards with modern BI tools like Metabase, Redash, or Tableau. Alternatively, implement custom dashboards using React, D3.js, and real-time data APIs for full customization. Ensure mobile responsiveness so executives can monitor metrics anywhere. Set up daily summary emails highlighting key movements and anomalies. Financial visibility drives accountability and alignment across your organization. Combine revenue analytics with performance monitoring for ChatGPT apps for complete operational visibility.

Production-Ready Code Examples

1. MRR Calculator (TypeScript, 165 lines)

// mrr-calculator.ts - Comprehensive MRR tracking and analysis
import Stripe from 'stripe';
import { Firestore } from '@google-cloud/firestore';

interface SubscriptionMetrics {
  mrr: number;
  arr: number;
  activeCustomers: number;
  arpu: number;
  newMrr: number;
  expansionMrr: number;
  contractionMrr: number;
  churnMrr: number;
  netMrrGrowth: number;
  breakdown: {
    planId: string;
    planName: string;
    customers: number;
    mrr: number;
    arpu: number;
  }[];
}

export class MRRCalculator {
  private stripe: Stripe;
  private firestore: Firestore;

  constructor(stripeKey: string) {
    this.stripe = new Stripe(stripeKey, { apiVersion: '2023-10-16' });
    this.firestore = new Firestore();
  }

  // Calculate current MRR from active subscriptions
  async calculateCurrentMRR(): Promise<SubscriptionMetrics> {
    const subscriptions = await this.stripe.subscriptions.list({
      status: 'active',
      limit: 100,
      expand: ['data.plan.product']
    });

    let totalMrr = 0;
    const planBreakdown = new Map<string, { name: string; customers: number; revenue: number }>();

    for (const subscription of subscriptions.data) {
      const plan = subscription.items.data[0].plan;
      const amount = plan.amount / 100; // Convert cents to dollars

      // Normalize to monthly
      let monthlyAmount = amount;
      if (plan.interval === 'year') {
        monthlyAmount = amount / 12;
      } else if (plan.interval === 'week') {
        monthlyAmount = amount * 4.33;
      } else if (plan.interval === 'day') {
        monthlyAmount = amount * 30;
      }

      totalMrr += monthlyAmount;

      // Track by plan
      const planId = plan.id;
      const existing = planBreakdown.get(planId) || {
        name: (plan as any).product?.name || planId,
        customers: 0,
        revenue: 0
      };
      existing.customers++;
      existing.revenue += monthlyAmount;
      planBreakdown.set(planId, existing);
    }

    const activeCustomers = subscriptions.data.length;
    const arpu = activeCustomers > 0 ? totalMrr / activeCustomers : 0;

    return {
      mrr: Math.round(totalMrr * 100) / 100,
      arr: Math.round(totalMrr * 12 * 100) / 100,
      activeCustomers,
      arpu: Math.round(arpu * 100) / 100,
      newMrr: 0, // Calculate separately
      expansionMrr: 0, // Calculate separately
      contractionMrr: 0, // Calculate separately
      churnMrr: 0, // Calculate separately
      netMrrGrowth: 0, // Calculate separately
      breakdown: Array.from(planBreakdown.entries()).map(([planId, data]) => ({
        planId,
        planName: data.name,
        customers: data.customers,
        mrr: Math.round(data.revenue * 100) / 100,
        arpu: Math.round((data.revenue / data.customers) * 100) / 100
      }))
    };
  }

  // Calculate MRR movements for a specific period
  async calculateMRRMovements(startDate: Date, endDate: Date): Promise<Partial<SubscriptionMetrics>> {
    // Fetch subscription events
    const events = await this.stripe.events.list({
      created: {
        gte: Math.floor(startDate.getTime() / 1000),
        lte: Math.floor(endDate.getTime() / 1000)
      },
      types: [
        'customer.subscription.created',
        'customer.subscription.updated',
        'customer.subscription.deleted'
      ],
      limit: 100
    });

    let newMrr = 0;
    let expansionMrr = 0;
    let contractionMrr = 0;
    let churnMrr = 0;

    for (const event of events.data) {
      if (event.type === 'customer.subscription.created') {
        // New subscription = new MRR
        const subscription = event.data.object as Stripe.Subscription;
        const plan = subscription.items.data[0].plan;
        newMrr += this.normalizeMRR(plan.amount / 100, plan.interval);
      } else if (event.type === 'customer.subscription.updated') {
        // Compare previous and current MRR
        const previousSubscription = (event.data as any).previous_attributes;
        const currentSubscription = event.data.object as Stripe.Subscription;

        if (previousSubscription?.items) {
          const oldPlan = previousSubscription.items.data[0].plan;
          const newPlan = currentSubscription.items.data[0].plan;

          const oldMrr = this.normalizeMRR(oldPlan.amount / 100, oldPlan.interval);
          const newMrr = this.normalizeMRR(newPlan.amount / 100, newPlan.interval);

          const delta = newMrr - oldMrr;
          if (delta > 0) {
            expansionMrr += delta;
          } else if (delta < 0) {
            contractionMrr += Math.abs(delta);
          }
        }
      } else if (event.type === 'customer.subscription.deleted') {
        // Cancellation = churn MRR
        const subscription = event.data.object as Stripe.Subscription;
        const plan = subscription.items.data[0].plan;
        churnMrr += this.normalizeMRR(plan.amount / 100, plan.interval);
      }
    }

    const netMrrGrowth = newMrr + expansionMrr - contractionMrr - churnMrr;

    return {
      newMrr: Math.round(newMrr * 100) / 100,
      expansionMrr: Math.round(expansionMrr * 100) / 100,
      contractionMrr: Math.round(contractionMrr * 100) / 100,
      churnMrr: Math.round(churnMrr * 100) / 100,
      netMrrGrowth: Math.round(netMrrGrowth * 100) / 100
    };
  }

  // Normalize any billing interval to monthly amount
  private normalizeMRR(amount: number, interval: string): number {
    switch (interval) {
      case 'year':
        return amount / 12;
      case 'month':
        return amount;
      case 'week':
        return amount * 4.33;
      case 'day':
        return amount * 30;
      default:
        return amount;
    }
  }
}

2. Cohort Analyzer (SQL + TypeScript, 175 lines)

// cohort-analyzer.ts - Customer cohort analysis and retention tracking
import { Firestore, Timestamp } from '@google-cloud/firestore';

interface CohortData {
  cohortMonth: string;
  customersAtStart: number;
  retentionByMonth: { [month: number]: number };
  revenueByMonth: { [month: number]: number };
  expansionRate: number;
  churnRate: number;
}

interface CustomerEvent {
  userId: string;
  signupDate: Timestamp;
  subscriptionStatus: 'active' | 'cancelled' | 'paused';
  currentMrr: number;
  planTier: string;
}

export class CohortAnalyzer {
  private firestore: Firestore;

  constructor() {
    this.firestore = new Firestore();
  }

  // Generate cohort analysis for all monthly cohorts
  async analyzeCohorts(monthsBack: number = 12): Promise<CohortData[]> {
    const cohorts: CohortData[] = [];
    const now = new Date();

    for (let i = 0; i < monthsBack; i++) {
      const cohortDate = new Date(now.getFullYear(), now.getMonth() - i, 1);
      const cohortData = await this.analyzeCohort(cohortDate);
      cohorts.push(cohortData);
    }

    return cohorts.sort((a, b) => a.cohortMonth.localeCompare(b.cohortMonth));
  }

  // Analyze a specific cohort (all users who signed up in a given month)
  private async analyzeCohort(cohortDate: Date): Promise<CohortData> {
    const cohortMonth = `${cohortDate.getFullYear()}-${String(cohortDate.getMonth() + 1).padStart(2, '0')}`;

    // Get cohort start and end dates
    const cohortStart = Timestamp.fromDate(new Date(cohortDate.getFullYear(), cohortDate.getMonth(), 1));
    const cohortEnd = Timestamp.fromDate(new Date(cohortDate.getFullYear(), cohortDate.getMonth() + 1, 0));

    // Fetch all customers who signed up in this cohort month
    const cohortSnapshot = await this.firestore
      .collection('users')
      .where('signupDate', '>=', cohortStart)
      .where('signupDate', '<=', cohortEnd)
      .get();

    const customersAtStart = cohortSnapshot.size;
    const customerIds = cohortSnapshot.docs.map(doc => doc.id);

    // Track retention and revenue month by month
    const retentionByMonth: { [month: number]: number } = { 0: 100 };
    const revenueByMonth: { [month: number]: number } = {};

    const monthsSinceCohort = this.getMonthsSince(cohortDate);

    for (let month = 0; month <= monthsSinceCohort; month++) {
      const checkDate = new Date(cohortDate.getFullYear(), cohortDate.getMonth() + month, 15);
      const checkTimestamp = Timestamp.fromDate(checkDate);

      // Count active customers and revenue at this point
      let activeCount = 0;
      let totalRevenue = 0;

      for (const customerId of customerIds) {
        const subscriptionSnapshot = await this.firestore
          .collection('subscriptions')
          .where('userId', '==', customerId)
          .where('status', '==', 'active')
          .where('createdAt', '<=', checkTimestamp)
          .get();

        if (!subscriptionSnapshot.empty) {
          activeCount++;
          const subscription = subscriptionSnapshot.docs[0].data();
          totalRevenue += subscription.mrr || 0;
        }
      }

      retentionByMonth[month] = customersAtStart > 0
        ? Math.round((activeCount / customersAtStart) * 100 * 10) / 10
        : 0;
      revenueByMonth[month] = Math.round(totalRevenue * 100) / 100;
    }

    // Calculate expansion and churn rates
    const initialRevenue = revenueByMonth[0] || 0;
    const currentRevenue = revenueByMonth[monthsSinceCohort] || 0;
    const revenueGrowth = initialRevenue > 0
      ? ((currentRevenue - initialRevenue) / initialRevenue) * 100
      : 0;

    const initialRetention = 100;
    const currentRetention = retentionByMonth[monthsSinceCohort] || 0;
    const churnRate = initialRetention - currentRetention;

    return {
      cohortMonth,
      customersAtStart,
      retentionByMonth,
      revenueByMonth,
      expansionRate: Math.round(revenueGrowth * 10) / 10,
      churnRate: Math.round(churnRate * 10) / 10
    };
  }

  // Calculate months elapsed since cohort start
  private getMonthsSince(cohortDate: Date): number {
    const now = new Date();
    return (now.getFullYear() - cohortDate.getFullYear()) * 12
      + (now.getMonth() - cohortDate.getMonth());
  }

  // Generate retention curve visualization data
  async generateRetentionCurve(): Promise<any> {
    const cohorts = await this.analyzeCohorts(12);

    const chartData = {
      labels: Array.from({ length: 13 }, (_, i) => `Month ${i}`),
      datasets: cohorts.map((cohort, index) => ({
        label: cohort.cohortMonth,
        data: Array.from({ length: 13 }, (_, month) => cohort.retentionByMonth[month] || null),
        borderColor: this.getColorForCohort(index),
        fill: false
      }))
    };

    return chartData;
  }

  private getColorForCohort(index: number): string {
    const colors = ['#3B82F6', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6', '#EC4899'];
    return colors[index % colors.length];
  }
}

3. Revenue Forecasting Model (Python, 155 lines)

# revenue_forecasting.py - ML-powered revenue forecasting with ARIMA and scenario planning
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
from statsmodels.tsa.arima.model import ARIMA
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.model_selection import train_test_split
import warnings
warnings.filterwarnings('ignore')

class RevenueForecastor:
    def __init__(self, historical_data_path=None):
        """Initialize with historical MRR data (CSV with columns: date, mrr, customers, arpu)"""
        if historical_data_path:
            self.data = pd.read_csv(historical_data_path, parse_dates=['date'])
            self.data = self.data.sort_values('date')
        else:
            self.data = None

    def prepare_time_series(self):
        """Prepare data for time series analysis"""
        if self.data is None:
            raise ValueError("No historical data loaded")

        # Set date as index
        ts_data = self.data.set_index('date')

        # Fill missing dates with interpolation
        date_range = pd.date_range(start=ts_data.index.min(), end=ts_data.index.max(), freq='D')
        ts_data = ts_data.reindex(date_range)
        ts_data['mrr'] = ts_data['mrr'].interpolate(method='linear')

        # Resample to monthly
        monthly_data = ts_data['mrr'].resample('M').mean()

        return monthly_data

    def arima_forecast(self, periods=12, confidence_level=0.95):
        """Generate ARIMA forecast for specified periods with confidence intervals"""
        ts_data = self.prepare_time_series()

        # Fit ARIMA model (auto-selecting parameters)
        # Using (1,1,1) as default - adjust based on ACF/PACF analysis
        model = ARIMA(ts_data, order=(1, 1, 1))
        fitted_model = model.fit()

        # Generate forecast
        forecast = fitted_model.forecast(steps=periods)

        # Get confidence intervals
        forecast_df = fitted_model.get_forecast(steps=periods)
        confidence_intervals = forecast_df.conf_int(alpha=1-confidence_level)

        # Prepare results
        last_date = ts_data.index[-1]
        forecast_dates = [last_date + timedelta(days=30*i) for i in range(1, periods+1)]

        results = pd.DataFrame({
            'date': forecast_dates,
            'forecast_mrr': forecast.values,
            'lower_bound': confidence_intervals.iloc[:, 0].values,
            'upper_bound': confidence_intervals.iloc[:, 1].values
        })

        return results

    def ml_forecast(self, periods=12, feature_engineering=True):
        """Use gradient boosting for multi-feature revenue forecasting"""
        if self.data is None:
            raise ValueError("No historical data loaded")

        # Feature engineering
        df = self.data.copy()
        df['month'] = df['date'].dt.month
        df['year'] = df['date'].dt.year
        df['day_of_week'] = df['date'].dt.dayofweek
        df['day_of_month'] = df['date'].dt.day
        df['quarter'] = df['date'].dt.quarter

        # Lagged features
        df['mrr_lag_1'] = df['mrr'].shift(1)
        df['mrr_lag_7'] = df['mrr'].shift(7)
        df['mrr_lag_30'] = df['mrr'].shift(30)
        df['customers_lag_1'] = df['customers'].shift(1)

        # Rolling statistics
        df['mrr_rolling_mean_7'] = df['mrr'].rolling(window=7).mean()
        df['mrr_rolling_std_7'] = df['mrr'].rolling(window=7).std()
        df['mrr_rolling_mean_30'] = df['mrr'].rolling(window=30).mean()

        # Drop NaN rows from feature engineering
        df = df.dropna()

        # Prepare features and target
        feature_cols = ['month', 'year', 'day_of_week', 'day_of_month', 'quarter',
                       'mrr_lag_1', 'mrr_lag_7', 'mrr_lag_30', 'customers_lag_1',
                       'mrr_rolling_mean_7', 'mrr_rolling_std_7', 'mrr_rolling_mean_30',
                       'customers', 'arpu']

        X = df[feature_cols]
        y = df['mrr']

        # Train model
        X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, shuffle=False)

        model = GradientBoostingRegressor(
            n_estimators=100,
            learning_rate=0.1,
            max_depth=5,
            random_state=42
        )
        model.fit(X_train, y_train)

        # Generate forecast
        last_row = df.iloc[-1]
        forecasts = []

        for i in range(periods):
            # Create feature vector for next period
            forecast_date = last_row['date'] + timedelta(days=30*i)

            features = {
                'month': forecast_date.month,
                'year': forecast_date.year,
                'day_of_week': forecast_date.dayofweek,
                'day_of_month': forecast_date.day,
                'quarter': (forecast_date.month - 1) // 3 + 1,
                'mrr_lag_1': last_row['mrr'],
                'mrr_lag_7': last_row['mrr_lag_7'],
                'mrr_lag_30': last_row['mrr_lag_30'],
                'customers_lag_1': last_row['customers'],
                'mrr_rolling_mean_7': last_row['mrr_rolling_mean_7'],
                'mrr_rolling_std_7': last_row['mrr_rolling_std_7'],
                'mrr_rolling_mean_30': last_row['mrr_rolling_mean_30'],
                'customers': last_row['customers'] * 1.05,  # Assume 5% monthly growth
                'arpu': last_row['arpu']
            }

            X_forecast = pd.DataFrame([features])
            predicted_mrr = model.predict(X_forecast)[0]

            forecasts.append({
                'date': forecast_date,
                'forecast_mrr': predicted_mrr
            })

        return pd.DataFrame(forecasts)

    def scenario_planning(self, periods=12):
        """Generate bull, base, and bear case scenarios"""
        base_forecast = self.arima_forecast(periods=periods)

        scenarios = {
            'base_case': base_forecast['forecast_mrr'].values,
            'bull_case': base_forecast['forecast_mrr'].values * 1.3,  # 30% upside
            'bear_case': base_forecast['forecast_mrr'].values * 0.7,  # 30% downside
        }

        scenario_df = pd.DataFrame(scenarios)
        scenario_df['date'] = base_forecast['date']

        return scenario_df

# Example usage
if __name__ == "__main__":
    # Load historical data
    forecaster = RevenueForecastor('historical_mrr.csv')

    # ARIMA forecast
    arima_results = forecaster.arima_forecast(periods=12)
    print("ARIMA Forecast (12 months):")
    print(arima_results)

    # ML forecast
    ml_results = forecaster.ml_forecast(periods=12)
    print("\nML Forecast (12 months):")
    print(ml_results)

    # Scenario planning
    scenarios = forecaster.scenario_planning(periods=12)
    print("\nScenario Planning:")
    print(scenarios)

4. LTV/CAC Calculator (TypeScript, 145 lines)

// ltv-cac-calculator.ts - Customer lifetime value and acquisition cost analysis
import { Firestore, Timestamp } from '@google-cloud/firestore';

interface LTVMetrics {
  ltv: number;
  cac: number;
  ltvCacRatio: number;
  paybackPeriodMonths: number;
  averageCustomerLifespanMonths: number;
}

interface CohortLTV {
  cohortMonth: string;
  ltv: number;
  actualRevenue: number;
  projectedRevenue: number;
  customersInCohort: number;
}

export class LTVCACCalculator {
  private firestore: Firestore;

  constructor() {
    this.firestore = new Firestore();
  }

  // Calculate LTV using cohort-based historical data
  async calculateHistoricalLTV(monthsOfHistory: number = 12): Promise<number> {
    const cohorts = await this.getCohortData(monthsOfHistory);

    if (cohorts.length === 0) return 0;

    // Average revenue per customer across all cohorts
    let totalRevenue = 0;
    let totalCustomers = 0;

    for (const cohort of cohorts) {
      totalRevenue += cohort.actualRevenue;
      totalCustomers += cohort.customersInCohort;
    }

    const averageRevenuePerCustomer = totalCustomers > 0
      ? totalRevenue / totalCustomers
      : 0;

    return Math.round(averageRevenuePerCustomer * 100) / 100;
  }

  // Calculate LTV using simple formula: ARPU / Churn Rate
  async calculateSimpleLTV(arpu: number, monthlyChurnRate: number): Promise<number> {
    if (monthlyChurnRate <= 0 || monthlyChurnRate >= 1) {
      throw new Error('Churn rate must be between 0 and 1');
    }

    return Math.round((arpu / monthlyChurnRate) * 100) / 100;
  }

  // Calculate CAC from marketing spend and customer acquisition
  async calculateCAC(startDate: Date, endDate: Date): Promise<number> {
    // Get total marketing spend in period
    const marketingSpend = await this.getMarketingSpend(startDate, endDate);

    // Get new customers acquired in period
    const newCustomers = await this.getNewCustomers(startDate, endDate);

    if (newCustomers === 0) return 0;

    return Math.round((marketingSpend / newCustomers) * 100) / 100;
  }

  // Calculate comprehensive LTV/CAC metrics
  async calculateMetrics(arpu: number, monthlyChurnRate: number): Promise<LTVMetrics> {
    const ltv = await this.calculateSimpleLTV(arpu, monthlyChurnRate);

    // Calculate CAC for last 30 days
    const endDate = new Date();
    const startDate = new Date();
    startDate.setDate(startDate.getDate() - 30);

    const cac = await this.calculateCAC(startDate, endDate);

    const ltvCacRatio = cac > 0 ? Math.round((ltv / cac) * 100) / 100 : 0;

    // Payback period = CAC / ARPU (months to recover acquisition cost)
    const paybackPeriodMonths = arpu > 0
      ? Math.round((cac / arpu) * 10) / 10
      : 0;

    // Average customer lifespan = 1 / churn rate
    const averageCustomerLifespanMonths = monthlyChurnRate > 0
      ? Math.round((1 / monthlyChurnRate) * 10) / 10
      : 0;

    return {
      ltv,
      cac,
      ltvCacRatio,
      paybackPeriodMonths,
      averageCustomerLifespanMonths
    };
  }

  // Get cohort data for LTV calculation
  private async getCohortData(monthsBack: number): Promise<CohortLTV[]> {
    const cohorts: CohortLTV[] = [];
    const now = new Date();

    for (let i = 0; i < monthsBack; i++) {
      const cohortDate = new Date(now.getFullYear(), now.getMonth() - i, 1);
      const cohortMonth = `${cohortDate.getFullYear()}-${String(cohortDate.getMonth() + 1).padStart(2, '0')}`;

      const cohortStart = Timestamp.fromDate(cohortDate);
      const cohortEnd = Timestamp.fromDate(new Date(cohortDate.getFullYear(), cohortDate.getMonth() + 1, 0));

      // Get customers in cohort
      const cohortSnapshot = await this.firestore
        .collection('users')
        .where('signupDate', '>=', cohortStart)
        .where('signupDate', '<=', cohortEnd)
        .get();

      const customersInCohort = cohortSnapshot.size;

      // Calculate actual revenue from this cohort
      let actualRevenue = 0;

      for (const doc of cohortSnapshot.docs) {
        const userId = doc.id;
        const paymentsSnapshot = await this.firestore
          .collection('payments')
          .where('userId', '==', userId)
          .get();

        paymentsSnapshot.forEach(paymentDoc => {
          const payment = paymentDoc.data();
          actualRevenue += payment.amount || 0;
        });
      }

      cohorts.push({
        cohortMonth,
        ltv: customersInCohort > 0 ? actualRevenue / customersInCohort : 0,
        actualRevenue,
        projectedRevenue: actualRevenue, // Can add projection logic
        customersInCohort
      });
    }

    return cohorts;
  }

  // Get marketing spend from expense tracking
  private async getMarketingSpend(startDate: Date, endDate: Date): Promise<number> {
    const snapshot = await this.firestore
      .collection('expenses')
      .where('category', '==', 'marketing')
      .where('date', '>=', Timestamp.fromDate(startDate))
      .where('date', '<=', Timestamp.fromDate(endDate))
      .get();

    let totalSpend = 0;
    snapshot.forEach(doc => {
      totalSpend += doc.data().amount || 0;
    });

    return totalSpend;
  }

  // Get new customers in period
  private async getNewCustomers(startDate: Date, endDate: Date): Promise<number> {
    const snapshot = await this.firestore
      .collection('users')
      .where('signupDate', '>=', Timestamp.fromDate(startDate))
      .where('signupDate', '<=', Timestamp.fromDate(endDate))
      .get();

    return snapshot.size;
  }
}

5. Subscription Analytics (TypeScript, 135 lines)

// subscription-analytics.ts - Detailed subscription movement tracking
import Stripe from 'stripe';
import { Firestore, Timestamp } from '@google-cloud/firestore';

interface SubscriptionMovement {
  date: string;
  newMrr: number;
  expansionMrr: number;
  contractionMrr: number;
  churnMrr: number;
  netMrrMovement: number;
  newCustomers: number;
  expandedCustomers: number;
  contractedCustomers: number;
  churnedCustomers: number;
}

interface MonthlyAnalytics {
  startMrr: number;
  endMrr: number;
  movements: SubscriptionMovement;
  growthRate: number;
  customerCount: number;
  arpu: number;
}

export class SubscriptionAnalytics {
  private stripe: Stripe;
  private firestore: Firestore;

  constructor(stripeKey: string) {
    this.stripe = new Stripe(stripeKey, { apiVersion: '2023-10-16' });
    this.firestore = new Firestore();
  }

  // Analyze subscription movements for a month
  async analyzeMonth(year: number, month: number): Promise<MonthlyAnalytics> {
    const startDate = new Date(year, month - 1, 1);
    const endDate = new Date(year, month, 0);

    // Get MRR at start and end of month
    const startMrr = await this.getMRRAtDate(startDate);
    const endMrr = await this.getMRRAtDate(endDate);

    // Calculate movements
    const movements = await this.calculateMovements(startDate, endDate);

    // Calculate growth rate
    const growthRate = startMrr > 0
      ? Math.round(((endMrr - startMrr) / startMrr) * 100 * 10) / 10
      : 0;

    // Get customer count
    const customerCount = await this.getActiveCustomerCount(endDate);

    // Calculate ARPU
    const arpu = customerCount > 0
      ? Math.round((endMrr / customerCount) * 100) / 100
      : 0;

    return {
      startMrr,
      endMrr,
      movements,
      growthRate,
      customerCount,
      arpu
    };
  }

  // Calculate detailed subscription movements
  private async calculateMovements(startDate: Date, endDate: Date): Promise<SubscriptionMovement> {
    const movements: SubscriptionMovement = {
      date: `${startDate.getFullYear()}-${String(startDate.getMonth() + 1).padStart(2, '0')}`,
      newMrr: 0,
      expansionMrr: 0,
      contractionMrr: 0,
      churnMrr: 0,
      netMrrMovement: 0,
      newCustomers: 0,
      expandedCustomers: 0,
      contractedCustomers: 0,
      churnedCustomers: 0
    };

    // Track subscription changes
    const events = await this.stripe.events.list({
      created: {
        gte: Math.floor(startDate.getTime() / 1000),
        lte: Math.floor(endDate.getTime() / 1000)
      },
      types: [
        'customer.subscription.created',
        'customer.subscription.updated',
        'customer.subscription.deleted'
      ],
      limit: 100
    });

    for (const event of events.data) {
      if (event.type === 'customer.subscription.created') {
        const subscription = event.data.object as Stripe.Subscription;
        const mrr = this.calculateMRR(subscription);
        movements.newMrr += mrr;
        movements.newCustomers++;
      } else if (event.type === 'customer.subscription.updated') {
        const previousAttributes = (event.data as any).previous_attributes;
        const currentSubscription = event.data.object as Stripe.Subscription;

        if (previousAttributes?.items) {
          const oldMrr = this.calculateMRRFromItems(previousAttributes.items.data);
          const newMrr = this.calculateMRR(currentSubscription);

          const delta = newMrr - oldMrr;

          if (delta > 0) {
            movements.expansionMrr += delta;
            movements.expandedCustomers++;
          } else if (delta < 0) {
            movements.contractionMrr += Math.abs(delta);
            movements.contractedCustomers++;
          }
        }
      } else if (event.type === 'customer.subscription.deleted') {
        const subscription = event.data.object as Stripe.Subscription;
        const mrr = this.calculateMRR(subscription);
        movements.churnMrr += mrr;
        movements.churnedCustomers++;
      }
    }

    movements.netMrrMovement = movements.newMrr + movements.expansionMrr
      - movements.contractionMrr - movements.churnMrr;

    // Round all values
    movements.newMrr = Math.round(movements.newMrr * 100) / 100;
    movements.expansionMrr = Math.round(movements.expansionMrr * 100) / 100;
    movements.contractionMrr = Math.round(movements.contractionMrr * 100) / 100;
    movements.churnMrr = Math.round(movements.churnMrr * 100) / 100;
    movements.netMrrMovement = Math.round(movements.netMrrMovement * 100) / 100;

    return movements;
  }

  // Calculate MRR from subscription
  private calculateMRR(subscription: Stripe.Subscription): number {
    const plan = subscription.items.data[0].plan;
    const amount = plan.amount / 100;

    switch (plan.interval) {
      case 'year':
        return amount / 12;
      case 'month':
        return amount;
      case 'week':
        return amount * 4.33;
      case 'day':
        return amount * 30;
      default:
        return amount;
    }
  }

  private calculateMRRFromItems(items: any[]): number {
    return items.reduce((total, item) => {
      const amount = item.plan.amount / 100;
      const interval = item.plan.interval;

      switch (interval) {
        case 'year':
          return total + (amount / 12);
        case 'month':
          return total + amount;
        default:
          return total + amount;
      }
    }, 0);
  }

  private async getMRRAtDate(date: Date): Promise<number> {
    // Implement by querying historical snapshot or calculating from events
    // This is a placeholder implementation
    return 0;
  }

  private async getActiveCustomerCount(date: Date): Promise<number> {
    const subscriptions = await this.stripe.subscriptions.list({
      status: 'active',
      created: { lte: Math.floor(date.getTime() / 1000) },
      limit: 100
    });

    return subscriptions.data.length;
  }
}

6. Time Series Forecaster (Python, 125 lines)

# time_series_forecaster.py - Advanced time series forecasting with Prophet
from prophet import Prophet
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
import matplotlib.pyplot as plt

class TimeSeriesForecaster:
    def __init__(self, historical_data=None):
        """
        Initialize forecaster with historical data
        historical_data: DataFrame with 'ds' (date) and 'y' (value) columns
        """
        self.data = historical_data
        self.model = None
        self.forecast = None

    def load_data_from_csv(self, filepath):
        """Load historical MRR data from CSV"""
        df = pd.read_csv(filepath)

        # Ensure proper column names for Prophet
        if 'date' in df.columns and 'mrr' in df.columns:
            df = df.rename(columns={'date': 'ds', 'mrr': 'y'})

        # Convert to datetime
        df['ds'] = pd.to_datetime(df['ds'])

        self.data = df[['ds', 'y']]
        return self.data

    def fit_model(self, seasonality_mode='multiplicative', changepoint_prior_scale=0.05):
        """
        Fit Prophet model to historical data
        seasonality_mode: 'additive' or 'multiplicative'
        changepoint_prior_scale: controls flexibility (0.001-0.5, default 0.05)
        """
        if self.data is None:
            raise ValueError("No data loaded. Use load_data_from_csv() first.")

        # Initialize Prophet model
        self.model = Prophet(
            seasonality_mode=seasonality_mode,
            changepoint_prior_scale=changepoint_prior_scale,
            yearly_seasonality=True,
            weekly_seasonality=False,
            daily_seasonality=False
        )

        # Add custom seasonalities
        self.model.add_seasonality(name='monthly', period=30.5, fourier_order=5)
        self.model.add_seasonality(name='quarterly', period=91.25, fourier_order=3)

        # Fit model
        self.model.fit(self.data)

        return self.model

    def generate_forecast(self, periods=12, freq='M'):
        """
        Generate forecast for future periods
        periods: number of periods to forecast
        freq: 'D' for daily, 'M' for monthly
        """
        if self.model is None:
            raise ValueError("Model not fitted. Use fit_model() first.")

        # Create future dataframe
        future = self.model.make_future_dataframe(periods=periods, freq=freq)

        # Generate forecast
        self.forecast = self.model.predict(future)

        return self.forecast

    def get_forecast_summary(self):
        """Get summary of forecast with confidence intervals"""
        if self.forecast is None:
            raise ValueError("No forecast generated. Use generate_forecast() first.")

        # Get only future predictions (exclude historical fit)
        historical_end = self.data['ds'].max()
        future_forecast = self.forecast[self.forecast['ds'] > historical_end]

        summary = future_forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].copy()
        summary.columns = ['Date', 'Forecast', 'Lower Bound (95%)', 'Upper Bound (95%)']

        return summary

    def plot_forecast(self, save_path=None):
        """Visualize forecast with confidence intervals"""
        if self.model is None or self.forecast is None:
            raise ValueError("Model must be fitted and forecast generated first.")

        fig = self.model.plot(self.forecast, xlabel='Date', ylabel='MRR ($)')
        plt.title('Revenue Forecast with 95% Confidence Intervals')

        if save_path:
            plt.savefig(save_path, dpi=300, bbox_inches='tight')

        plt.show()

        return fig

    def plot_components(self, save_path=None):
        """Plot forecast components (trend, seasonality)"""
        if self.model is None or self.forecast is None:
            raise ValueError("Model must be fitted and forecast generated first.")

        fig = self.model.plot_components(self.forecast)

        if save_path:
            plt.savefig(save_path, dpi=300, bbox_inches='tight')

        plt.show()

        return fig

    def calculate_growth_metrics(self):
        """Calculate growth rate and other metrics from forecast"""
        if self.forecast is None:
            raise ValueError("No forecast generated.")

        historical_end = self.data['ds'].max()
        future_forecast = self.forecast[self.forecast['ds'] > historical_end]

        # Calculate month-over-month growth
        future_forecast['mom_growth'] = future_forecast['yhat'].pct_change() * 100

        # Calculate cumulative growth from current MRR
        current_mrr = self.data['y'].iloc[-1]
        future_forecast['cumulative_growth'] = ((future_forecast['yhat'] - current_mrr) / current_mrr) * 100

        metrics = {
            'avg_monthly_growth_rate': future_forecast['mom_growth'].mean(),
            'total_growth_rate': future_forecast['cumulative_growth'].iloc[-1],
            'final_mrr_forecast': future_forecast['yhat'].iloc[-1],
            'confidence_interval_width': (future_forecast['yhat_upper'] - future_forecast['yhat_lower']).mean()
        }

        return metrics

# Example usage
if __name__ == "__main__":
    # Initialize forecaster
    forecaster = TimeSeriesForecaster()

    # Load historical data
    forecaster.load_data_from_csv('mrr_history.csv')

    # Fit model
    forecaster.fit_model(seasonality_mode='multiplicative')

    # Generate 12-month forecast
    forecast = forecaster.generate_forecast(periods=12, freq='M')

    # Get summary
    summary = forecaster.get_forecast_summary()
    print("Revenue Forecast Summary:")
    print(summary)

    # Calculate growth metrics
    metrics = forecaster.calculate_growth_metrics()
    print("\nGrowth Metrics:")
    for key, value in metrics.items():
        print(f"{key}: {value:.2f}")

    # Plot forecast
    forecaster.plot_forecast(save_path='revenue_forecast.png')
    forecaster.plot_components(save_path='forecast_components.png')

7. Financial Dashboard (React, 115 lines)

// FinancialDashboard.tsx - Real-time revenue metrics dashboard
import React, { useEffect, useState } from 'react';
import { Line, Bar } from 'react-chartjs-2';
import { Chart as ChartJS, registerables } from 'chart.js';

ChartJS.register(...registerables);

interface MetricsData {
  mrr: number;
  arr: number;
  customerCount: number;
  arpu: number;
  ltvCacRatio: number;
  churnRate: number;
  netMrrGrowth: number;
  runway: number;
}

interface ChartData {
  labels: string[];
  datasets: {
    label: string;
    data: number[];
    borderColor: string;
    backgroundColor: string;
  }[];
}

export const FinancialDashboard: React.FC = () => {
  const [metrics, setMetrics] = useState<MetricsData | null>(null);
  const [mrrHistory, setMrrHistory] = useState<ChartData | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetchDashboardData();

    // Refresh every 5 minutes
    const interval = setInterval(fetchDashboardData, 5 * 60 * 1000);

    return () => clearInterval(interval);
  }, []);

  const fetchDashboardData = async () => {
    try {
      const [metricsRes, historyRes] = await Promise.all([
        fetch('/api/metrics/current'),
        fetch('/api/metrics/history?months=12')
      ]);

      const metricsData = await metricsRes.json();
      const historyData = await historyRes.json();

      setMetrics(metricsData);
      setMrrHistory({
        labels: historyData.map((d: any) => d.month),
        datasets: [
          {
            label: 'MRR',
            data: historyData.map((d: any) => d.mrr),
            borderColor: '#3B82F6',
            backgroundColor: 'rgba(59, 130, 246, 0.1)'
          }
        ]
      });

      setLoading(false);
    } catch (error) {
      console.error('Failed to fetch dashboard data:', error);
      setLoading(false);
    }
  };

  const formatCurrency = (value: number): string => {
    return new Intl.NumberFormat('en-US', {
      style: 'currency',
      currency: 'USD',
      minimumFractionDigits: 0
    }).format(value);
  };

  if (loading) {
    return <div className="dashboard-loading">Loading financial metrics...</div>;
  }

  return (
    <div className="financial-dashboard">
      <h1>Financial Dashboard</h1>

      <div className="metrics-grid">
        <div className="metric-card">
          <div className="metric-label">Monthly Recurring Revenue</div>
          <div className="metric-value">{formatCurrency(metrics?.mrr || 0)}</div>
          <div className={`metric-change ${(metrics?.netMrrGrowth || 0) >= 0 ? 'positive' : 'negative'}`}>
            {(metrics?.netMrrGrowth || 0) >= 0 ? '▲' : '▼'} {Math.abs(metrics?.netMrrGrowth || 0).toFixed(1)}%
          </div>
        </div>

        <div className="metric-card">
          <div className="metric-label">Annual Recurring Revenue</div>
          <div className="metric-value">{formatCurrency(metrics?.arr || 0)}</div>
        </div>

        <div className="metric-card">
          <div className="metric-label">Active Customers</div>
          <div className="metric-value">{metrics?.customerCount || 0}</div>
        </div>

        <div className="metric-card">
          <div className="metric-label">Average Revenue Per User</div>
          <div className="metric-value">{formatCurrency(metrics?.arpu || 0)}</div>
        </div>

        <div className="metric-card">
          <div className="metric-label">LTV/CAC Ratio</div>
          <div className="metric-value">{(metrics?.ltvCacRatio || 0).toFixed(1)}x</div>
          <div className="metric-benchmark">
            {(metrics?.ltvCacRatio || 0) >= 3 ? '✓ Healthy' : '⚠ Below Target'}
          </div>
        </div>

        <div className="metric-card">
          <div className="metric-label">Monthly Churn Rate</div>
          <div className="metric-value">{(metrics?.churnRate || 0).toFixed(1)}%</div>
          <div className="metric-benchmark">
            {(metrics?.churnRate || 0) <= 5 ? '✓ Good' : '⚠ High'}
          </div>
        </div>

        <div className="metric-card">
          <div className="metric-label">Runway (Months)</div>
          <div className="metric-value">{metrics?.runway || 0}</div>
        </div>
      </div>

      <div className="charts-section">
        <div className="chart-container">
          <h2>MRR Trend (12 Months)</h2>
          {mrrHistory && (
            <Line
              data={mrrHistory}
              options={{
                responsive: true,
                plugins: {
                  legend: { display: false },
                  tooltip: {
                    callbacks: {
                      label: (context) => formatCurrency(context.parsed.y)
                    }
                  }
                },
                scales: {
                  y: {
                    ticks: {
                      callback: (value) => formatCurrency(Number(value))
                    }
                  }
                }
              }}
            />
          )}
        </div>
      </div>
    </div>
  );
};

8. Scenario Planner (TypeScript, 105 lines)

// scenario-planner.ts - Financial scenario modeling and planning
interface ScenarioInputs {
  currentMrr: number;
  customerCount: number;
  arpu: number;
  monthlyGrowthRate: number; // New customer growth %
  churnRate: number; // Monthly churn %
  expansionRate: number; // Monthly expansion %
  cac: number;
  forecastMonths: number;
}

interface ScenarioOutput {
  month: number;
  mrr: number;
  arr: number;
  customers: number;
  newCustomers: number;
  churnedCustomers: number;
  totalRevenue: number;
  cumulativeCAC: number;
  ltv: number;
  ltvCacRatio: number;
}

export class ScenarioPlanner {
  // Run base case scenario
  runBaseCase(inputs: ScenarioInputs): ScenarioOutput[] {
    return this.runScenario(inputs);
  }

  // Run bull case (30% better than base)
  runBullCase(inputs: ScenarioInputs): ScenarioOutput[] {
    const bullInputs = {
      ...inputs,
      monthlyGrowthRate: inputs.monthlyGrowthRate * 1.3,
      churnRate: inputs.churnRate * 0.7,
      expansionRate: inputs.expansionRate * 1.3
    };

    return this.runScenario(bullInputs);
  }

  // Run bear case (30% worse than base)
  runBearCase(inputs: ScenarioInputs): ScenarioOutput[] {
    const bearInputs = {
      ...inputs,
      monthlyGrowthRate: inputs.monthlyGrowthRate * 0.7,
      churnRate: inputs.churnRate * 1.3,
      expansionRate: inputs.expansionRate * 0.7
    };

    return this.runScenario(bearInputs);
  }

  // Core scenario calculation engine
  private runScenario(inputs: ScenarioInputs): ScenarioOutput[] {
    const results: ScenarioOutput[] = [];

    let currentCustomers = inputs.customerCount;
    let currentMrr = inputs.currentMrr;
    let currentArpu = inputs.arpu;
    let cumulativeRevenue = 0;
    let cumulativeCAC = 0;

    for (let month = 0; month <= inputs.forecastMonths; month++) {
      // Calculate new customers
      const newCustomers = month === 0
        ? 0
        : Math.round(currentCustomers * (inputs.monthlyGrowthRate / 100));

      // Calculate churned customers
      const churnedCustomers = Math.round(currentCustomers * (inputs.churnRate / 100));

      // Update customer count
      currentCustomers = currentCustomers + newCustomers - churnedCustomers;

      // Calculate expansion revenue (existing customers increasing spend)
      const expansionMrr = currentMrr * (inputs.expansionRate / 100);

      // Calculate new MRR from new customers
      const newMrr = newCustomers * currentArpu;

      // Calculate churned MRR
      const churnedMrr = (churnedCustomers / (currentCustomers + churnedCustomers)) * currentMrr;

      // Update MRR
      currentMrr = currentMrr + newMrr + expansionMrr - churnedMrr;

      // Update ARPU (slight increase from expansion)
      currentArpu = currentCustomers > 0 ? currentMrr / currentCustomers : currentArpu;

      // Calculate cumulative metrics
      cumulativeRevenue += currentMrr;
      cumulativeCAC += newCustomers * inputs.cac;

      // Calculate LTV (simplified: ARPU / churn rate)
      const ltv = inputs.churnRate > 0 ? currentArpu / (inputs.churnRate / 100) : 0;
      const ltvCacRatio = inputs.cac > 0 ? ltv / inputs.cac : 0;

      results.push({
        month,
        mrr: Math.round(currentMrr * 100) / 100,
        arr: Math.round(currentMrr * 12 * 100) / 100,
        customers: currentCustomers,
        newCustomers,
        churnedCustomers,
        totalRevenue: Math.round(cumulativeRevenue * 100) / 100,
        cumulativeCAC: Math.round(cumulativeCAC * 100) / 100,
        ltv: Math.round(ltv * 100) / 100,
        ltvCacRatio: Math.round(ltvCacRatio * 100) / 100
      });
    }

    return results;
  }

  // Compare scenarios side by side
  compareScenarios(inputs: ScenarioInputs): {
    base: ScenarioOutput[];
    bull: ScenarioOutput[];
    bear: ScenarioOutput[];
  } {
    return {
      base: this.runBaseCase(inputs),
      bull: this.runBullCase(inputs),
      bear: this.runBearCase(inputs)
    };
  }
}

9. Metrics Collector (TypeScript, 95 lines)

// metrics-collector.ts - Real-time metrics collection from multiple sources
import Stripe from 'stripe';
import { Firestore } from '@google-cloud/firestore';

interface CollectedMetrics {
  timestamp: Date;
  mrr: number;
  arr: number;
  customers: {
    active: number;
    new30d: number;
    churned30d: number;
  };
  revenue: {
    new30d: number;
    expansion30d: number;
    contraction30d: number;
    churn30d: number;
  };
  averages: {
    arpu: number;
    ltv: number;
    cac30d: number;
  };
  rates: {
    churnRate: number;
    expansionRate: number;
    growthRate: number;
  };
}

export class MetricsCollector {
  private stripe: Stripe;
  private firestore: Firestore;

  constructor(stripeKey: string) {
    this.stripe = new Stripe(stripeKey, { apiVersion: '2023-10-16' });
    this.firestore = new Firestore();
  }

  // Collect all metrics in parallel
  async collectMetrics(): Promise<CollectedMetrics> {
    const [stripeMetrics, firestoreMetrics] = await Promise.all([
      this.collectStripeMetrics(),
      this.collectFirestoreMetrics()
    ]);

    // Combine metrics
    const combined: CollectedMetrics = {
      timestamp: new Date(),
      mrr: stripeMetrics.mrr,
      arr: stripeMetrics.mrr * 12,
      customers: {
        active: stripeMetrics.activeCustomers,
        new30d: firestoreMetrics.newCustomers30d,
        churned30d: stripeMetrics.churned30d
      },
      revenue: {
        new30d: stripeMetrics.newMrr30d,
        expansion30d: stripeMetrics.expansionMrr30d,
        contraction30d: stripeMetrics.contractionMrr30d,
        churn30d: stripeMetrics.churnMrr30d
      },
      averages: {
        arpu: stripeMetrics.activeCustomers > 0
          ? stripeMetrics.mrr / stripeMetrics.activeCustomers
          : 0,
        ltv: 0, // Calculate separately
        cac30d: firestoreMetrics.cac30d
      },
      rates: {
        churnRate: stripeMetrics.churnRate,
        expansionRate: stripeMetrics.expansionRate,
        growthRate: stripeMetrics.growthRate
      }
    };

    // Store in Firestore for historical tracking
    await this.storeMetrics(combined);

    return combined;
  }

  private async collectStripeMetrics() {
    const subscriptions = await this.stripe.subscriptions.list({
      status: 'active',
      limit: 100
    });

    // Calculate current MRR
    let mrr = 0;
    subscriptions.data.forEach(sub => {
      const plan = sub.items.data[0].plan;
      const amount = plan.amount / 100;
      mrr += plan.interval === 'year' ? amount / 12 : amount;
    });

    return {
      mrr,
      activeCustomers: subscriptions.data.length,
      newMrr30d: 0, // Calculate from events
      expansionMrr30d: 0,
      contractionMrr30d: 0,
      churnMrr30d: 0,
      churned30d: 0,
      churnRate: 0,
      expansionRate: 0,
      growthRate: 0
    };
  }

  private async collectFirestoreMetrics() {
    const thirtyDaysAgo = new Date();
    thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);

    // Count new signups
    const newSignupsSnapshot = await this.firestore
      .collection('users')
      .where('signupDate', '>=', thirtyDaysAgo)
      .get();

    return {
      newCustomers30d: newSignupsSnapshot.size,
      cac30d: 0 // Calculate from marketing spend
    };
  }

  private async storeMetrics(metrics: CollectedMetrics) {
    await this.firestore.collection('metrics_history').add({
      ...metrics,
      timestamp: new Date()
    });
  }
}

10. Executive Report Generator (TypeScript, 85 lines)

// executive-report.ts - Automated executive reporting
import { Firestore } from '@google-cloud/firestore';
import PDFDocument from 'pdfkit';
import fs from 'fs';

interface ExecutiveReport {
  period: string;
  summary: {
    mrr: number;
    mrrGrowth: number;
    customers: number;
    customerGrowth: number;
    arpu: number;
    ltvCacRatio: number;
  };
  highlights: string[];
  concerns: string[];
  recommendations: string[];
}

export class ExecutiveReportGenerator {
  private firestore: Firestore;

  constructor() {
    this.firestore = new Firestore();
  }

  async generateMonthlyReport(year: number, month: number): Promise<ExecutiveReport> {
    const currentMetrics = await this.getMetricsForMonth(year, month);
    const previousMetrics = await this.getMetricsForMonth(
      month === 1 ? year - 1 : year,
      month === 1 ? 12 : month - 1
    );

    const mrrGrowth = previousMetrics.mrr > 0
      ? ((currentMetrics.mrr - previousMetrics.mrr) / previousMetrics.mrr) * 100
      : 0;

    const customerGrowth = previousMetrics.customers > 0
      ? ((currentMetrics.customers - previousMetrics.customers) / previousMetrics.customers) * 100
      : 0;

    const report: ExecutiveReport = {
      period: `${year}-${String(month).padStart(2, '0')}`,
      summary: {
        mrr: currentMetrics.mrr,
        mrrGrowth,
        customers: currentMetrics.customers,
        customerGrowth,
        arpu: currentMetrics.arpu,
        ltvCacRatio: currentMetrics.ltvCacRatio
      },
      highlights: this.generateHighlights(currentMetrics, previousMetrics),
      concerns: this.identifyConcerns(currentMetrics, previousMetrics),
      recommendations: this.generateRecommendations(currentMetrics, previousMetrics)
    };

    return report;
  }

  private generateHighlights(current: any, previous: any): string[] {
    const highlights: string[] = [];

    if (current.mrr > previous.mrr * 1.1) {
      highlights.push(`MRR grew ${((current.mrr / previous.mrr - 1) * 100).toFixed(1)}% month-over-month`);
    }

    if (current.ltvCacRatio > 5) {
      highlights.push(`Strong unit economics with ${current.ltvCacRatio.toFixed(1)}x LTV/CAC ratio`);
    }

    return highlights;
  }

  private identifyConcerns(current: any, previous: any): string[] {
    const concerns: string[] = [];

    if (current.churnRate > 7) {
      concerns.push(`Churn rate at ${current.churnRate.toFixed(1)}% exceeds healthy threshold of 5%`);
    }

    if (current.ltvCacRatio < 3) {
      concerns.push(`LTV/CAC ratio of ${current.ltvCacRatio.toFixed(1)}x below target of 3x`);
    }

    return concerns;
  }

  private generateRecommendations(current: any, previous: any): string[] {
    return [
      'Focus on reducing churn through improved onboarding',
      'Implement expansion revenue strategies to increase ARPU',
      'Optimize CAC through better channel allocation'
    ];
  }

  private async getMetricsForMonth(year: number, month: number): Promise<any> {
    // Fetch from metrics_history collection
    return {
      mrr: 50000,
      customers: 350,
      arpu: 142.86,
      ltvCacRatio: 4.5,
      churnRate: 5.2
    };
  }
}

Conclusion

Revenue forecasting transforms your ChatGPT app from an uncertain venture into a predictable business with clear growth trajectories. The models and systems in this guide provide the financial visibility required to make strategic decisions confidently. Whether you're forecasting MRR with ARIMA time series models, tracking cohort retention curves, or building executive dashboards, these production-ready implementations give you enterprise-grade analytics without enterprise complexity.

Start with the MRR calculator and subscription analytics to establish baseline metrics. Add cohort analysis to understand customer value trajectories. Implement forecasting models to project future growth and plan resources. Finally, build financial dashboards that provide real-time visibility to your entire team. With rigorous revenue forecasting, you'll know exactly when you'll hit $100K MRR, how much runway you have, and which levers to pull for accelerated growth.

Ready to build your revenue-optimized ChatGPT app? Start free with MakeAIHQ and deploy your first ChatGPT app in 48 hours. Our platform handles the technical complexity while you focus on financial optimization. Explore our pricing strategy guide, user retention analytics, and performance monitoring systems to build a complete analytics stack for your ChatGPT app business.