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  }