Skip to content

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

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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions