AI Real-Time Bidding (RTB) Optimization System

We design and deploy artificial intelligence systems: from prototype to production-ready solutions. Our team combines expertise in machine learning, data engineering and MLOps to make AI work not in the lab, but in real business.
Showing 1 of 1 servicesAll 1566 services
AI Real-Time Bidding (RTB) Optimization System
Complex
~2-4 weeks
FAQ
AI Development Areas
AI Solution Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1212
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    822

Real-time RTB optimization

Real-Time Bidding is a 100-millisecond auction in which each participant simultaneously decides three tasks: whether to participate at all, what is the maximum bid, and how to balance bidding aggressiveness with budget. Optimizing this process involves more than just predicting CTR; it involves managing a multidimensional space of tradeoffs under conditions of incomplete information.

Three levels of RTB optimization

Request level — a decision in < 10ms for a specific bid request: whether to participate or not, and at what price. CTR/CVR models and bid shading algorithms are used here.

Campaign level – manage budget, frequency, and targeting on an hourly/daily basis. Pacing algorithms, frequency cap, and audience exclusion.

Portfolio level - budget allocation between campaigns, channels and formats based on projected ROI.

Optimal betting strategy

import numpy as np
from scipy import stats
from scipy.optimize import minimize_scalar
import pandas as pd

class OptimalBiddingStrategy:
    """
    Математически обоснованная стратегия ставок.
    Базируется на теории механизмов аукционов и оптимальном управлении.
    """

    def __init__(self, campaign_goal: str = 'cpa'):
        """
        campaign_goal: 'cpa' | 'ctr' | 'roas' | 'awareness'
        """
        self.goal = campaign_goal

    def compute_bid_landscape(self, historical_auctions: pd.DataFrame,
                               floor_price: float) -> dict:
        """
        Оценка конкурентного ландшафта аукциона.
        historical_auctions: winning_price, floor_price, won (bool)
        """
        winning_prices = historical_auctions[historical_auctions['won']]['winning_price']

        if len(winning_prices) < 50:
            return {'distribution': 'unknown', 'p50': floor_price * 2}

        # Подбираем распределение победных цен
        # Log-normal хорошо описывает цены в RTB
        params = stats.lognorm.fit(winning_prices, floc=0)
        dist = stats.lognorm(*params)

        return {
            'distribution': 'lognorm',
            'params': params,
            'p25': dist.ppf(0.25),
            'p50': dist.ppf(0.50),
            'p75': dist.ppf(0.75),
            'p90': dist.ppf(0.90),
            'mean': float(winning_prices.mean()),
        }

    def optimal_cpa_bid(self, predicted_cvr: float,
                         target_cpa: float,
                         bid_landscape: dict,
                         budget_remaining: float,
                         impressions_remaining: int) -> float:
        """
        Оптимальная ставка для цели CPA.
        Максимизирует число конверсий при соблюдении eCPA <= target_cpa.
        """
        # Valuation: сколько стоит одно впечатление для нас
        valuation = predicted_cvr * target_cpa * 1000  # В CPM

        if bid_landscape.get('distribution') == 'unknown':
            return valuation * 0.7  # Консервативно без данных

        # Для second-price auction: bid = valuation (dominant strategy)
        # Для first-price: применяем bid shading

        params = bid_landscape['params']
        dist = stats.lognorm(*params)

        def expected_profit(bid_cpm):
            win_prob = dist.cdf(bid_cpm)
            expected_payment = bid_cpm  # First-price (мы платим свою ставку)
            profit = win_prob * (valuation - expected_payment)
            return -profit  # Минус для минимизации

        result = minimize_scalar(
            expected_profit,
            bounds=(0.01, valuation * 1.5),
            method='bounded'
        )

        optimal_bid = result.x

        # Корректировка на бюджетный дефицит
        if impressions_remaining > 0:
            avg_bid_needed = budget_remaining / impressions_remaining * 1000
            # Не ставим выше среднего необходимого
            optimal_bid = min(optimal_bid, avg_bid_needed * 2)

        return round(float(optimal_bid), 4)

    def compute_efficiency_frontier(self, bid_range: np.ndarray,
                                     cvr_model,
                                     bid_landscape: dict) -> pd.DataFrame:
        """
        Кривая эффективности: для каждого уровня ставки считаем
        ожидаемое число конверсий и стоимость за конверсию.
        """
        results = []

        params = bid_landscape.get('params')
        if params is None:
            return pd.DataFrame()

        dist = stats.lognorm(*params)

        for bid in bid_range:
            win_prob = float(dist.cdf(bid))
            expected_conversions_per_1k = win_prob * cvr_model.get('avg_cvr', 0.02)
            cost_per_conversion = bid / max(expected_conversions_per_1k, 1e-6)

            results.append({
                'bid_cpm': bid,
                'win_probability': round(win_prob, 3),
                'expected_conversions_per_1k': round(expected_conversions_per_1k, 4),
                'ecpa': round(cost_per_conversion, 2),
            })

        return pd.DataFrame(results)


