A13 · Value bidding, budgets, and ROAS constraints
Prerequisites: foundations 31–34; A12 (chapter 22).
Evidence: explicitly constructed mechanism experiments, not real ad logs. This chapter does not claim industrial-system reproduction or online gains.
Learning order: predict → change one variable → run → explain.
English companion. All instructional Markdown is translated. Experiment code, Chinese language examples, execution counts, and saved outputs remain as in the source edition; historical output text may therefore be Chinese. The initial setup cell anchors the working directory and Python import path for this companion's deeper folder; it does not change the experiment. Saved outputs are historical evidence, not a new run. Run from the top after preparing the required data and models.
# English companions live one directory deeper; keep the original experiment code unchanged.
from pathlib import Path as _TranslationPath
import os as _translation_os
import sys as _translation_sys
_translation_root = next((p for p in [_TranslationPath.cwd(), *_TranslationPath.cwd().parents]
if (p / "reclab").is_dir()), None)
if _translation_root is None:
raise RuntimeError("Open this notebook from inside the Search Recommendation Ads Lab checkout.")
_translation_os.chdir(_translation_root)
if str(_translation_root) not in _translation_sys.path:
_translation_sys.path.insert(0, str(_translation_root))
Problem and input/output contract
For risk-neutral independent second-price auctions without budgets, value bid b=pCTCVR×conversion_value is value per impression. Target-CPA bidding uses b=target_CPA×pCTCVR; a target-ROAS threshold uses b=value/target_ROAS. These depend on reliable probabilities and billing definitions. Optimal first-price bidding also depends on win probability and cannot copy the same rule. Budget constraints couple requests and introduce opportunity costs or dual prices.
Before running, predict which result the changed variable will affect and which invariants must hold.
from pathlib import Path
import sys
root=Path.cwd()
if not (root/'reclab').exists(): root=root.parent
if str(root) not in sys.path: sys.path.insert(0,str(root))
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from reclab import ads_curriculum as acRun the one-variable comparison
Inputs are small constructed examples visible in source. Output units and constraints are defined above. Run assertions to verify mathematics and accounting.
rows=ac.constrained_bidding();display(pd.DataFrame(rows))
assert all(r['spend']<=4 for r in rows)
assert rows[1]['expected_roas']>=2
print('更紧预算',ac.constrained_bidding(budget=2))Saved notebook output · not executed on this page
| policy | spend | expected_value | expected_roas | win | |
|---|---|---|---|---|---|
| 0 | value | 3.6 | 7.0 | 1.944444 | [True, True, True, False, True, False] |
| 1 | roas_cap | 3.5 | 9.0 | 2.571429 | [False, True, False, False, False, True] |
更紧预算 [{'policy': 'value', 'spend': np.float64(1.9), 'expected_value': np.float64(4.5), 'expected_roas': np.float64(2.368421052631579), 'win': [True, True, False, False, False, False]}, {'policy': 'roas_cap', 'spend': np.float64(1.5), 'expected_value': np.float64(4.0), 'expected_roas': np.float64(2.6666666666666665), 'win': [False, True, False, False, False, False]}]
Interpretation and limits
Hold the constructed market fixed and change value bidding to ROAS-threshold bidding. Even meeting expected ROAS does not guarantee realized finite-sample ROAS. Hard budget truncation is not globally value-optimal; examine feedback and arrival order in the next unit.
Inspect source, then try an exercise
Enumerate all budget-feasible subsets of six requests to obtain an offline knapsack upper bound. Compare both strategies’ gaps to that bound.
import inspect
print(inspect.getsource(ac.constrained_bidding))Saved notebook output · not executed on this page
def constrained_bidding(budget=4., target_roas=2.):
# Known constructed pCTCVR and conversion values; prices independent of bid.
p=np.array([.01,.08,.02,.12,.03,.1]); value=p*50
price=np.array([.4,1.5,.7,2.8,1.,2.])
out=[]
for name, bids in [('value',value),('roas_cap',value/target_roas)]:
spent=reward=0.; decisions=[]
for v,c,b in zip(value,price,bids):
win=b>=c and spent+c<=budget+1e-12
if win: spent+=c;reward+=v
assert spent<=budget+1e-12
decisions.append(bool(win))
out.append(dict(policy=name,spend=spent,expected_value=reward,expected_roas=reward/spent if spent else None,win=decisions))
return out
Sources and next step
A-RTB pp. 71–91. This is an original Lab exercise, not a copy of the complete lecture text.
Chinese GitHub chapter · Authors’ RTB slides. The Chinese deck is not a complete modern advertising-engineering course. Historical descriptions of platforms are not presented as current market facts.
After the exercise, return to the ads learning path and continue to the next semantic unit.