Команда, работающая с партнёрскими программами, каждый день сверяет три отчёта: рекламный кабинет показывает расходы, партнёрская сеть показывает целевые действия, трекер показывает клики, и ни один отчёт в одиночку не отвечает на главный вопрос, сколько кампания заработала чистыми. Кастомная аналитическая связка закрывает ровно эту задачу: сырые события льются в одну базу, поверх считается рентабельность, а дашборд выводит ROI без ручных табличек и вечерней рутины. Разберём метрики, схему таблиц, формулы, модели атрибуции и саму доску, на которой цифры начинают работать на команду.
Метрики партнёрского трафика от первого клика до выплаты
Партнёрский маркетинг построен на сделке с отсроченным расчётом: вебмастер приводит посетителя, площадка платит за целевое действие, покупку, регистрацию или одобренную заявку, а деньги приходят после модерации. Чтобы ни один переход не потерялся, в ссылку добавляются пять стандартных UTM-параметров:
?utm_source=partner_network
&utm_medium=cpa
&utm_campaign=spring_sale
&utm_content=banner_a
&utm_term=keyboard
Экономика кампании собирается из четырёх базовых величин. Конверсия CR показывает долю целевых действий от числа кликов, EPC показывает средний доход с одного клика, цена клика CPC показывает расход на трафик, выплата payout показывает сумму за действие. Для примера возьмём день из жизни одной кампании: 40 000 кликов по 3 руб., 320 одобренных действий со средней выплатой 900 руб. Расходы составили 120 000 руб., доход 288 000 руб., конверсия 0.8%, EPC 7.2 руб. На каждый одобренный заказ кампания тратит 375 руб. трафика и получает 900 руб. выплаты, маржа одного действия равна 525 руб.
Механика сбора тоже заслуживает внимания. Редирект площадки ловит параметры ссылки, трекер пишет строку клика с уникальным click_id, лендинг дорисовывает события поведения, а кнопка заказа фиксирует целевое действие. Если в цепочке хотя бы одно звено теряет идентификатор, конверсия прилетает безликой и ложится в отчёт общим числом. Именно поэтому аналитик первым делом проверяет сквозную передачу от объявления до постбэка, а уже потом смотрит на графики.
Показатель EPC заслуживает отдельного уважения, потому что сворачивает в одну цифру и цену клика, и конверсию, и размер выплаты. Две кампании с одинаковой маржой могут отличаться по EPC в разы: одна покупает дешёвые клики с низкой конверсией, другая платит за дорогой, но горячий трафик. Когда площадка меняет условия и урезает выплату на 15%, EPC падает мгновенно, а ROI на первый взгляд держится, пока не подтянутся новые конверсии. Статусы действий делятся на одобренные, находящиеся на модерации и отклонённые, и разница между ними объясняет, почему один и тот же день в двух отчётах выглядит по-разному.
Формула ROI и её подводные камни на живых цифрах
Рентабельность инвестиций считается одной строкой:
ROI = (доход - затраты) / затраты * 100%
Для цифр из примера формула даёт (288 000 - 120 000) / 120 000 * 100 = 140%: каждый вложенный рубль возвращается с ещё одним рублём и сорока копейками сверху. Ноль означает точку безубыточности, отрицательное значение означает убыток. Оговорки начинаются сразу. В затраты нередко забывают положить стоимость трекера, доменов, прокси и рабочих часов, и тогда красивые 140% по факту превращаются в скромные 90%. Выплаты на модерации создают вторую ловушку: в день старта отчёт радует нулями, а деньги приходят через две недели, когда расходы уже удвоились. Третья ловушка, возвраты: часть одобренных действий отменяется позже, и честный ROI считается по факту удержавшихся выплат, а не по оптимистичной сумме первых часов.
Практика выручает разложением формулы на слагаемые. Если доход фиксирован выплатой 900 руб. за действие, то предельная цена конверсии, при которой кампания выходит в ноль, равна самой выплате. Значит, при конверсии 0.8% предельная цена клика составляет 7.2 руб., и текущие 3 руб. дают запас прочности в 2.4 раза. Такой расчёт сразу отвечает на вопрос, сколько ещё можно повышать цену за трафик, прежде чем кампания перестанет зарабатывать.
Отдельный класс ошибок связан со сроками. ROI, посчитанный в первые сутки, почти всегда врёт в обе стороны: дорогой тестовый трафик съедает бюджет, выплаты ещё не пришли, и экран заливается красным. Тот же бюджет, пересчитанный через месяц, оказывается рабочей связкой. Поэтому рядом с ROI в дашборде всегда держат возраст разреза, а решение об отключении кампании принимают по достигшему зрелости срезу, а не по вчерашнему всплеску.
Схема таблиц для честного трекинга конверсий
Кастомный дашборд начинается не с графиков, а с двух таблиц, в которых сходятся клики и конверсии. Клик пишется в момент перехода, конверсия прилетает постбэком с тем же идентификатором, и связка между ними делает любые разрезы тривиальными:
CREATE TABLE clicks (
click_id VARCHAR(64) PRIMARY KEY,
ts TIMESTAMP NOT NULL,
campaign_id INT NOT NULL,
source VARCHAR(64),
sub_id VARCHAR(64),
cost DECIMAL(10,4) NOT NULL
);
CREATE TABLE conversions (
conv_id VARCHAR(64) PRIMARY KEY,
click_id VARCHAR(64) NOT NULL,
ts TIMESTAMP NOT NULL,
status VARCHAR(16) NOT NULL,
payout DECIMAL(10,2) NOT NULL
);
Ключ click_id в обеих таблицах заменяет собой файлы cookie: даже если посетитель сменил браузер, серверная связка сработает, потому что идентификатор живёт не на устройстве, а в ссылке и в базе. Поле sub_id несёт информацию о вложенном канале, баннере или связке, что позволяет резать отчёты до конкретного креатива. Поле cost хранит цену конкретного клика, а не среднюю по кампании, и в спорной ситуации день всегда можно пересчитать построчно. На потоке в миллионы кликов в сутки ClickHouse-подобные колоночные хранилища или партиционированный PostgreSQL справляются лучше рядовой реляционной базы, и выбор хранилища стоит делать по планируемому объёму, а не по привычке.
Грубая форма постбэка от площадки к трекеру выглядит так:
https://track.example.com/postback
?click_id={click_id}
&payout={payout}
&status={status}
&tx_id={transaction_id}
Макросы в фигурных скобках площадка подставляет при отправке, а трекер по click_id находит строку клика и дозаписывает конверсию. Параметр tx_id закрывает дубли: повторный запрос с тем же номером транзакции отбрасывается молча, и статистика не распухает от повторов.
Модели атрибуции распределяют ценность по касаниям
Один покупатель перед заказом кликает по объявлению трижды: из рассылки, из поиска и из ретаргетинга. Кому засчитать продажу, решает модель атрибуции. Последний клик отдаёт всю ценность финальному касанию, первый клик всему начальному, линейная модель делит деньги поровну, модель с затуханием времени отдаёт больше веса касаниям, близким к покупке, а позиционная схема закрепляет 40% за первым касанием, 40% за последним и делит оставшиеся 20% между серединой пути. У каждой модели своя правда: первый клик объясняет, где рождается спрос, последний закрывает вопрос оплаты, а сравнение двух картин вскрывает переоценённые каналы, которые паразитируют на чужом прогреве.
На цифрах расхождение моделей видно сразу. Путь из трёх касаний с расходом 10, 20 и 30 руб. заканчивается продажей с выплатой 600 руб. Последний клик запишет все 600 руб. ретаргетингу и оставит поиск в нуле, первый клик отдаст всю сумму рассылке, линейная модель раздаст по 200 руб. каждому касанию, позиционная схема закрепит по 240 руб. за крайними касаниями и 120 руб. за серединой. Если ретаргетинговая кампания стоит дорого, а получает полный кредит последнего клика, бюджет годами кормит не того, кого следовало бы.
Технически атрибуция держится на серверных постбэках: площадка, зафиксировав целевое действие, отправляет на трекер запрос с идентификатором клика и суммой выплаты. Дубли отсекаются по идентификатору транзакции, поэтому одна продажа не превращается в три. Окно атрибуции задаёт срок жизни привязки, в партнёрских программах стандартом считаются 30 или 90 дней, и это окно должно совпадать во всех системах цепочки, иначе расхождение отчётов гарантировано ещё до первого спора с площадкой.
Кастомный дашборд вместо ручного свода таблиц
Инструментов для доски хватает на любой вкус. Looker Studio бесплатен, подключается к базам и таблицам и собирается мышкой за вечер, Metabase легко поднимается в собственном контуре и радует вопросами на естественном языке, Apache Superset даёт богатые SQL-фильтры и кеширование, Grafana затачивалась под мониторинг инфраструктуры, но с базой данных рисует отличные графики. Выбор инструмента меньше влияет на результат, чем состав первого экрана. Минимальный набор плиток выглядит так:
- ROI за вчера, неделю и месяц по каждой партнёрской программе;
- доля одобренных конверсий и средняя выплата по действиям;
- EPC и конверсия по каналам с разрезом до sub_id;
- расхождение между отправленными и полученными постбэками;
- топ кампаний по абсолютной прибыли и по маржинальности.
Первый экран отвечает на два вопроса, куда ушли деньги и где рождается прибыль, остальное прячется во вложенных вкладках. Раз в час доска обновляется сама, живые фильтры по дате, каналу и кампании превращают её из картинки в инструмент, а сохранённые представления закрепляют за каждым членом команды собственный угол зрения: медиабайер смотрит расход и цену клика, владелец смотрит чистую прибыль и кассовые разрывы.
Практика сборки обычно идёт от вопроса к плитке. Сначала фиксируют три сценария, ради которых доска существует: утренняя проверка вчерашнего результата, недельный разбор по кампаниям, месячный отчёт для владельца. Под каждый сценарий собирается свой экран, и плитки не смешиваются: графики расходов не соседствуют с выплатами, чтобы глаз не путал линии. Основой для доски чаще служит не сама база, а подготовленное SQL-представление, в котором уже посчитаны агрегаты за день и неделю, тогда страница открывается за секунды даже на миллионах строк.
Когортный анализ в SQL раскрывает задержку выплат
Когорты отвечают на вопрос, который свод за день скрыть может: как кампания ведёт себя на дистанции. Запрос группирует клики по неделям и подтягивает подтверждённые выплаты:
SELECT
DATE_TRUNC('week', c.ts) AS cohort_week,
COUNT(DISTINCT c.click_id) AS clicks,
SUM(c.cost) AS spend,
SUM(CASE WHEN v.status = 'approved' THEN v.payout ELSE 0 END) AS revenue,
ROUND(
(SUM(CASE WHEN v.status = 'approved' THEN v.payout ELSE 0 END) - SUM(c.cost))
/ NULLIF(SUM(c.cost), 0) * 100, 1
) AS roi_pct
FROM clicks c
LEFT JOIN conversions v ON v.click_id = c.click_id
GROUP BY cohort_week
ORDER BY cohort_week;
Столбец roi_pct по неделям показывает траекторию: кампания стартует с минусом, выходит в ноль на второй неделе и зарабатывает на третьей, когда подтягиваются выплаты. Без когорт месяц выглядит средним болотом, в котором тонут и удачные связки, и сливные. В том же запросе полезно посчитать долю статусов: если по когорте двухнедельной давности половина действий всё ещё на модерации, площадка затягивает подтверждение, и денежный поток команды проседает без всяких проблем с трафиком. Разрез по sub_id внутри когорты добавляет третий слой: связка, блестящая в первых двух неделях, может выдохнуть на третьей, и это видно раньше, чем вливается новый бюджет.
Автоматизация отчётности и пороги для тревоги
Дашборд окупается, когда его не открывают по расписанию вручную. Достаточно трёх уровней автоматизма. Ежедневное письмо с вчерашним ROI ставит точку в дне, уведомление о падении конверсии ниже порога ловит проблему раньше, чем она съест бюджет, а алерт на остановку постбэков сигнализирует о разорванной связке между площадкой и трекером, классической причине потерянных конверсий. Пороги ставятся от факта, а не от надежды: если историческая конверсия кампании держится около 0.8%, тревогу разумно вешать на падение ниже 0.5%, а не на любое шевеление цифры. Для кассового разрыва полезен отдельный порог по доле выплат на модерации выше 40%, он предупреждает, что деньги застряли.
Со временем база копит историю, и доска начинает подсказывать. Связки с ROI выше 100% получают масштаб, связки между нулём и сотней получают проверку креативов, связки ниже нуля выключаются без сожалений. Цифры перестают быть отчётом для галочки и превращаются в контур управления: кампании масштабируются по метрикам, а не по ощущениям, и каждая новая площадка входит в работу только через собственный трекинг и собственный экран с рентабельностью.