Skip to content
This repository has been archived by the owner on Dec 22, 2022. It is now read-only.

Commit

Permalink
Adds preferDeals support (prebid#1528)
Browse files Browse the repository at this point in the history
  • Loading branch information
hhhjort authored Oct 20, 2020
1 parent 50cee62 commit aaa5526
Show file tree
Hide file tree
Showing 7 changed files with 240 additions and 5 deletions.
2 changes: 1 addition & 1 deletion endpoints/openrtb2/amp_auction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1232,7 +1232,7 @@ func TestBuildAmpObject(t *testing.T) {
},
AT: 1,
TMax: 500,
Ext: json.RawMessage(`{"prebid":{"cache":{"bids":{"returnCreative":null},"vastxml":null},"targeting":{"pricegranularity":{"precision":2,"ranges":[{"min":0,"max":20,"increment":0.1}]},"includewinners":true,"includebidderkeys":true,"includebrandcategory":null,"includeformat":false,"durationrangesec":null}}}`),
Ext: json.RawMessage(`{"prebid":{"cache":{"bids":{"returnCreative":null},"vastxml":null},"targeting":{"pricegranularity":{"precision":2,"ranges":[{"min":0,"max":20,"increment":0.1}]},"includewinners":true,"includebidderkeys":true,"includebrandcategory":null,"includeformat":false,"durationrangesec":null,"preferdeals":false}}}`),
},
AuctionResponse: &openrtb.BidResponse{
SeatBid: []openrtb.SeatBid{{
Expand Down
17 changes: 15 additions & 2 deletions exchange/auction.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func (d *DebugLog) PutDebugLogError(cache prebid_cache_client.Client, timeout in
return nil
}

func newAuction(seatBids map[openrtb_ext.BidderName]*pbsOrtbSeatBid, numImps int) *auction {
func newAuction(seatBids map[openrtb_ext.BidderName]*pbsOrtbSeatBid, numImps int, preferDeals bool) *auction {
winningBids := make(map[string]*pbsOrtbBid, numImps)
winningBidsByBidder := make(map[string]map[openrtb_ext.BidderName]*pbsOrtbBid, numImps)

Expand All @@ -102,7 +102,7 @@ func newAuction(seatBids map[openrtb_ext.BidderName]*pbsOrtbSeatBid, numImps int
for _, bid := range seatBid.bids {
cpm := bid.bid.Price
wbid, ok := winningBids[bid.bid.ImpID]
if !ok || cpm > wbid.bid.Price {
if !ok || isNewWinningBid(bid.bid, wbid.bid, preferDeals) {
winningBids[bid.bid.ImpID] = bid
}
if bidMap, ok := winningBidsByBidder[bid.bid.ImpID]; ok {
Expand All @@ -124,6 +124,19 @@ func newAuction(seatBids map[openrtb_ext.BidderName]*pbsOrtbSeatBid, numImps int
}
}

// isNewWinningBid calculates if the new bid (nbid) will win against the current winning bid (wbid) given preferDeals.
func isNewWinningBid(bid, wbid *openrtb.Bid, preferDeals bool) bool {
if preferDeals {
if len(wbid.DealID) > 0 && len(bid.DealID) == 0 {
return false
}
if len(wbid.DealID) == 0 && len(bid.DealID) > 0 {
return true
}
}
return bid.Price > wbid.Price
}

func (a *auction) setRoundedPrices(priceGranularity openrtb_ext.PriceGranularity) {
roundedPrices := make(map[*pbsOrtbBid]string, 5*len(a.winningBids))
for _, topBidsPerImp := range a.winningBidsByBidder {
Expand Down
219 changes: 219 additions & 0 deletions exchange/auction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,225 @@ func runCacheSpec(t *testing.T, fileDisplayName string, specData *cacheSpec) {
}
}

func TestNewAuction(t *testing.T) {
bid1p077 := pbsOrtbBid{
bid: &openrtb.Bid{
ImpID: "imp1",
Price: 0.77,
},
}
bid1p123 := pbsOrtbBid{
bid: &openrtb.Bid{
ImpID: "imp1",
Price: 1.23,
},
}
bid1p230 := pbsOrtbBid{
bid: &openrtb.Bid{
ImpID: "imp1",
Price: 2.30,
},
}
bid1p088d := pbsOrtbBid{
bid: &openrtb.Bid{
ImpID: "imp1",
Price: 0.88,
DealID: "SpecialDeal",
},
}
bid1p166d := pbsOrtbBid{
bid: &openrtb.Bid{
ImpID: "imp1",
Price: 1.66,
DealID: "BigDeal",
},
}
bid2p123 := pbsOrtbBid{
bid: &openrtb.Bid{
ImpID: "imp2",
Price: 1.23,
},
}
bid2p144 := pbsOrtbBid{
bid: &openrtb.Bid{
ImpID: "imp2",
Price: 1.44,
},
}
tests := []struct {
description string
seatBids map[openrtb_ext.BidderName]*pbsOrtbSeatBid
numImps int
preferDeals bool
expectedAuction auction
}{
{
description: "Basic auction test",
seatBids: map[openrtb_ext.BidderName]*pbsOrtbSeatBid{
"appnexus": {
bids: []*pbsOrtbBid{&bid1p123},
},
"rubicon": {
bids: []*pbsOrtbBid{&bid1p230},
},
},
numImps: 1,
preferDeals: false,
expectedAuction: auction{
winningBids: map[string]*pbsOrtbBid{
"imp1": &bid1p230,
},
winningBidsByBidder: map[string]map[openrtb_ext.BidderName]*pbsOrtbBid{
"imp1": {
"appnexus": &bid1p123,
"rubicon": &bid1p230,
},
},
},
},
{
description: "Multi-imp auction",
seatBids: map[openrtb_ext.BidderName]*pbsOrtbSeatBid{
"appnexus": {
bids: []*pbsOrtbBid{&bid1p230, &bid2p123},
},
"rubicon": {
bids: []*pbsOrtbBid{&bid1p077, &bid2p144},
},
"openx": {
bids: []*pbsOrtbBid{&bid1p123},
},
},
numImps: 2,
preferDeals: false,
expectedAuction: auction{
winningBids: map[string]*pbsOrtbBid{
"imp1": &bid1p230,
"imp2": &bid2p144,
},
winningBidsByBidder: map[string]map[openrtb_ext.BidderName]*pbsOrtbBid{
"imp1": {
"appnexus": &bid1p230,
"rubicon": &bid1p077,
"openx": &bid1p123,
},
"imp2": {
"appnexus": &bid2p123,
"rubicon": &bid2p144,
},
},
},
},
{
description: "Basic auction with deals, no preference",
seatBids: map[openrtb_ext.BidderName]*pbsOrtbSeatBid{
"appnexus": {
bids: []*pbsOrtbBid{&bid1p123},
},
"rubicon": {
bids: []*pbsOrtbBid{&bid1p088d},
},
},
numImps: 1,
preferDeals: false,
expectedAuction: auction{
winningBids: map[string]*pbsOrtbBid{
"imp1": &bid1p123,
},
winningBidsByBidder: map[string]map[openrtb_ext.BidderName]*pbsOrtbBid{
"imp1": {
"appnexus": &bid1p123,
"rubicon": &bid1p088d,
},
},
},
},
{
description: "Basic auction with deals, prefer deals",
seatBids: map[openrtb_ext.BidderName]*pbsOrtbSeatBid{
"appnexus": {
bids: []*pbsOrtbBid{&bid1p123},
},
"rubicon": {
bids: []*pbsOrtbBid{&bid1p088d},
},
},
numImps: 1,
preferDeals: true,
expectedAuction: auction{
winningBids: map[string]*pbsOrtbBid{
"imp1": &bid1p088d,
},
winningBidsByBidder: map[string]map[openrtb_ext.BidderName]*pbsOrtbBid{
"imp1": {
"appnexus": &bid1p123,
"rubicon": &bid1p088d,
},
},
},
},
{
description: "Auction with 2 deals",
seatBids: map[openrtb_ext.BidderName]*pbsOrtbSeatBid{
"appnexus": {
bids: []*pbsOrtbBid{&bid1p166d},
},
"rubicon": {
bids: []*pbsOrtbBid{&bid1p088d},
},
},
numImps: 1,
preferDeals: true,
expectedAuction: auction{
winningBids: map[string]*pbsOrtbBid{
"imp1": &bid1p166d,
},
winningBidsByBidder: map[string]map[openrtb_ext.BidderName]*pbsOrtbBid{
"imp1": {
"appnexus": &bid1p166d,
"rubicon": &bid1p088d,
},
},
},
},
{
description: "Auction with 3 bids and 2 deals",
seatBids: map[openrtb_ext.BidderName]*pbsOrtbSeatBid{
"appnexus": {
bids: []*pbsOrtbBid{&bid1p166d},
},
"rubicon": {
bids: []*pbsOrtbBid{&bid1p088d},
},
"openx": {
bids: []*pbsOrtbBid{&bid1p230},
},
},
numImps: 1,
preferDeals: true,
expectedAuction: auction{
winningBids: map[string]*pbsOrtbBid{
"imp1": &bid1p166d,
},
winningBidsByBidder: map[string]map[openrtb_ext.BidderName]*pbsOrtbBid{
"imp1": {
"appnexus": &bid1p166d,
"rubicon": &bid1p088d,
"openx": &bid1p230,
},
},
},
},
}

for _, test := range tests {
auc := newAuction(test.seatBids, test.numImps, test.preferDeals)

assert.Equal(t, test.expectedAuction, *auc, test.description)
}

}

type cacheSpec struct {
BidRequest openrtb.BidRequest `json:"bidRequest"`
PbsBids []pbsBid `json:"pbsBids"`
Expand Down
4 changes: 2 additions & 2 deletions exchange/exchange.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,9 @@ func (e *exchange) HoldAuction(ctx context.Context, bidRequest *openrtb.BidReque
}
}

auc = newAuction(adapterBids, len(bidRequest.Imp))

if targData != nil {
// A non-nil auction is only needed if targeting is active. (It is used below this block to extract cache keys)
auc = newAuction(adapterBids, len(bidRequest.Imp), targData.preferDeals)
auc.setRoundedPrices(targData.priceGranularity)

if requestExt.Prebid.SupportDeals {
Expand Down
1 change: 1 addition & 0 deletions exchange/targeting.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type targetData struct {
includeCacheBids bool
includeCacheVast bool
includeFormat bool
preferDeals bool
// cacheHost and cachePath exist to supply cache host and path as targeting parameters
cacheHost string
cachePath string
Expand Down
1 change: 1 addition & 0 deletions exchange/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,7 @@ func getExtTargetData(requestExt *openrtb_ext.ExtRequest, cacheInstructions *ext
includeCacheBids: cacheInstructions.cacheBids,
includeCacheVast: cacheInstructions.cacheVAST,
includeFormat: requestExt.Prebid.Targeting.IncludeFormat,
preferDeals: requestExt.Prebid.Targeting.PreferDeals,
}
}
return targData
Expand Down
1 change: 1 addition & 0 deletions openrtb_ext/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ type ExtRequestTargeting struct {
IncludeBrandCategory *ExtIncludeBrandCategory `json:"includebrandcategory"`
IncludeFormat bool `json:"includeformat"`
DurationRangeSec []int `json:"durationrangesec"`
PreferDeals bool `json:"preferdeals"`
AppendBidderNames bool `json:"appendbiddernames,omitempty"`
}

Expand Down

0 comments on commit aaa5526

Please sign in to comment.