Инженерный хаб

AdTech-арбитраж в Алматы: как выжать CPC до 450 ₸ через Prebid.js и Ad Manager

09.10.2026
Шарафутдинов Р.

1. В чём заключалась реальная проблема: рабство у бартера и алгоритмов Meta

Алматинский блогер в автомобильной тематике (обзоры СТО, подбор контрактных запчастей с Car City, тюнинг на Ташкентской) за три года собрал лояльную аудиторию в 65 000 подписчиков в Instagram. Однако к весне 2026 года модель прямой рекламы зашла в глухой тупик:

  • Алгоритмическая деградация: Очередной апдейт алгоритмов Instagram срезал охваты Stories с 18 000 до 5 200 просмотров. Ссылки в шапке профиля (Bio) и стикеры-ссылки в сторис начали мгновенно пессимизироваться спам-фильтрами.
  • Токсичные микро-бюджеты: Владельцы локальных автосервисов и магазинов запчастей требовали 10 правок за интеграцию стоимостью 40 000–50 000 ₸, а согласование публикации растягивалось на две-три недели.
  • Иллюзия стандартного AdSense: Первая попытка сделать сайт и повесить базовый скрипт Google AdSense принесла разочарование: эффективный RPM на казахстанском трафике не превышал $0.65–$0.75 (~350 ₸ за 1 000 показов), а средняя цена за клик колебалась на уровне 25–40 ₸.

Причина проста: в Казахстане одиночный AdSense без внешнего давления забирает показ по минимальной возможной цене аукциона второй цены или слабого floor-порога. Чтобы заставить рекламодателей (банки Kaspi/Halyk, автодилеров, сервисы заказа запчастей) платить реальную рыночную стоимость за премиальный клик, необходима жесткая конкуренция между рекламными сетями в реальном времени.

2. Почему простое решение не подошло

Стандартные решения из общедоступных статей потерпели крах по трем фундаментальным причинам:

  1. Тяжелые плагины CMS: Установка «всеядных» плагинов рекламы на WordPress обвалила метрики Core Web Vitals (FID > 380 мс, LCP > 4.8 с). В результате показатель видимости рекламы (Viewability Rate) упал ниже 42%, что привело к автоматическому снижению ставок со стороны алгоритмов Google Smart Bidding.
  2. Последовательные водопады (Waterfall): Классическая схема «сначала крутим AdSense, если нет клика — вызываем РСЯ» создавала задержку в 2.4–3.5 секунды. Пользователь успевал проскроллить страницу до загрузки баннера, сжигая показы впустую.
  3. Отсутствие серверного контроля цен: Без динамических порогов (Dynamic Floor Pricing) баннеры выкупались дешёвыми агрегаторами по $0.15 за клик, лишая блогера основного дохода.

3. Архитектура AdTech-пайплайна: Prebid.js + Google Ad Manager + Cloud Run

Мы спроектировали высокоскоростную распределенную архитектуру на базе Google Cloud Run и Google Ad Manager (GAM), в которой Google и Яндекс соревнуются за каждый показ в прозрачном аукционе первой цены:

Архитектурная схема / Data Flow Pipeline (ASCII)
┌─────────────────────────────────────────────────────────────────────────┐
│ Браузер пользователя (Алматы / Астана, 4G / Wi-Fi)                      │
│ - Инициализация Prebid.js (Асинхронный параллельный опрос SSP/DSP)      │
└────────────────────────────────────┬────────────────────────────────────┘
                                     │
           ┌─────────────────────────┴─────────────────────────┐
           ▼ Параллельный запрос бидов (Timeout: 650 ms)       ▼
┌──────────────────────────────────────┐  ┌───────────────────────────────┐
│ Яндекс РСЯ (Yandex Header Bidding)  │  │ Независимые DSP (Rubicon/Criteo)
│ Предлагает бид: $1.40 eCPM (700 ₸)   │  │ Предлагает бид: $1.10 eCPM    │
└──────────────────┬───────────────────┘  └───────────────┬───────────────┘
                   │                                      │
                   └──────────────────┬───────────────────┘
                                      │ Победитель Header Bidding: $1.40
                                      ▼ Передача таргетинга (hb_pb=1.40)
┌─────────────────────────────────────────────────────────────────────────┐
│ Google Ad Manager (GAM) — Модуль Dynamic Allocation                     │
│ Запускает внутренний аукцион Ad Exchange / AdSense:                     │
│ Google обязан заплатить > $1.40 eCPM, чтобы перебить ставку Яндекса!     │
│ Итог: Google Ad Exchange побеждает со ставкой $1.65 eCPM                │
└─────────────────────────────────────┬───────────────────────────────────┘
                                      │
                                      ▼ Мгновенная отрисовка (Clean CSS)
┌─────────────────────────────────────────────────────────────────────────┐
│ Google Cloud Run + Cloud CDN                                            │
│ - Доставка статических PWA-шаблонов со скоростью <40 мс                 │
│ - BigQuery Stream: Сквозной когортный аудит eCPM и CPC по городам РК   │
└─────────────────────────────────────────────────────────────────────────┘

