Skip to content

Instantly share code, notes, and snippets.

@dohse
Last active December 16, 2015 01:29
Show Gist options
  • Save dohse/5355085 to your computer and use it in GitHub Desktop.
Save dohse/5355085 to your computer and use it in GitHub Desktop.
RTBKit bidding agent in node
/** bidding_agent_ex.cc -*- C++ -*-
Rémi Attab, 22 Feb 2013
Copyright (c) 2013 Datacratic. All rights reserved.
Example of a simple fixed-price bidding agent.
*/
#include "rtbkit/common/bids.h"
#include "rtbkit/core/banker/slave_banker.h"
#include "rtbkit/core/agent_configuration/agent_config.h"
#include "rtbkit/plugins/bidding_agent/bidding_agent.h"
#include "soa/service/service_utils.h"
#include <boost/program_options/cmdline.hpp>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/positional_options.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/variables_map.hpp>
#include <iostream>
#include <thread>
#include <chrono>
using namespace std;
using namespace ML;
namespace RTBKIT {
/******************************************************************************/
/* FIXED PRICE BIDDING AGENT */
/******************************************************************************/
/** Simple bidding agent whose sole purpose in life is to bid 1$ CPM on every
bid requests it sees. It also has a very simple pacer which ensures that we
always have at most 1$ to spend every 10 seconds.
*/
struct FixedPriceBiddingAgent :
public BiddingAgent
{
FixedPriceBiddingAgent(
std::shared_ptr<Datacratic::ServiceProxies> services,
const string& serviceName) :
BiddingAgent(services, serviceName),
accountSetup(false)
{}
void init()
{
// We only want to specify a subset of the callbacks so turn the
// annoying safety belt off.
strictMode(false);
onBidRequest = bind(
&FixedPriceBiddingAgent::bid, this, _1, _2, _3, _4, _5, _6);
// This component is used to speak with the master banker and pace the
// rate at which we spend our budget.
budgetController.init(getServices()->config);
budgetController.start();
// Update our pacer every 10 seconds. Note that since this interacts
// with the budgetController which is only synced up with the router
// every few seconds, the wait period shouldn't be set too low.
addPeriodic("FixedPriceBiddingAgent::pace", 10.0,
[&] (uint64_t) { this->pace(); });
BiddingAgent::init();
}
void start()
{
BiddingAgent::start();
// Build our configuration and tell the world about it.
setConfig();
}
void shutdown()
{
BiddingAgent::shutdown();
budgetController.shutdown();
}
/** Sets up an agent configuration for our example. */
void setConfig()
{
config = AgentConfig();
// Accounts are used to control the allocation of spending budgets for
// an agent. The whole mechanism is fully generic and can be setup in
// whatever you feel it bests suits you. For this example, I went for
// overly silly because that's how I roll.
config.account = {"a", "very", "long", "account", "name", "thingy"};
// Specify the properties of the creatives we are trying to show.
config.creatives.push_back(Creative::sampleLB);
config.creatives.push_back(Creative::sampleWS);
config.creatives.push_back(Creative::sampleBB);
// Set our frequency cap to 42. This has two effects: 1) it instructs
// the router that we want bid requests destined for our agent to first
// be augmented with frequency capping information and 2) it instructs
// the frequency cap augmentor to tag any bid requests for which we've
// seen the user less the 42 times.
config.addAugmentation("frequency-cap-ex", Json::Value(42));
// Instructs the router to only keep bid requests that have this tag. In
// other words keep only the bid requests that our agents has seen less
// then 42 times.
config.augmentationFilter.include.push_back("pass-frequency-cap-ex");
// Tell the world about our config. We can change the configuration of
// an agent at any time by calling this function.
doConfig(config.toJson());
}
/** Simple fixed price bidding strategy. Note that the router is in charge
of making sure we stay within budget and don't go bankrupt.
*/
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) {
// In our example, all our creatives are of the different sizes so
// there should only ever be one biddable creative. Note that that
// the router won't ask for bids on spots that don't have any
// biddable creatives.
ExcAssertEqual(bid.availableCreatives.size(), 1);
int availableCreative = bid.availableCreatives.front();
// We don't really need it here but this is how you can get the
// AdSpot and Creative object from the indexes.
(void) br->spots[bid.spotIndex];
(void) config.creatives[availableCreative];
// Create a 2$ CPM bid with our available creative. Note that by
// default, the bid price is set to 0 which indicates that we don't
// wish to bid on the given spot.
bid.bid(availableCreative, USD_CPM(2));
}
// A value that will be passed back to us when we receive the result of
// our bid.
Json::Value metadata;
metadata["html"] = "<tag></tag>";
metadata["landingPageUrl"] = "https://www.fraport.de/";
metadata["creativeName"] = "Name";
metadata["advertiserName"] = "shameless selfplug";
// Send our bid back to the agent.
doBid(id, bids, metadata);
}
/** Simple pacing scheme which allocates 1$ to spend every period. */
void pace()
{
// We need to register our account once with the banker service.
if (!accountSetup) {
accountSetup = true;
budgetController.addAccountSync(config.account);
}
// Make sure we have 1$ to spend for the next period.
budgetController.topupTransferSync(config.account, USD(1));
}
AgentConfig config;
bool accountSetup;
SlaveBudgetController budgetController;
};
} // namepsace RTBKIT
/******************************************************************************/
/* MAIN */
/******************************************************************************/
int main(int argc, char** argv)
{
using namespace boost::program_options;
Datacratic::ServiceProxyArguments args;
options_description options = args.makeProgramOptions();
options.add_options() ("help,h", "Print this message");
variables_map vm;
store(command_line_parser(argc, argv).options(options).run(), vm);
notify(vm);
if (vm.count("help")) {
cerr << options << endl;
return 1;
}
auto serviceProxies = args.makeServiceProxies();
RTBKIT::FixedPriceBiddingAgent agent(serviceProxies, "fixed-price-agent-ex");
agent.init();
agent.start();
while (true) this_thread::sleep_for(chrono::seconds(10));
// Won't ever reach this point but this is how you shutdown an agent.
agent.shutdown();
return 0;
}
var RTBkit = require('./build/x86_64/bin/rtb.node');
var services = require('./build/x86_64/bin/services.node');
var zookeeperUri = "localhost:2181"; // must point to same Zookeeper as routers
var services = new services.ServiceProxies();
services.useZookeeper(zookeeperUri);
services.logToCarbon('localhost:2003');
var agent = new RTBkit.BiddingAgent("myAgent", services);
agent.onBidRequest = function(){
console.log('onBidRequest', arguments);
//agent.doBid();
};
agent.init();
agent.start()
agent.doConfig({
"account":[
"a",
"very",
"long",
"account",
"name",
"thingy"
],
"augment":{
"frequency-cap-ex":42,
"random":null
},
"augmentationFilter":{
"include":[
"pass-frequency-cap-ex"
]
},
"bidControl":{
"fixedBidCpmInMicros":0,
"type":"RELAY"
},
"creatives":[
{
"format":"728x90",
"id":2,
"name":"LeaderBoard",
"tagId":2
},
{
"format":"160x600",
"id":0,
"name":"LeaderBoard",
"tagId":0
},
{
"format":"300x250",
"id":1,
"name":"BigBox",
"tagId":1
}
],
"errorFormat":"lightweight",
"lossFormat":"lightweight",
"minTimeAvailableMs":5.0,
"tagFilter":{
"excludeIfNotPresent":false
},
"test":false,
"winFormat":"full"
});
[{
"exchangeType": "<exchange>",
"host": "localhost",
"port": 8080,
"threads": 4
}]
#!/bin/bash -e
SERVICES=/dev/null
make -j4 build/x86_64/bin/{mbr_router_runner,bidding_agent_ex} \
build/x86_64/bin/rtb.node
monitor_service_runner &>$SERVICES &
PIDS=($!)
trap 'kill ${PIDS[*]}' EXIT
agent_configuration_service_runner &>$SERVICES &
PIDS+=($!)
router_logger_runner &>/dev/null &
PIDS+=($!)
banker_service_runner --redis-uri localhost:6379 &>$SERVICES &
PIDS+=($!)
mbr_router_runner -nx exchange.json &>$SERVICES &
PIDS+=($!)
sleep 1
$@ &
PIDS+=($!)
sleep 2
curl http://localhost:8080/<exchange request fixture>
/* mbr_router_runner.cc -*- C++ -*-
Jonas Dohse, 26 March 2013
Copyright (c) 2013 mbr targeting GmbH. All rights reserved.
Start router by router runner
*/
#include "jml/arch/timers.h"
#include "rtbkit/core/router/router_runner.h"
int main(int argc, char ** argv) {
RTBKIT::RouterRunnehr routerRunner;
routerRunner.doOptions(argc, argv);
routerRunner.init();
routerRunner.start();
while (true) {
ML::sleep(1.0);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment