Оглавление11 разделов
Анализ отклонённых конверсий должен объяснить решение по каждой группе, а не доказать заранее выбранную версию о «плохом трафике». Rejected — финальный или корректирующий статус процесса квалификации. Он может отражать дубликат, неверное GEO, незавершённое условие, техническую потерю связи, отмену платежа или подтверждённый abuse. Смешивание причин делает оптимизацию случайной.
Результат расследованияНа выходе нужны: воспроизводимая когорта, распределение стабильных reason codes, оценка денежного влияния, список спорных решений, владелец каждой причины и защитная мера. Процент rejected без этих элементов не является диагнозом.
Сначала зафиксируйте знаменатель
Rejection rate можно считать среди всех received, среди решённых или среди событий, прошедших техническую валидацию. Это разные метрики. Для качества закупки обычно нужен знаменатель eligible_decided: события применимой когорты, по которым окно решения завершилось и техническая доставка подтверждена.
Числитель хранит конкретный класс rejected. Общий rejected_total подходит для финансовой сводки, но не для действия. Доля geo_mismatch оптимизируется иначе, чем duplicate или payment_reversed.
Схема неизменяемого решения
CREATE TABLE conversion_decisions (
conversion_id text NOT NULL,
status_version integer NOT NULL,
status text NOT NULL,
reason_code text,
terms_version text NOT NULL,
occurred_at timestamptz NOT NULL,
decided_at timestamptz NOT NULL,
source_id text,
campaign_id text,
geo text,
amount_minor bigint,
currency text,
payload_hash text NOT NULL,
PRIMARY KEY (conversion_id, status_version)
);Текущее состояние вычисляется по максимальной применимой status_version, но история не перезаписывается. Иначе поздний approved или reversal невозможно отличить от первоначального решения.
Таксономия причин
- Eligibility: GEO, новый пользователь, возраст продукта, допустимый канал
- Completion: не выполнено квалифицирующее действие
- Duplicate: повтор того же пользователя или события по определённому правилу
- Technical: отсутствует click_id, неверная подпись, контракт события
- Payment: отмена, возврат, неподтверждённый платёж
- Policy: запрещённый источник или нарушение согласованных условий
- Abuse: подтверждённый автоматизированный или координированный сценарий
- Unknown: причина ещё не сопоставлена и требует карантина
Reason code должен быть стабильным машинным значением. Человеческое пояснение может уточняться, код меняется только через версию справочника. Категория fraud_without_detail непригодна: она не говорит, какое правило сработало и как его проверить.
Постройте зрелую когорту
Определите watermark по распределению decided_at − occurred_at. Для отчёта включаются события, у которых прошло согласованное окно, либо уже есть окончательное решение по правилам конкретного оффера. Нельзя выбирать универсальные 24 или 72 часа без наблюдаемой задержки и условий.
WITH current_status AS (
SELECT DISTINCT ON (conversion_id)
conversion_id, status, reason_code, terms_version,
occurred_at, decided_at, source_id, campaign_id,
amount_minor, currency
FROM conversion_decisions
ORDER BY conversion_id, status_version DESC
), mature AS (
SELECT *
FROM current_status
WHERE occurred_at < :cohort_watermark
AND status IN ('approved', 'rejected', 'reversed')
), totals AS (
SELECT terms_version, count(*) AS all_decisions
FROM mature
GROUP BY terms_version
)
SELECT m.terms_version,
m.reason_code,
count(*) AS rejected,
t.all_decisions,
round(count(*)::numeric / NULLIF(t.all_decisions, 0), 4) AS rejection_share
FROM mature AS m
JOIN totals AS t USING (terms_version)
WHERE m.status = 'rejected'
GROUP BY m.terms_version, m.reason_code, t.all_decisions
ORDER BY rejected DESC;Параметр cohort_watermark рассчитывается отдельно. Запрос разделяет terms_version: изменение правил не должно выглядеть как внезапное ухудшение источника.
Не доверяйте одному среднему проценту
Небольшой источник может показать 50% rejected после двух решений, крупный — стабильные 8% на тысячах. Вместе с долей выводите count и интервал неопределённости. Для биномиальной доли можно использовать интервал Уилсона; он ведёт себя устойчивее грубой нормальной аппроксимации на малых n. Решение о паузе всё равно учитывает денежный риск и причину.
p = rejected / n
center = (p + z² / (2n)) / (1 + z² / n)
margin = z * sqrt((p(1-p) + z² / (4n)) / n) / (1 + z² / n)
interval = [center - margin, center + margin]
z выбирается под требуемый уровень уверенности до анализа,
а не под желаемый вывод.Стратифицированная ручная выборка
Случайные сто строк из общего пула будут состоять из самой частой причины. Разделите выборку по reason_code, source_id, GEO, terms_version и неделе. Внутри каждой страты возьмите воспроизводимую случайную выборку, а отдельно добавьте события с наибольшим денежным влиянием.
WITH current_status AS (
SELECT DISTINCT ON (conversion_id) *
FROM conversion_decisions
ORDER BY conversion_id, status_version DESC
), ranked AS (
SELECT d.*,
row_number() OVER (
PARTITION BY reason_code, source_id, terms_version
ORDER BY md5(conversion_id || :audit_salt)
) AS sample_rank
FROM current_status AS d
WHERE status = 'rejected'
AND occurred_at >= :cohort_start
AND occurred_at < :cohort_end
)
SELECT *
FROM ranked
WHERE sample_rank <= :rows_per_stratum
ORDER BY reason_code, source_id, terms_version, sample_rank;MD5 здесь используется только для детерминированного порядка выборки, не для хранения секретов или защиты идентификаторов. audit_salt фиксируется в журнале конкретного аудита, чтобы выборку можно было повторить.
Проверка согласованности решения
- Открыть версию условий, действовавшую в occurred_at.
- Проверить исходное событие и payload hash.
- Подтвердить связь с click_id без ручной подстановки.
- Воспроизвести правило reason_code на обезличенных полях.
- Сверить decided_at и порядок status_version.
- Проверить одинаковое применение правила к сопоставимым источникам.
- Отделить ошибку данных от коммерческого отклонения.
- Зафиксировать спорную строку и основание пересмотра.
Денежное влияние по валютам
Не складывайте amount_minor разных валют. Сначала покажите исходные суммы по currency и модели оплаты. Для управленческого пересчёта применяйте документированный FX snapshot с датой и источником, сохраняя исходную величину. Rejected count и rejected value отвечают на разные вопросы: частая дешёвая причина может требовать автоматизации, редкая дорогая — немедленного контроля.
WITH current_status AS (
SELECT DISTINCT ON (conversion_id) *
FROM conversion_decisions
ORDER BY conversion_id, status_version DESC
)
SELECT reason_code,
currency,
count(*) AS rejected_count,
sum(amount_minor) AS rejected_amount_minor,
percentile_cont(0.5) WITHIN GROUP (ORDER BY amount_minor) AS median_amount_minor
FROM current_status
WHERE status = 'rejected'
AND occurred_at >= :cohort_start
AND occurred_at < :cohort_end
GROUP BY reason_code, currency
ORDER BY currency, rejected_amount_minor DESC;Как отличить источник от системной ошибки
Сравните одну причину между источниками после выравнивания terms_version, GEO, placement, дня недели и задержки решения. Если technical_missing_click_id растёт во всех источниках после релиза интеграции, пауза одного паблишера не исправит систему. Если geo_mismatch локализован в одной кампании при стабильной доставке, проверяется таргетинг и редирект.
Что отдавать affiliate-менеджеру
- Определение когорты и watermark
- Версия условий и словаря причин
- Агрегаты count и amount по валютам
- Обезличенная воспроизводимая выборка ID
- Примеры противоречий с payload hash и status_version
- Список вопросов, а не обвинений
- Ожидаемый SLA ответа и процедура пересмотра
Не оптимизируйте по непрозрачному rejectedЕсли программа не может объяснить reason codes, применимую версию условий и выборку решений, данные недостаточны для вывода о качестве источника. Ограничение трафика может быть мерой риска, но не доказательством фрода.
Источники и границы применимостиPostgreSQL: aggregate functions · PostgreSQL: window functions
Вывод
Сильный разбор rejected-конверсий начинается с зрелого знаменателя и версионированного решения. Таксономия причин назначает действие, стратифицированная выборка делает аудит воспроизводимым, интервалы удерживают от выводов на малом n, а суммы по валютам показывают реальный риск. Только после этого обсуждается качество источника.
Станьте партнёром и начните работать
Перейдите в партнёрскую программу, изучите актуальные условия и выберите подходящий формат сотрудничества.
СТАТЬ ПАРТНЁРОМ
