github.com/prebid/prebid-server/v2@v2.18.0/bidadjustment/apply.go (about) 1 package bidadjustment 2 3 import ( 4 "math" 5 6 "github.com/prebid/prebid-server/v2/adapters" 7 "github.com/prebid/prebid-server/v2/openrtb_ext" 8 ) 9 10 const ( 11 AdjustmentTypeCPM = "cpm" 12 AdjustmentTypeMultiplier = "multiplier" 13 AdjustmentTypeStatic = "static" 14 WildCard = "*" 15 Delimiter = "|" 16 ) 17 18 const maxNumOfCombos = 8 19 const pricePrecision float64 = 10000 // Rounds to 4 Decimal Places 20 const minBid = 0.1 21 22 // Apply gets the highest priority adjustment slice given a map of rules, and applies those adjustments to a bid's price 23 func Apply(rules map[string][]openrtb_ext.Adjustment, bidInfo *adapters.TypedBid, bidderName openrtb_ext.BidderName, currency string, reqInfo *adapters.ExtraRequestInfo, bidType string) (float64, string) { 24 var adjustments []openrtb_ext.Adjustment 25 if len(rules) > 0 { 26 adjustments = get(rules, bidType, string(bidderName), bidInfo.Bid.DealID) 27 } else { 28 return bidInfo.Bid.Price, currency 29 } 30 adjustedPrice, adjustedCurrency := apply(adjustments, bidInfo.Bid.Price, currency, reqInfo) 31 32 if bidInfo.Bid.DealID != "" && adjustedPrice < 0 { 33 return 0, currency 34 } 35 if bidInfo.Bid.DealID == "" && adjustedPrice <= 0 { 36 return minBid, currency 37 } 38 return adjustedPrice, adjustedCurrency 39 } 40 41 func apply(adjustments []openrtb_ext.Adjustment, bidPrice float64, currency string, reqInfo *adapters.ExtraRequestInfo) (float64, string) { 42 if len(adjustments) == 0 { 43 return bidPrice, currency 44 } 45 originalBidPrice := bidPrice 46 47 for _, adjustment := range adjustments { 48 switch adjustment.Type { 49 case AdjustmentTypeMultiplier: 50 bidPrice = bidPrice * adjustment.Value 51 case AdjustmentTypeCPM: 52 convertedVal, err := reqInfo.ConvertCurrency(adjustment.Value, adjustment.Currency, currency) 53 if err != nil { 54 return originalBidPrice, currency 55 } 56 bidPrice = bidPrice - convertedVal 57 case AdjustmentTypeStatic: 58 bidPrice = adjustment.Value 59 currency = adjustment.Currency 60 } 61 } 62 roundedBidPrice := math.Round(bidPrice*pricePrecision) / pricePrecision 63 64 return roundedBidPrice, currency 65 } 66 67 // get() should return the highest priority slice of adjustments from the map that we can match with the given bid info 68 // given the bid info, we create the same format of combinations that's present in the key of the ruleToAdjustments map 69 // the slice is ordered by priority from highest to lowest, as soon as we find a match, we return that slice 70 func get(rules map[string][]openrtb_ext.Adjustment, bidType, bidderName, dealID string) []openrtb_ext.Adjustment { 71 priorityRules := [maxNumOfCombos]string{} 72 if dealID != "" { 73 priorityRules[0] = bidType + Delimiter + bidderName + Delimiter + dealID 74 priorityRules[1] = bidType + Delimiter + bidderName + Delimiter + WildCard 75 priorityRules[2] = bidType + Delimiter + WildCard + Delimiter + dealID 76 priorityRules[3] = WildCard + Delimiter + bidderName + Delimiter + dealID 77 priorityRules[4] = bidType + Delimiter + WildCard + Delimiter + WildCard 78 priorityRules[5] = WildCard + Delimiter + bidderName + Delimiter + WildCard 79 priorityRules[6] = WildCard + Delimiter + WildCard + Delimiter + dealID 80 priorityRules[7] = WildCard + Delimiter + WildCard + Delimiter + WildCard 81 } else { 82 priorityRules[0] = bidType + Delimiter + bidderName + Delimiter + WildCard 83 priorityRules[1] = bidType + Delimiter + WildCard + Delimiter + WildCard 84 priorityRules[2] = WildCard + Delimiter + bidderName + Delimiter + WildCard 85 priorityRules[3] = WildCard + Delimiter + WildCard + Delimiter + WildCard 86 } 87 88 for _, rule := range priorityRules { 89 if _, ok := rules[rule]; ok { 90 return rules[rule] 91 } 92 } 93 return nil 94 }