Расписание выкупов с jitter: как мы распределяем заказы, чтобы не было «всплеска как у бота»

Ровный график выкупов — это пульс мертвеца на кардиомониторе. Ровная линия, один и тот же интервал, ноль жизни. Антифрод считывает её за секунду.

Поэтому Toptaker не ставит выкупы пачкой и не размазывает их идеально ровно — оба варианта палятся. Вместо этого incremental scheduler на max-heap (двоичная куча, где наверху всегда самый большой элемент) ищет самый широкий пробел в уже занятом графике, ставит туда следующий заказ и добавляет jitter — случайный сдвиг по времени. Получается распределение, в котором нет ни «тика метронома», ни свалки заказов в один час.

Дальше — как это устроено внутри и почему два «очевидных» способа расписать выкупы (ровно и кучей) выдают тебя с головой.

Почему ровный график палится

Кажется логично: 30 выкупов на 30 дней — значит по одному в день, в одно и то же время. Просто, предсказуемо, удобно считать.

Это и есть проблема. Естественный спрос так не выглядит. Реальные покупатели заказывают рвано: всплеск в выходные, провал в среду, ночной заказ, утренний.

А когда у карточки 20 дней подряд ровно один заказ в 14:00 — это не покупатели. Это скрипт.

Антифрод WB по нашим оценкам ловит около 98% накрутки и смотрит на 200+ параметров. Регулярность интервала — самый дешёвый для них сигнал: посчитать дисперсию времени между заказами умеет даже примитивный запрос. Слишком низкая дисперсия = бот.

Цена ошибки высокая. При поимке снимают СПП (скидку постоянного покупателя) на 30 дней — и по всему ассортименту, а не только по «спалённой» карточке.

Почему кучный график палится ещё хуже

Второй типовой способ — «давай открутим 30 выкупов за два дня, чтобы быстрее поднять карточку».

Всплеск заказов на ровном месте — без рекламы, без трафика, без сезона — это аномалия, которую видно невооружённым глазом. У товара неделю не было продаж, и вдруг 15 заказов за вечер. Откуда?

Резкий градиент спроса — такой же красный флаг, как и идеальная ровность. Антифроду не нужны 200 параметров, чтобы это заметить; хватает производной по количеству заказов во времени.

Что делает incremental scheduler

Мы расписываем выкупы инкрементально, по одному, а не раскидываем всю пачку сразу формулой «период / количество».

Механика:

  1. Берём окно (например, 14 дней) и уже занятые точки графика — это могут быть прошлые выкупы, дни без активности, ограничения по дневному окну пользователя.
  2. Строим max-heap из пробелов (gap) между соседними точками. Наверху кучи — самый большой свободный промежуток.
  3. Достаём верхушку, ставим новый выкуп в середину этого пробела. Пробел разбивается на два меньших — оба возвращаются в кучу.
  4. Повторяем, пока не разложим все заказы.

Сложность — O(n log n): каждая вставка это extract-max и два push, каждый log n. Для 30–50 выкупов это микросекунды.

Но важно не быстродействие. Важно, что заказы садятся туда, где график реже всего — и распределение само собой выходит близким к равномерному по плотности, но без жёсткой решётки.

Зачем нужен jitter поверх max-heap

Сам по себе max-heap детерминирован: одинаковый вход — одинаковые точки. Два клиента с похожим объёмом получили бы один и тот же «скелет» расписания.

А скелет — это паттерн. Паттерн — ровно то, что антифрод и ищет.

Поэтому каждую посчитанную точку мы сдвигаем на случайную величину — jitter (дрожание). Сдвиг идёт и по времени внутри дня, и по самому дню в пределах допустимого окна. Параметры дрожания берутся из дневного окна пользователя (когда он реально готов, чтобы заказы шли в его рабочее время), так что выкуп не вылезет в 03:00 — отдельная история, но смысл тот же: время заказа должно быть правдоподобным.

Итог: плотность распределения задаёт max-heap (равномерно, без куч), а микрорисунок задаёт jitter (нерегулярно, без решётки). Дисперсия интервалов получается такой, как у живого спроса — ненулевой, но и не хаотичной.

Чем это отличается от «расписать в Excel»

Большинство сервисов и тем более ручных схем делают одно из двух: делят период на количество (ровно) или гонят пачкой (кучно). И то, и другое — линейная функция, которую антифрод раскладывает в одну строчку.

Разница на уровне подхода:

  • Ровный делитель: interval = период / n. Нулевая дисперсия по времени суток. Палится.
  • Пачка: всё за пару дней. Резкий градиент. Палится.
  • Toptaker: max-heap по пробелам + jitter. Плотность ровная, микрорисунок случайный. Похоже на спрос.

Это та же логика, по которой мы не шлём запросы голым Node fetch (его TLS-fingerprint — отпечаток TLS-рукопожатия, по которому сервер отличает браузер от скрипта — виден сразу), а используем пресет под реальный мобильный клиент. Бессмысленно вылизывать сетевой слой, если расписание заказов кричит «бот». Антифрод смотрит на всю картину сразу. Подробнее про подход — в чём Toptaker отличается.

Сколько выкупов вообще ставить

Расписание решает «когда», но не «сколько». Объём считается от ниши, конкурентов и текущей позиции карточки — слишком мало не сдвинет выдачу, слишком много даст тот самый подозрительный градиент, который не спасёт никакой jitter. Как прикинуть число под свою карточку — разобрали отдельно: сколько выкупов нужно.

Скажу честно, как продавец с пятью годами на WB: «100% безопасно» в самовыкупах не бывает, это серая зона. Грамотное расписание снижает риск примерно до 95%, а не до нуля. Кто обещает ноль — врёт или не понимает, как это работает.

Но между «спалиться на ровном тике за неделю» и «выглядеть как живой спрос» разница — именно в таких деталях.

FAQ

Что такое incremental scheduler в Toptaker? Алгоритм, который добавляет выкупы в график по одному, каждый раз в самый широкий свободный пробел. Реализован на max-heap (куче пробелов), сложность O(n log n).

Зачем нужен jitter в расписании самовыкупов? Чтобы убрать регулярность. Max-heap даёт ровную плотность, но детерминированные точки — это паттерн. Случайный сдвиг (jitter) по времени и дню ломает решётку, и интервалы между заказами выглядят как у живого спроса.

Почему нельзя просто заказывать по одному в день в одно время? Нулевая дисперсия интервалов — дешёвый сигнал для антифрода. Реальные покупатели так не заказывают. Ровный график WB ловит как накрутку.

Что будет, если антифрод WB поймает накрутку? Снимают СПП (скидку постоянного покупателя) на 30 дней — причём по всему ассортименту продавца, а не только по одной карточке. По нашим оценкам антифрод отлавливает около 98% накрутки по 200+ параметрам.

Гарантирует ли расписание безопасность? Нет. Самовыкупы — серая зона, гарантий не бывает. Грамотное распределение снижает риск примерно до 95%, но не до нуля.

Расписание — это не «когда тебе удобно», а «когда поверит антифрод». Живой спрос дышит. Метроном — нет, и его слышно за версту.

Хотите так же — но без ручной возни?

Toptaker делает выкупы и отзывы автоматически: SRID, чеки и скриншоты по каждому действию. 5 выкупов бесплатно при регистрации.