К списку метрик
MONETIZATION

Monthly Recurring Revenue (MRR)

Monthly Recurring Revenue

Сумма всех ежемесячных подписочных платежей, нормализованных к месяцу.

ФОРМУЛА

$$\text{MRR} = \sum_{i} \text{monthly_amount}i$$ Для годовых подписок: $\text{MRR}{annual} = \text{annual_amount} / 12$

ОПИСАНИЕ

Что измеряет: ключевая метрика SaaS-бизнеса. Предсказуемый, повторяющийся доход.

MRR Breakdown (всегда смотрят 4 компонента):

NEW MRR     — от новых клиентов в этом месяце
EXPANSION   — апгрейды существующих (Tier 1 → Tier 2)
CONTRACTION — даунгрейды
CHURN       — отток (отрицательный)

NET NEW MRR = NEW + EXPANSION - CONTRACTION - CHURN

Healthy SaaS: Net New MRR > 0 каждый месяц.

Связанные метрики:

  • ARR = MRR × 12 (для годовой отчётности)
  • NRR (Net Revenue Retention) = (start MRR + expansion - churn - contraction) / start MRR
    • NRR > 100% — negative churn (expansion перекрывает потери) — мечта
    • NRR 80-100% — стандарт для SMB
    • NRR < 80% — проблема с retention

Каспи Pay контекст: для подписочного Premium-тарифа NAQTY DATA:

  • 100 Pro юзеров × 14,990 ₸ = 1.5M ₸ MRR
  • Если churn 5%/мес и net new 10/мес → NRR ~95%, нормально для consumer

Pitfalls:

  1. One-time payments не в MRR. Если клиент заплатил 50K единовременно — это не MRR, это booking. MRR — только recurring.

  2. Discounts: если у клиента 50% скидка на год — MRR = list_price × 0.5, не list price.

  3. MRR vs Revenue: MRR — теоретическая. Cash collected revenue может отличаться (delays, refunds).

SQL ПРИМЕР
-- MRR breakdown по типам изменений месяц-к-месяцу
WITH monthly_subs AS (
  SELECT user_id,
         date_trunc('month', billing_date) AS month,
         SUM(amount_kzt) AS mrr_amount
  FROM subscriptions
  GROUP BY 1, 2
),
changes AS (
  SELECT user_id, month,
         mrr_amount AS curr_mrr,
         LAG(mrr_amount) OVER (PARTITION BY user_id ORDER BY month) AS prev_mrr
  FROM monthly_subs
)
SELECT month,
  SUM(curr_mrr) AS total_mrr,
  SUM(CASE WHEN prev_mrr IS NULL THEN curr_mrr ELSE 0 END) AS new_mrr,
  SUM(CASE WHEN prev_mrr < curr_mrr THEN curr_mrr - prev_mrr ELSE 0 END) AS expansion,
  SUM(CASE WHEN prev_mrr > curr_mrr THEN prev_mrr - curr_mrr ELSE 0 END) AS contraction
FROM changes
GROUP BY month
ORDER BY month;
PYTHON ПРИМЕР
subs = subscriptions.assign(
    month=subscriptions["billing_date"].dt.to_period("M")
)
mrr = subs.groupby(["user_id", "month"])["amount_kzt"].sum().reset_index()
mrr["prev"] = mrr.groupby("user_id")["amount_kzt"].shift(1)

summary = mrr.groupby("month").apply(lambda x: pd.Series({
    "mrr": x["amount_kzt"].sum(),
    "new": x.loc[x["prev"].isna(), "amount_kzt"].sum(),
    "expansion": (x.loc[x["amount_kzt"] > x["prev"], "amount_kzt"] - x.loc[x["amount_kzt"] > x["prev"], "prev"]).sum(),
}))
СВЯЗАННЫЕ МЕТРИКИ