LTV: как считать ценность пользователя по когортам
Как считать LTV по когортам, связать его с ARPU и retention, сравнить каналы и не выдать грубую оценку за пожизненную ценность пользователя.

Команда видит, что новый канал привёл много регистраций, и сразу спрашивает: “А сколько эти пользователи принесут?” Здесь появляется LTV. Метрика помогает связать поведение когорты с деньгами, но только если назвать горизонт, единицу пользователя и способ учёта выручки. Один средний LTV без этих условий часто создаёт больше уверенности, чем знаний.
Коротко
LTV, или lifetime value, показывает, сколько чистой выручки в среднем приносит пользователь за выбранный горизонт жизни. Для исторического расчёта это не магическое число на всю жизнь, а накопительная выручка когорты, разделённая на размер этой когорты.
LTV_N = cumulative net revenue from cohort during N / users in cohortLTV30 и LTV90 — разные метрики. Нельзя сравнивать их как одно и то же число или выдавать LTV30 за полную ценность пользователя.
- Всегда указывай горизонт: D30, D90, 6 месяцев или другой срок.
- Считай одинаковые когорты: по регистрации, первой покупке или первой активации.
- Используй net revenue, если сравниваешь экономическую ценность каналов.
- Сравнивай только зрелые когорты, которым уже исполнился выбранный горизонт.
- Для решения о закупке трафика связывай LTV с CAC, маржой и сроком окупаемости.
LTV, ARPU и ARPPU отвечают на разные вопросы
ARPU обычно смотрит на выбранный период: месяц, неделю или день. LTV накапливает выручку одной когорты с момента старта. ARPPU исключает пользователей без платежа и поэтому описывает только платящую часть базы. Эти метрики связаны, но не заменяют друг друга.
Если ARPU вырос в июле, это ещё не значит, что июльская когорта станет более ценной. Возможно, в ней много крупных разовых платежей. LTV по когортам заставляет посмотреть, сохраняется ли разница на D30 или D90.
| Метрика | Знаменатель | Какой вопрос задаёт |
|---|---|---|
| ARPU | активные пользователи периода | Сколько в среднем принесла аудитория за этот период? |
| ARPPU | платящие пользователи периода | Сколько в среднем заплатил пользователь, который уже платит? |
| LTV30 / LTV90 | пользователи одной зрелой когорты | Сколько чистой выручки накопила когорта за 30 или 90 дней? |
Начинай с исторического LTV по когортам
Для нового продукта лучше начать с наблюдаемого LTV, а не с модели, которая обещает точную ценность на годы вперёд. Возьми пользователей с одинаковым стартом, посчитай их net revenue на D1, D7, D30 и D90, а затем сравни когорты между собой.
Когорта может строиться по регистрации, первой активации, первой покупке или другому событию, которое действительно задаёт начало жизненного цикла. Выбор зависит от вопроса. Для оценки рекламного канала чаще подходит регистрация или первая активация; для повторных покупок — первая покупка.
| Параметр | Пример решения |
|---|---|
| Старт когорты | signup_at или первая успешная покупка |
| Единица | user_id, account_id или платящий workspace |
| Выручка | успешные платежи минус возвраты и скидки |
| Горизонт | D1, D7, D30, D90; только зрелые когорты |
| Разрез | канал, платформа, тариф или страна |
Кривая LTV важнее одного финального числа
Одинаковый LTV90 может складываться из разных сценариев. Одна когорта получает большую часть выручки в первый день и потом почти не растёт. Другая медленно набирает ценность за счёт повторных покупок или подписки. Для продукта это разные риски и разные решения.
На графике ниже paid social выглядит слабее уже на D7 и почти не догоняет органику к D90. Это не доказывает, что канал плохой сам по себе: нужно проверить CAC, маржу, состав кампаний и зрелость когорт. Но такой разрыв уже подсказывает, где искать причину.
Условный пример. Сравниваются зрелые когорты на одинаковых горизонтах; значения показывают net revenue на одного пользователя.
Какая выручка должна попасть в LTV
Для сравнения каналов LTV лучше считать на чистой выручке. Если один источник приводит много заказов, которые потом возвращают, его gross revenue будет выглядеть здоровее реальной экономики. То же относится к скидкам, комиссиям платёжных систем, налогам и отменённым списаниям.
В B2B-продукте важно выбрать правильную единицу. Если один аккаунт оплачивает workspace для 30 сотрудников, LTV на user_id может исказить историю. Для такого продукта полезнее считать LTV на account_id или workspace и отдельно показывать пользователей внутри аккаунта.
| Компонент | Что проверить |
|---|---|
| Платежи | учитываются только успешные операции |
| Возвраты | вычитаются из выручки в том же периоде или по согласованному правилу |
| Скидки | не теряются при переходе от gross к net revenue |
| Комиссии | понятно, считаем выручку или contribution margin |
| Валюта | все суммы приведены к одной валюте и таймзоне |
SQL для LTV30 и LTV90 по каналам
Ниже запрос для PostgreSQL. Таблица users содержит одну строку на пользователя с полями user_id, signup_at и channel. Таблица payments содержит платежи, возвраты и статус операции. Запрос берёт только когорты, которым уже исполнилось 90 дней, поэтому свежие регистрации не занижают LTV90 из-за незрелого окна.
D1, D7, D30 и D90 здесь означают первые 1, 7, 30 и 90 календарных дней после signup_at. Для продукта с другой логикой жизненного цикла нужно заменить стартовое событие и границы окна.
with cohorts as (
select user_id, signup_at, channel,
date_trunc('week', signup_at)::date as cohort_week
from users
where signup_at >= date_trunc('week', current_date) - interval '24 weeks'
and signup_at < current_date - interval '90 days'
),
normalized_payments as (
select user_id, paid_at,
case
when payment_type = 'refund' then -amount
when payment_status = 'succeeded' then amount
else 0
end as net_amount
from payments
where paid_at >= date_trunc('week', current_date) - interval '24 weeks'
and paid_at < current_date
),
select c.cohort_week, c.channel,
count(distinct c.user_id) as cohort_users,
round(coalesce(sum(p.net_amount) filter (where p.paid_at < c.signup_at + interval '1 day'), 0) / nullif(count(distinct c.user_id), 0), 2) as ltv_d1,
round(coalesce(sum(p.net_amount) filter (where p.paid_at < c.signup_at + interval '7 days'), 0) / nullif(count(distinct c.user_id), 0), 2) as ltv_d7,
round(coalesce(sum(p.net_amount) filter (where p.paid_at < c.signup_at + interval '30 days'), 0) / nullif(count(distinct c.user_id), 0), 2) as ltv_d30,
round(coalesce(sum(p.net_amount) filter (where p.paid_at < c.signup_at + interval '90 days'), 0) / nullif(count(distinct c.user_id), 0), 2) as ltv_d90
from cohorts c
left join normalized_payments p
on p.user_id = c.user_id
and p.paid_at >= c.signup_at
and p.paid_at < c.signup_at + interval '90 days'
group by 1, 2
order by 1, 2;LTV без CAC не отвечает на вопрос об окупаемости
Высокий LTV ещё не означает, что канал выгоден. Если привлечь пользователя стоит 300 ₽, а LTV90 равен 248 ₽, рост такой аудитории не окупается на этом горизонте. Если LTV считается по выручке, а CAC и расходы сравниваются с маржой, вывод тоже будет слишком оптимистичным.
Практическое сравнение начинай с одной пары: зрелый LTV выбранного горизонта и CAC той же когорты. Потом добавляй gross margin, возвраты и срок окупаемости. Не смешивай органику с paid traffic: у них разная стоимость привлечения и разная структура аудитории.
contribution LTV_N = LTV_N × gross margin; payback when contribution LTV_N >= CACЭто рабочая проверка на выбранном горизонте, а не универсальный прогноз всей жизни пользователя.
Где LTV начинает врать
Самая опасная версия LTV — одно среднее число для всех пользователей. Оно может расти из-за нескольких крупных клиентов, изменения mix каналов или случайного исключения возвратов. Ещё одна ошибка — назвать прогнозный LTV историческим фактом и забыть, что модель строится на предположениях о будущем поведении.
- Не сравнивай LTV30 свежей когорты с LTV90 зрелой когорты.
- Не дели выручку только на платящих, если называешь результат LTV всей когорты.
- Не смешивай user_id, account_id и workspace в одном отчёте.
- Не используй gross revenue, если возвраты и скидки заметно меняют экономику.
- Не принимай корреляцию LTV с каналом за доказательство причинности.
- Не масштабируй канал по LTV без проверки CAC, маржи и срока окупаемости.
Как сформулировать вывод для команды
Слабый вывод звучит так: “LTV в органике выше, значит, нужно отключить paid social”. Он не говорит, достаточно ли объёма органики и окупается ли платный канал после учёта CAC.
Сильнее будет так: “У зрелых июньских когорт D90 LTV organic составил 248 ₽, referral — 238 ₽, paid social — 129 ₽. Разрыв появляется уже на D7 и почти не сокращается. Следующий шаг — сравнить CAC и contribution margin по кампаниям paid social; отключать весь канал до этого сравнения рано”. Такой вывод связывает цифру с решением, но не притворяется доказанной причинностью.
- Назови горизонт и зрелость когорт.
- Покажи единицу: пользователь, аккаунт или workspace.
- Разложи LTV по каналу, тарифу или платформе.
- Сравни LTV с CAC и contribution margin.
- Зафиксируй следующий шаг и владельца проверки.
Что почитать дальше
LTV лучше всего читать в связке с поведением и входом в продукт. Сначала проверь, возвращаются ли пользователи, затем посмотри, как они доходят до оплаты, и только после этого сравнивай ценность каналов.