4. Программная реализация клиентского аукциона Prebid.js и GPT

Код клиентского бандла инициализирует Header Bidding с жестким окном ожидания 650 миллисекунд. Если Яндекс или внешние DSP не отвечают вовремя, управление немедленно передается Google Ad Manager без просадки рендеринга страницы:

interface PrebidAdUnit {
  code: string;
  mediaTypes: {
    banner: {
      sizes: number[][];
    };
  };
  bids: Array<{
    bidder: string;
    params: Record<string, unknown>;
  }>;
}

declare global {
  interface Window {
    pbjs: {
      que: Array<() => void>;
      addAdUnits: (units: PrebidAdUnit[]) => void;
      requestBids: (options: {
        timeout: number;
        bidsBackHandler: () => void;
      }) => void;
      setTargetingForGPTAsync: () => void;
    };
    googletag: {
      cmd: Array<() => void>;
      defineSlot: (path: string, sizes: number[][], divId: string) => any;
      pubads: () => any;
      enableServices: () => void;
      display: (divId: string) => void;
    };
  }
}

export function initHeaderBiddingAuction(slotId: string, adUnitPath: string): void {
  window.pbjs = window.pbjs || { que: [] };
  window.googletag = window.googletag || { cmd: [] };

  const PREBID_TIMEOUT_MS = 650;

  const adUnits: PrebidAdUnit[] = [
    {
      code: slotId,
      mediaTypes: {
        banner: {
          sizes: [[300, 250], [336, 280], [300, 600]]
        }
      },
      bids: [
        {
          bidder: 'yandex',
          params: {
            pageId: 1984210,
            impId: 1,
            currency: 'KZT'
          }
        },
        {
          bidder: 'rubicon',
          params: {
            accountId: '18492',
            siteId: '394812',
            zoneId: '2109482'
          }
        }
      ]
    }
  ];

  window.pbjs.que.push(() => {
    window.pbjs.addAdUnits(adUnits);
    window.pbjs.requestBids({
      timeout: PREBID_TIMEOUT_MS,
      bidsBackHandler: () => {
        window.googletag.cmd.push(() => {
          window.pbjs.setTargetingForGPTAsync();
          window.googletag.pubads().refresh();
        });
      }
    });
  });

  window.googletag.cmd.push(() => {
    window.googletag
      .defineSlot(adUnitPath, [[300, 250], [336, 280]], slotId)
      .addService(window.googletag.pubads());

    window.googletag.pubads().enableSingleRequest();
    window.googletag.pubads().collapseEmptyDivs();
    window.googletag.pubads().setCentering(true);
    window.googletag.enableServices();
  });
}

Смотреть код на GitHub (OZAT-kz)

5. Серверная конфигурация динамических порогов на Google Cloud Run

Серверный микросервис на Google Cloud Run определяет город пользователя по входящим заголовкам Cloud CDN и динамически задает пороги отсечения (Floor Prices). Это предотвращает демпинг со стороны низкокачественных DSP в премиальных регионах (Алматы и Астана):

import express, { Request, Response } from 'express';
import compression from 'compression';

const app = express();
const PORT = process.env.PORT || 8080;

interface GeoAdFloorPolicy {
  minFloorUSD: number;
  currency: string;
  preferredBidders: string[];
}

const KAZAKHSTAN_GEO_FLOORS: Record<string, GeoAdFloorPolicy> = {
  'Almaty': { minFloorUSD: 0.85, currency: 'USD', preferredBidders: ['yandex', 'rubicon', 'criteo'] },
  'Astana': { minFloorUSD: 0.75, currency: 'USD', preferredBidders: ['yandex', 'rubicon'] },
  'Shymkent': { minFloorUSD: 0.50, currency: 'USD', preferredBidders: ['yandex'] },
  'Default': { minFloorUSD: 0.40, currency: 'USD', preferredBidders: ['yandex'] }
};

app.use(compression());

app.get('/api/adtech/auction-config', (req: Request, res: Response) => {
  const cityHeader = (req.headers['x-client-city'] as string) || 'Default';
  const policy = KAZAKHSTAN_GEO_FLOORS[cityHeader] || KAZAKHSTAN_GEO_FLOORS['Default'];

  res.setHeader('Cache-Control', 'public, max-age=60, s-maxage=300');
  res.setHeader('Content-Type', 'application/json');

  res.json({
    timeoutMs: 650,
    geoCity: cityHeader,
    floorPriceCpm: policy.minFloorUSD,
    currency: policy.currency,
    bidders: policy.preferredBidders,
    priceGranularity: {
      buckets: [
        { max: 5.0, increment: 0.05 },
        { max: 15.0, increment: 0.25 },
        { max: 30.0, increment: 1.00 }
      ]
    }
  });
});

app.listen(PORT, () => {
  console.log(`AdTech Edge Gateway active on port ${PORT}`);
});

Смотреть код на GitHub (OZAT-kz)

