Skip to content

As a publisher I need to add configurable bid price rules and modifiers #77

@jevansnyc

Description

@jevansnyc

User story

Currently, mocktioneer uses fixed CPM pricing based on ad size ($1.70-$4.20) with a simple per impression override via ext.mocktioneer.bid. To better simulate realnworld DSP bidding behavior in testing scenarios, we need a comprehensive bid price configuration system that supports common targeting and pricing strategies.

Prod DSPs evaluate multiple factors when determining bid prices: deal agreements, audience data, geographic targeting, contextual relevance, and inventory quality. A testing tool should replicate these pricing mechanics to ensure integrations handle various bid scenarios correctly.

Acceptance criteria

  • Rule Evaluation Order: Define clear precedence (e.g., deal IDs override all other rules, then segments, then geo, etc.)
  • Composition Strategy: Support both additive and multiplicative modifiers
  • Determinism: Maintain mocktioneer's core principle—identical requests produce identical bids
  • Validation: Reject invalid configurations at startup with clear error messages
  • Observability: Log which rules were applied to calculate each bid (useful for debugging)
  • Backward Compatibility: Preserve existing size-based pricing as default when no advanced rules are configured

Testing Requirements

  • Unit tests for each pricing rule type
  • Integration tests with realistic OpenRTB requests
  • Validation that complex rule combinations work correctly
  • Performance benchmarks to ensure rule evaluation doesn't significantly impact latency

Documentation Needs

  • Update API reference with pricing rule details
  • Add configuration examples to guide
  • Create troubleshooting section for common pricing configuration errors

Affected area

Core (auction, OpenRTB, APS, mediation, rendering)

Proposed approach

Core Pricing Rules

Deal ID Pricing

Support imp[].pmp.deals[] from OpenRTB spec
Configure fixed or floor prices per deal ID
Example: Deal "PMP-12345" always bids $8.50 CPM

[[pricing.deals]]
id = "PMP-12345"
price = 8.50
Price Floor & Ceiling Controls

Global and per-size min/max bid constraints
Honor imp[].bidfloor from bid requests
Configurable hard limits to prevent unrealistic bids

[pricing.limits]
global_floor = 0.50
global_ceiling = 25.00
Geographic Pricing

Country, region/state, DMA, and postal code-based multipliers
Extract from device.geo in OpenRTB requests
Example: US traffic at 1.5x base price, UK at 1.2x

[pricing.geo]
"US" = 1.5
"GB" = 1.2
"CA" = 1.3
Audience Segment Pricing

Match against user.data[] segments from bid request
Configurable premiums for high-value audiences
Example: "auto-intender" segment adds $2.00 CPM

[[pricing.segments]]
id = "auto-intender"
modifier = 2.00 # additive

[[pricing.segments]]
id = "high-income"
multiplier = 1.3 # multiplicative
Contextual Match Pricing

Keyword/category matching from site.keywords or app.cat[]
IAB taxonomy category-based pricing tiers
Example: "finance" content at $6 base CPM

[[pricing.contextual]]
keywords = ["finance", "investment", "banking"]
base_price = 6.00

[[pricing.contextual]]
iab_categories = ["IAB13"] # Personal Finance
multiplier = 1.4
Additional DSP-Realistic Features
Device Type Modifiers

Differential pricing for mobile, tablet, desktop, CTV
Extract from device.devicetype (OpenRTB 2.x)

[pricing.device]
mobile = 0.8 # devicetype = 4
tablet = 1.0 # devicetype = 5
desktop = 1.2 # devicetype = 2
ctv = 2.0 # devicetype = 3
Publisher/Domain Pricing

Allowlist premium publishers with higher bids
Blocklist or reduce bids for low-quality inventory

[[pricing.publishers]]
domain = "nytimes.com"
multiplier = 1.8

[[pricing.publishers]]
domain = "example-low-quality.com"
multiplier = 0.3
Dayparting (Time-Based Pricing)

Hour-of-day and day-of-week bid adjustments
Timezone-aware or UTC-based schedules

[[pricing.daypart]]
days = ["mon", "tue", "wed", "thu", "fri"]
hours = [9, 10, 11, 12, 13, 14, 15, 16, 17]
multiplier = 1.3 # Business hours premium
Ad Position Multipliers

Above-the-fold vs. below-the-fold pricing
Based on imp[].banner.pos (OpenRTB field)

[pricing.position]
above_fold = 1.5 # pos = 1
below_fold = 0.7 # pos = 3
Supply Source Pricing

Different pricing for web, in-app, CTV
Derived from presence of site vs. app object

[pricing.supply]
web = 1.0
app = 1.2
ctv = 2.5
Private Marketplace (PMP) vs. Open Exchange

Automatically bid higher for imp[].pmp requests
Simulate preferential treatment of private deals

[pricing.marketplace]
pmp_multiplier = 1.5
open_exchange_multiplier = 1.0
Brand Safety & Viewability Scores

Optional risk-based bid reduction
Custom fields via ext.brand_safety or ext.viewability

[[pricing.brand_safety]]
score_min = 0.8
multiplier = 1.0

[[pricing.brand_safety]]
score_min = 0.0
score_max = 0.5
multiplier = 0.3 # Risky inventory
Bid Shading/Reduction Strategies

Simulate second-price auction mechanics
Configurable shading percentage

[pricing.bid_shading]
enabled = true
reduction_percent = 5 # Bid 5% below calculated price
Currency Support with Custom Rates

Multi-currency bid responses
Configurable exchange rates for testing

[pricing.currency]
default = "USD"

[pricing.currency.rates]
EUR = 0.92
GBP = 0.79
JPY = 148.50
Configuration Schema Proposal
Extend edgezero.toml with a new [pricing] section:

[pricing]

Base pricing (existing behavior)

base_price_by_size = true
area_fallback = true

Global constraints

[pricing.limits]
global_floor = 0.50
global_ceiling = 25.00

Deal-specific pricing

[[pricing.deals]]
id = "PMP-12345"
price = 8.50

[[pricing.deals]]
id = "PMP-67890"
floor = 5.00
ceiling = 12.00

Geographic multipliers

[pricing.geo]
"US" = 1.5
"GB" = 1.2
default = 1.0

Segment-based modifiers

[[pricing.segments]]
id = "auto-intender"
modifier = 2.00

[[pricing.segments]]
id = "high-income"
multiplier = 1.3

Contextual pricing

[[pricing.contextual]]
keywords = ["finance", "investment"]
base_price = 6.00

Device type modifiers

[pricing.device]
mobile = 0.8
desktop = 1.2
ctv = 2.0

Publisher allowlist/blocklist

[[pricing.publishers]]
domain = "nytimes.com"
multiplier = 1.8

Dayparting

[[pricing.daypart]]
days = ["mon", "tue", "wed", "thu", "fri"]
hours = [9, 10, 11, 12, 13, 14, 15, 16, 17]
multiplier = 1.3

Position-based pricing

[pricing.position]
above_fold = 1.5
below_fold = 0.7

Marketplace type

[pricing.marketplace]
pmp_multiplier = 1.5
open_exchange_multiplier = 1.0

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions