-
Notifications
You must be signed in to change notification settings - Fork 0
How to write a bidding agent
In this document we'll write a simple fixed price bidding agent. This will be made up of three major components:
- The agent's configuration which controls the type of bid requests that the router will forward to the agent.
- The bidding logic which decides how much we want to bid on each spots.
- The pacer which controls the rate at which we spend our budget. The annotated source code for the full bidding agent that we will present in this example is available rtbkit's repository.
In this section, we'll create an AgentConfig object which will hold basic information about our bidding agent as well as setup various filters which will ensure that the routers will only forward bid requests that we're interesting in.
Let's get started:
AgentConfig config;
void setConfig()
{
config = AgentConfig();
Pretty straightforward begining but we had to start somewhere.
config.account = {"a", "very", "long", "account", "name", "thingy"};
Here we set up the account using the new and fancy C++11 initialization list[^1]. Accounts are used to keep track of the budget associated with each campaign and they're organized in a hierarchy. How this hierarchy is setup is entirely up to the writer of the bidding agents. A typical setup would be <campaign>.<strategy> but, as you can see in the example, you can use whatever silly setup your mind can conjure.
A more lengthy description of accounts can be found in the banker's documentation.
config.creatives.push_back(Creative(160, 600, "LeaderBoard"));
config.creatives.push_back(Creative(300, 250, "BigBox"));
config.creatives.push_back(Creative(728, 90, "LeaderBoard"));
This bit sets up the format of the creatives that our agent is interesting to bid on. Any bid requests that doesn't contain at least one spot that fits one of these impression formats will be filtered out by the router and will never make it to our bidding agent. Less work for us, woo!
In addition to this mandatory filter, there are several other optional filters that can be specified:
-
hosts and urls
-
languages and locations
-
segments
-
exchanges
-
postion relative to the page's fold
-
hour of the week
-
user partitions
-
augmentation tags (see below)
config.addAugmentation("frequency-cap-ex", Json::Value(42)); config.augmentationFilter.include.push_back("pass-frequency-cap-ex");
Next, we tell the router that we'd be interested in having our bid requests augmented by our frequency cap example and set the frequency cap ceiling to 42. Since we don't want to see bid requests that haven't been augmented or that have hit our frequency cap ceiling, we also tell the router to filter out any bid request that doesn't contain the augmentation tag set by the frequency cap example.
In this case, we use the augmentors output purely for filtering but it's still possible to retrieve information injected by an augmentor while we're making our bid decision.
doConfig(config.toJson());
}
Finally, we tell the world about our new shiny configuration using BiddingAgent's doConfig function. Note that an agent can change its configuration at any time by calling doConfig. This is useful to update certain filters like bidProbability which regulates the bid request stream that makes it to the agent.
blah
void bid(
double timestamp,
const Id & id,
std::shared_ptr<RTBKIT::BidRequest> br,
Bids bids,
double timeLeftMs,
const Json::Value & augmentations)
{
for (Bid& bid : bids) {
blah
ExcAssertEqual(bid.availableCreatives.size(), 1);
int availableCreative = bid.availableCreatives.front();
blah
(void) br->spots[bid.spotIndex];
(void) config.creatives[availableCreative];
blah
bid.bid(availableCreative, USD_CPM(2));
}
blah
Json::Value metadata = 42;
blah
doBid(id, bids, metadata);
}
blah
blah
bool accountSetup = false;
void pace()
{
if (!accountSetup) {
accountSetup = true;
budgetController.addAccountSync(config.account);
}
blah
budgetController.topupTransferSync(config.account, USD(1));
}
blah
blah
struct FixedPriceBiddingAgent : public BiddingAgent {
blah
void init()
{
onBidRequest = bind(
&FixedPriceBiddingAgent::bid, this, _1, _2, _3, _4, _5, _6);
blah
budgetController.init(getServices()->config);
budgetController.start();
blah
addPeriodic("FixedPriceBiddingAgent::pace", 10.0,
[&] (uint64_t) { this->pace(); });
blah
BiddingAgent::init();
}
blah
void start()
{
BiddingAgent::start();
setConfig();
}
blah
int main(int argc, char** argv)
{
auto serviceProxies = args.makeServiceProxies();
blah
RTBKIT::FixedPriceBiddingAgent agent(serviceProxies, "fixed-price-agent-ex");
agent.init();
agent.start();
while (true) this_thread::sleep_for(chrono::seconds(10));
}
And that's it. You now a production ready-ish fixed price bidding agent. Welcome to a brave new world.
[^1]: RTBKit makes heavy use of C++11 so learn it and love it. Well makes heavy use of the subset of C++11 that gcc 4.7 supports.
##Table of Contents
###Developer Documentation
###System Description
###Tutorials
- How to write a bidding agent
- How to write an augmentor
- How to write an exchange connector
- How to write a win cost model
- How to write an ad server connector
- How to write a data logger
- How to configure an Exchange Connector
- Monitoring using graphite
###Internals
###Design Proposals
###Utilities