6. BigQuery: Анализ эффективности аукциона и ценового спреда

Для оценки реальной отдачи аукциона мы настроили стриминг рекламных событий в Google BigQuery. Следующий аналитический запрос вычисляет динамику eCPM, среднюю цену за клик (CPC) в тенге и распределение побед между Google и РСЯ:

SELECT
  DATE(event_timestamp) AS auction_date,
  traffic_source,
  geo_city,
  COUNT(1) AS total_ad_impressions,
  ROUND(AVG(winning_bid_usd) * 1000, 2) AS calculated_ecpm_usd,
  ROUND(AVG(cpc_value_kzt), 1) AS avg_cpc_kzt,
  ROUND(SUM(CASE WHEN winning_bidder = 'google_dynamic_allocation' THEN 1 ELSE 0 END) * 100.0 / COUNT(1), 1) AS google_win_share_pct,
  ROUND(SUM(CASE WHEN winning_bidder = 'yandex_rsya' THEN 1 ELSE 0 END) * 100.0 / COUNT(1), 1) AS yandex_win_share_pct,
  ROUND(SUM(estimated_revenue_usd) * 500.0, 0) AS total_daily_revenue_kzt
FROM
  `ozat-open-datasets.ozat_cloud_knowledge_hub.adtech_auction_events`
WHERE
  event_timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
  AND country_code = 'KZ'
GROUP BY
  auction_date,
  traffic_source,
  geo_city
ORDER BY
  auction_date DESC,
  total_daily_revenue_kzt DESC;

Смотреть код на GitHub (OZAT-kz)

7. Результаты внедрения: Метрики и FinOps

После 30 дней эксплуатации гибридного аукциона на независимом портале блогера мы зафиксировали следующие показатели (трафик: 14 000 уникальных посетителей в сутки, ~42 000 просмотров страниц):

Сравнение метрик монетизации: Одиночный AdSense vs Prebid + GAM

  • Рост eCPM: С $0.68 до $3.42 (+402% относительно базового AdSense).
  • Предельная цена клика (Max CPC): Достигла 415 ₸ в категориях «Контрактные автозапчасти», «КАСКО онлайн» и «Шиномонтаж Алматы».
  • Viewability Rate: 81.4% (за счет адаптивной ленивой загрузки и фиксированного соотношения сторон блоков).
  • Месячный доход: Вырос со 142 800 ₸ до 820 000 ₸ чистыми. При этом расходы на Cloud Run уложились в бесплатный уровень Google Cloud Free Tier ($0/мес).

Ежемесячный чистый доход контентного блога (₸)

⚠️ Реальные ограничения и компромиссы архитектуры

  • 1. Таймаут аукциона (Client-Side Latency): Увеличение таймаута Prebid свыше 800 мс на мобильном 3G/4G приводит к задержке контента и росту отказов (Bounce Rate) на +12.4%. Лимит 650 мс является строгим инженерным оптимумом.
  • 2. Риск недействительного трафика (Invalid Traffic / IVT): Прямой нагон немодерируемого трафика из Telegram-пабликов вызывает волну случайных кликов. AdSense накладывает фильтр ограничения показов (Ad Serving Limit), если доля Invalid Clicks превышает 2.1%.
  • 3. Политика Better Ads Standards: Запрещены всплывающие межстраничные баннеры с обратным отсчетом на смартфонах. Нарушение ведет к полной блокировке рекламного блока в Chrome.
  • 4. Налоговый комплаенс и банковский комплаенс РК: Выплаты от Google (Ирландия) и Яндекса требуют корректного прохождения валютного контроля в банках РК (Kaspi Pay, Halyk, BCC), заполнения формы W-8BEN и уплаты налогов по режиму СНР / упрощенной декларации.

Спец-курс «Издатель: Монетизация» — от нуля до легальных выплат Google

Собрать клиентский скрипт — это только малая часть успеха. Самый сложный этап для казахстанских авторов — пройти придирчивую модерацию Google Publisher Policy, защитить сайт от бот-трафика (Invalid Traffic), легально привязать валютный расчетный счет в банках РК (Kaspi, Halyk, BCC) и правильно оформить налоговую форму W-8BEN. В нашем прикладном спец-курсе за 80 000 ₸ вы получаете готовые шаблоны скоростных медиа-порталов с Core Web Vitals 95+ и персональное менторство инженеров OZAT.

Изучить программу курса «Издатель: Монетизация»

💡 Совет OZAT: Готовы к внедрению? Рассчитайте архитектуру и бюджет через Scope Builder или пройдите бесплатный ИИ-аудит.

Рустам Шарафутдинов

Рустам Шарафутдинов

Автор Инженерного хаба

Эксперт в области архитектуры Google Cloud и Senior Full-Stack разработчик с более чем 15-летним опытом. Специализируется на отказоустойчивых архитектурах, оптимизации высоконагруженных проектов и интеграции AI (Vertex AI).

Экспертность: GCP, Kubernetes, Микросервисы, React, Node.js

Комментарии (0)