How AI Is Changing the Approach to Real-Time Bidding?
A typical mistake: an advertiser sets a fixed CPM bid of $5 on a first-price auction and wonders why eCPA rises and the budget runs out in an hour. Without predictive models and bid shading, an RTB campaign is a shot in the dark. AI-powered Real-Time Bidding optimization not only predicts CVR and CTR but also dynamically manages bids to boost win rate and lower CPA.
We build AI systems that solve three problems in 100 milliseconds: whether to join an auction, what the optimal bid is, and how to distribute the budget across campaigns. Our solutions include predictive models, adaptive bid shading, and auto-pacing based on Thompson Sampling. Result: win rate grows by 15-35%, and eCPA drops to the target range.
How AI Optimizes Bids in RTB
An AI system works on three levels: request, campaign, portfolio. At the request level, within <10ms, a decision is made for each bid request: join or not, and at what price. Here, CTR/CVR models (gradient boosting or neural networks) and bid shading algorithms are used. At the campaign level — budget management with pacing strategies adapting to daily traffic patterns. At the portfolio level — budget allocation among campaigns based on predicted ROI.
Consider a case: an e-commerce client with a target eCPA of $10 and a daily budget of $500. Historical data: 200,000 auctions over 2 weeks. We deployed a system with Thompson Sampling and bid shading. Over 3 weeks, eCPA dropped from $12 to $9.8, and win rate rose from 18% to 24%. The secret lies in adaptive strategy exploration: the algorithm automatically tests different approaches (conservative, aggressive, viewability-aware) and picks the best one. Thompson Sampling reduces tuning time by 40% compared to traditional A/B testing.
Mathematical Foundation
import numpy as np
from scipy import stats
from scipy.optimize import minimize_scalar
import pandas as pd
class OptimalBiddingStrategy:
"""
Mathematically sound bidding strategy.
Based on auction mechanism theory and optimal control.
"""
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:
"""
Estimate the competitive auction landscape.
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}
# Fit distribution of winning prices
# Log-normal fits RTB prices well
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:
"""
Optimal bid for CPA goal.
Maximizes conversions while keeping eCPA <= target_cpa.
"""
# Valuation: how much one impression is worth to us
valuation = predicted_cvr * target_cpa * 1000 # In CPM
if bid_landscape.get('distribution') == 'unknown':
return valuation * 0.7 # Conservative without data
# For second-price auction: bid = valuation (dominant strategy)
# For first-price: apply 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 (we pay our bid)
profit = win_prob * (valuation - expected_payment)
return -profit # Negative for minimization
result = minimize_scalar(
expected_profit,
bounds=(0.01, valuation * 1.5),
method='bounded'
)
optimal_bid = result.x
# Adjust for budget deficit
if impressions_remaining > 0:
avg_bid_needed = budget_remaining / impressions_remaining * 1000
# Don't bid higher than twice the average needed
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:
"""
Efficiency frontier: for each bid level, compute
expected conversions and cost per conversion.
"""
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:
"""
Bid optimization with multiple simultaneous goals.
Typical scenario: minimize CPA AND maintain impression share.
"""
def pareto_optimal_bid(self, predicted_ctr: float,
predicted_cvr: float,
weights: dict) -> float:
"""
Weighted combination of multiple objectives.
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)
# Base value from conversions
conversion_value = predicted_ctr * predicted_cvr * target_cpa * 1000
# Reach bonus (if goal = 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:
"""
Reduce bid for non-viewable impressions.
If viewability = 40% with target 70% -> downward adjustment.
"""
if predicted_viewability >= viewability_target:
return base_bid
adjustment = predicted_viewability / viewability_target
return base_bid * max(adjustment, 0.5) # At least 50% of base
class BidThrottlingController:
"""
Control auction participation rate.
Goal: spend budget evenly, not participating in every auction.
"""
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:
"""
Percentage of bid requests to participate in.
spent_pct: fraction of budget spent today
time_elapsed_pct: fraction of the day elapsed
"""
# Normal pace: spent_pct ≈ time_elapsed_pct
deviation = spent_pct - time_elapsed_pct
if deviation > 0.15:
# Spending too fast -> hard throttling
return max(0.3, 1.0 - deviation * 3)
elif deviation < -0.15:
# Spending too slow -> aggressive participation
return min(1.0, 1.0 + abs(deviation) * 2)
else:
return 1.0
def should_bid(self, request_id: str, participation_rate: float) -> bool:
"""Deterministic sampling by request hash"""
hash_val = hash(request_id) % 10000 / 10000
return hash_val < participation_rate
Why Is Bid Shading Critical for First-Price Auctions?
In first-price auctions, you pay your own bid, not the second price. Without bid shading, you overpay by 15-30%. Our adaptive bid shading uses historical winning price distribution (usually log-normal) and finds the bid that maximizes expected profit. The result is a budget saving of 15-25% at the same conversion rate — twice as effective as a fixed discount factor.
| Approach | Budget Savings | Implementation Complexity | Risk of Missing Impressions |
|---|---|---|---|
| Fixed discount factor | 5-10% | Low | High (bid may be too low) |
| Adaptive bid shading | 15-25% | Medium | Low (adjusts to market) |
How to Increase Win Rate with Thompson Sampling?
Thompson Sampling is a Bayesian multi-armed bandit approach balancing exploration and exploitation. We use it to choose bidding strategies at the auction level.
class BidExperimentManager:
"""
Multi-armed bandit for selecting optimal bidding strategy.
Thompson Sampling: balances exploration vs exploitation.
"""
def __init__(self, strategies: list[str]):
self.strategies = strategies
# Beta distribution for each strategy: (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: choose strategy with highest sample"""
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):
"""Update statistics after auction"""
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:
"""Current performance of strategies"""
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')
Which Metrics Matter in RTB Optimization?
| Metric | Typical Value | Improvement Method |
|---|---|---|
| Win Rate | 15-35% | Raise bids, narrow targeting, improve CVR model |
| eCPA | target ± 20% | Calibrate CVR model, bid shading |
| Budget Utilization | 85-95% | Adaptive pacing |
| Impression Share | calculated | Expand targeting or raise bids |
| Bid Shading Rate | 15-25% savings | Train on historical data |
The key metric is maximum efficiency at the target eCPA. Systems with bid shading save up to 25% budget on first-price auctions. Model payback horizon is 2-4 weeks at a volume of 50,000+ auctions per day.
What’s Included in Deliverables
- Architecture documentation — model descriptions, pipelines, API contracts.
- Trained models — serialized CVR/CTR, bid shading, pacing models.
- Pipeline code — training, validation, deployment scripts.
- Monitoring dashboards — Grafana + Prometheus for metric tracking.
- Client team training — workshop and operations documentation.
AI RTB System Implementation Process
- Analytics — collect and analyze historical data (auctions, conversions, logs). Define goals: eCPA, ROAS, reach.
- Design — develop architecture: bidding models, bid shading, pacing. Select framework (PyTorch, HuggingFace, scikit-learn).
- Development and training — train CVR/CTR models, calibrate, validate on historical data.
- Testing — A/B experiment: new strategy vs current. Monitor key metrics.
- Deployment — integrate with RTB platform via API, set up monitoring and alerts.
Common RTB Optimization Mistakes
- Ignoring viewability: bidding on non-viewable impressions wastes budget. Our models adjust bids based on predicted viewability.
- Using first-price auction without bid shading: you overpay 15-30%. We implement adaptive bid shading based on winning price distribution.
- Insufficient historical data: models need at least 50,000 auctions per day for stable performance. Less — we use Bayesian approaches.
- Choosing the wrong optimization metric: CTR does not always correlate with conversion. We optimize directly for eCPA or ROAS.
Timelines and Pricing
Development and implementation timelines range from 4 to 8 weeks, depending on integration complexity and data quality. Pricing is calculated individually based on auction volume, number of campaigns, and required model accuracy. We guarantee transparency: you receive a clear specification and phased delivery of results.
Ready to boost your RTB campaign performance? Request a free audit of your current strategy — it takes no more than an hour. Get a consultation from an engineer who truly understands RTB.
Data based on 15+ projects across e-commerce, fintech, and gaming. For additional information, refer to the Real-Time Bidding documentation.







