4  Pacing for Optimization

Warning

🚧 This chapter is a work in progress.

Maths: control theory (PID)!

Explain how pacing optimizes delivery (for different parties).

Use Observable to render example of multiplier changing over time?

import random
import matplotlib.pyplot as plt

# Constants
BUDGET = 1000.0  # Daily budget
TOTAL_MINUTES = 1440  # Minutes in a day
K_p = 0.8  # Proportional gain
K_i = 0.05  # Integral gain
K_d = 0.1  # Derivative gain
OPPORTUNITY_VALUE = 0.2  # $2 CPM is 200/1000 cents per impression
# the value and budget above need to be in same units, so assume cents

# Initialization
pickiness_level = 0.01  # Initial pickiness (how selective to be with ad opportunities)
previous_error = 0
integral_error = 0
spent = 0

# Data for plotting
opportunities_per_minute = []
pickiness_levels = []
actual_spend = []
target_spend = []

# Simulation loop
for minute in range(TOTAL_MINUTES):
    # Simulate opportunities with smoother variation
    if minute == 0:
        opportunities = random.randint(10, 20)  # Initial range
    else:
        # Limit changes to +/- 5 opportunities from previous minute
        opportunities = max(5, min(25, opportunities_per_minute[-1] + random.randint(-5, 5)))
    opportunities_per_minute.append(opportunities)

    # Target spend per minute with linear (not opportunity based) pacing
    target_spend_per_minute = BUDGET / TOTAL_MINUTES
    target_spend.append(target_spend_per_minute * minute)

    # Calculate overall pace
    pace = spent / (target_spend_per_minute * minute) if minute > 0 else 0

    # Calculate error
    error = (1 - pace) / (1 + pace)

    # PID control
    derivative_error = (error - previous_error)
    integral_error += error
    new_pickiness_level = pickiness_level + (K_p * error + K_i * integral_error + K_d * derivative_error)

    pickiness_levels.append(new_pickiness_level)
    previous_error = error

    # Simulate spending with fixed opportunity value 
    # if new_pickiness_level is between 0 and 1, then you are only winning a fraction of all opportunities per minute
    spent_this_minute = opportunities * OPPORTUNITY_VALUE * new_pickiness_level
    spent += spent_this_minute
    actual_spend.append(spent)

# Plotting
plt.figure(figsize=(12, 8))

plt.subplot(3, 1, 1)
plt.plot(opportunities_per_minute)
plt.title("Ad Opportunities per Minute")

plt.subplot(3, 1, 2)
plt.plot(pickiness_levels)
plt.title("Adjusted Pickiness Level")

plt.subplot(3, 1, 3)
plt.plot(target_spend, label='Target Spend')
plt.plot(actual_spend, label='Actual Spend')
plt.title("Spending Over Time")
plt.legend()

plt.show()