class MultiObjectiveBidOptimizer:
    """
    Оптимизация ставок при нескольких целях одновременно.
    Типичный сценарий: минимизировать CPA И удержать долю показов.
    """

    def pareto_optimal_bid(self, predicted_ctr: float,
                            predicted_cvr: float,
                            weights: dict) -> float:
        """
        Взвешенная комбинация нескольких объективов.
        weights: {'cpa': 0.6, 'reach': 0.2, 'viewability': 0.2}
        """
        target_cpa = weights.get('target_cpa', 10.0)
        reach_weight = weights.get('reach', 0.2)

        # Базовая ценность от конверсий
        conversion_value = predicted_ctr * predicted_cvr * target_cpa * 1000

        # Бонус за охват (если цель = awareness)
        reach_bonus = weights.get('reach_bonus_cpm', 0) * reach_weight

        return conversion_value + reach_bonus

    def adjust_for_viewability(self, base_bid: float,
                                predicted_viewability: float,
                                viewability_target: float = 0.70) -> float:
        """
        Снижаем ставку за невидимые показы.
        Если viewability = 40% при цели 70% → понижающий коэф.
        """
        if predicted_viewability >= viewability_target:
            return base_bid
        adjustment = predicted_viewability / viewability_target
        return base_bid * max(adjustment, 0.5)  # Минимум 50% от базовой


class BidThrottlingController:
    """
    Управление темпом участия в аукционах.
    Цель: расходовать бюджет равномерно, не участвуя в каждом аукционе.
    """

    def __init__(self, daily_budget: float, daily_impression_forecast: int):
        self.daily_budget = daily_budget
        self.daily_impressions = daily_impression_forecast
        self.avg_cpm = daily_budget / daily_impression_forecast * 1000

    def compute_participation_rate(self, spent_pct: float,
                                    time_elapsed_pct: float) -> float:
        """
        Процент bid requests, в которых участвуем.
        spent_pct: доля бюджета, потраченная за сегодня
        time_elapsed_pct: доля суток, прошедшая
        """
        # Нормальный темп: spent_pct ≈ time_elapsed_pct
        deviation = spent_pct - time_elapsed_pct

        if deviation > 0.15:
            # Тратим слишком быстро — жёсткий throttling
            return max(0.3, 1.0 - deviation * 3)
        elif deviation < -0.15:
            # Тратим слишком медленно — агрессивное участие
            return min(1.0, 1.0 + abs(deviation) * 2)
        else:
            return 1.0

    def should_bid(self, request_id: str, participation_rate: float) -> bool:
        """Детерминированный sampling по request hash"""
        hash_val = hash(request_id) % 10000 / 10000
        return hash_val < participation_rate

Win Rate Optimization and A/B Testing

class BidExperimentManager:
    """
    Многорукий бандит для выбора оптимальной стратегии ставок.
    Thompson Sampling: балансирует exploration vs exploitation.
    """

    def __init__(self, strategies: list[str]):
        self.strategies = strategies
        # Beta распределение для каждой стратегии: (wins, losses)
        self.alpha = {s: 1.0 for s in strategies}
        self.beta = {s: 1.0 for s in strategies}
        self.conversions = {s: 0 for s in strategies}
        self.spend = {s: 0.0 for s in strategies}

    def select_strategy(self) -> str:
        """Thompson Sampling: выбираем стратегию с наибольшим семплом"""
        samples = {
            s: np.random.beta(self.alpha[s], self.beta[s])
            for s in self.strategies
        }
        return max(samples, key=samples.get)

    def update(self, strategy: str, won: bool,
                converted: bool, spend: float):
        """Обновление статистики после аукциона"""
        if won:
            self.alpha[strategy] += int(converted)
            self.beta[strategy] += int(not converted)
            self.conversions[strategy] += int(converted)
            self.spend[strategy] += spend

    def get_strategy_stats(self) -> pd.DataFrame:
        """Текущая эффективность стратегий"""
        rows = []
        for s in self.strategies:
            total = self.alpha[s] + self.beta[s] - 2
            conv_rate = self.alpha[s] / (self.alpha[s] + self.beta[s])
            cpa = self.spend[s] / max(self.conversions[s], 1)
            rows.append({
                'strategy': s,
                'auctions_won': int(total),
                'conversions': self.conversions[s],
                'estimated_cvr': round(conv_rate, 4),
                'ecpa': round(cpa, 2),
                'confidence_lower': round(np.percentile(
                    np.random.beta(self.alpha[s], self.beta[s], 10000), 5
                ), 4),
            })
        return pd.DataFrame(rows).sort_values('ecpa')

RTB Optimization Metrics

Metric Value Way to improve
Win Rate 15-35% Increase bids, narrow targeting
eCPA target ±20% Improve CVR model
Budget Utilization 85-95% Pacing algorithm
Impression Share calculated Expand Targeting
Bid Shading Rate 10-30% savings Historical data

The key metric isn't the minimum bid, but maximum efficiency at the target CPA. Bid shading systems save 15-25% of the budget on first-price auctions with the same conversion rate. The payback period for this model is 2-4 weeks with a volume of 50,000 auctions per day.