Multi-Period Budget Optimization - User Guide¶
Overview¶
Multi-period budget optimization extends AMMM’s budget allocation capabilities to plan across multiple time periods (e.g., 13 weeks, 6 months) rather than optimizing for a single aggregated period. This enables:
Time-varying budget allocation - Allocate different budgets to different periods based on expected effectiveness
Seasonality integration - Automatically adjust for seasonal variations in channel effectiveness
Realistic planning - Align with business planning cycles (quarterly, annual)
Constraint flexibility - Set per-period budget limits while optimizing total allocation
When to Use Multi-Period Optimization¶
Use multi-period optimization when:
You need to plan budgets for multiple weeks/months ahead
Your business has significant seasonality (e.g., retail, travel)
You want to optimize across a planning horizon (e.g., Q1, full year)
You need to respect time-varying budget constraints
Use single-period optimization when:
You’re optimizing for a single aggregated time period
You want quick “what-if” scenarios with fixed total budgets
Seasonality is not a major concern
Quick Start¶
Basic Multi-Period Optimization (13 weeks)¶
import src as ammm
from src.driver import MMMBaseDriverV2
# Fit model (standard pipeline)
driver = MMMBaseDriverV2('config.yml', 'data.csv', 'holidays.xlsx', 'results')
driver.main()
# Run 13-week multi-period optimization
ammm.optimize_marketing_budget(
model=driver.model,
data=driver.processed_data,
config=driver.config,
results_dir=driver.results_dir,
n_time_periods=13, # 13 weeks
multiperiod_mode=True, # Enable multi-period
use_seasonality=True, # Apply seasonal adjustments
frequency='W' # Weekly planning
)
Output:
CSV file:
results/multiperiod_optimization_results.csvConsole summary with total budget, contribution, and ROI
Usage Examples¶
Example 1: Quarterly Planning (13 weeks)¶
ammm.optimize_marketing_budget(
model=driver.model,
data=driver.processed_data,
config=driver.config,
results_dir='results',
n_time_periods=13,
multiperiod_mode=True,
use_seasonality=True,
frequency='W',
start_date='2025-01-06' # Q1 start
)
Example 2: Custom Budget with Seasonality¶
ammm.optimize_marketing_budget(
model=driver.model,
data=driver.processed_data,
config=driver.config,
results_dir='results',
n_time_periods=13,
total_budget=12_500_000, # £12.5M for Q1
multiperiod_mode=True,
use_seasonality=True,
frequency='W',
start_date='2025-01-06'
)
Example 3: Monthly Planning (12 months)¶
ammm.optimize_marketing_budget(
model=driver.model,
data=driver.processed_data,
config=driver.config,
results_dir='results',
n_time_periods=12,
multiperiod_mode=True,
use_seasonality=True,
frequency='M', # Monthly frequency
start_date='2025-01-01'
)
Example 4: With Per-Period Budget Constraints¶
# Direct call to multi-period function for advanced options
from src.driver.opt import optimize_multiperiod_budget
results_df = optimize_multiperiod_budget(
model=driver.model,
data=driver.processed_data,
config=driver.config,
results_dir='results',
n_periods=13,
total_budget=12_500_000,
period_budget_limits=(800_000, 1_200_000), # £800K-£1.2M per week
use_seasonality=True,
frequency='W',
start_date='2025-01-06'
)
Parameters Reference¶
Main Function: optimize_marketing_budget()¶
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
BaseMMM |
Required |
Fitted MMM model |
|
DataFrame |
Required |
Historical data |
|
Dict |
Required |
Configuration dictionary |
|
str |
Required |
Output directory |
|
int |
1 |
Number of periods to optimize |
|
bool |
False |
Enable multi-period optimization |
|
bool |
False |
Apply seasonal adjustments |
|
str |
‘M’ |
Time frequency (‘W’, ‘M’, ‘D’) |
|
str |
None |
Planning start date (YYYY-MM-DD) |
|
float |
None |
Total budget (auto-calculated if None) |
Advanced Function: optimize_multiperiod_budget()¶
Additional parameters for fine-grained control:
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
Tuple[float, float] |
None |
(min, max) budget per period |
|
float |
0.20 |
% below mean for channel lower bounds |
|
float |
0.20 |
% above mean for channel upper bounds |
Output Format¶
CSV File: multiperiod_optimization_results.csv¶
Column |
Description |
|---|---|
|
Period index (0-indexed) |
|
Actual date for period start |
|
Channel name (or ‘TOTAL’) |
|
Allocated budget for this period/channel |
|
Expected contribution (with seasonal adjustment) |
|
Seasonal effectiveness multiplier (1.0 = baseline) |
|
Return on investment for this period/channel |
Example Output¶
period,period_date,channel,budget,contribution,seasonal_multiplier,roi
0,2025-01-06,google,12500,60000,0.92,4.8
0,2025-01-06,facebook,28000,420000,0.88,15.0
0,2025-01-06,tv,95000,550000,1.15,5.8
0,2025-01-06,TOTAL,965000,1306000,0.98,1.35
1,2025-01-13,google,13200,63800,0.94,4.8
...
Console Summary¶
Multi-Period Optimization Summary (13 periods)
============================================================
Total Budget: 12,500,000.00
Total Contribution: 95,750,000.00
Overall ROI: 7.66×
Prophet Seasonality Integration¶
How It Works¶
Multi-period optimization uses Prophet’s seasonal decomposition from your fitted MMM model to adjust channel effectiveness over time.
Seasonal Components Used:
Yearly seasonality - Annual patterns (e.g., holiday peaks, summer lulls)
Weekly seasonality - Day-of-week patterns (e.g., weekend vs weekday)
Both combined - Additive effect of yearly + weekly
Example:
Week of December 15 (pre-Christmas Friday):
Yearly: +0.5 (holiday shopping season)
Weekly: +0.2 (Friday/weekend effect)
Combined: +0.7
Multiplier: ~1.07 (7% effectiveness boost)
Configuring Prophet Seasonality in Your Model¶
Prophet seasonality is configured when fitting your MMM model. Check your model config:
# In your config.yml
prophet:
yearly_seasonality: true # Enable yearly patterns
weekly_seasonality: true # Enable weekly patterns
daily_seasonality: false # Usually false for weekly/monthly data
# Optional: Custom seasonality
seasonalities:
- name: 'monthly'
period: 30.5
fourier_order: 5
Interpreting Seasonal Multipliers¶
1.0 - Baseline effectiveness (no seasonal adjustment)
>1.0 - Channel more effective (e.g., 1.08 = 8% boost)
<1.0 - Channel less effective (e.g., 0.92 = 8% reduction)
Example: If Google Ads normally generates £5 contribution per £1 spend:
Week with multiplier 1.08: £5.40 contribution per £1 spend
Week with multiplier 0.92: £4.60 contribution per £1 spend
Checking Seasonal Patterns¶
Inspect what seasonality your model has:
# After model fitting
prophet_model = driver.model.prophet_model
forecast = prophet_model.predict(prophet_model.history)
print(forecast.columns)
# Output: ['ds', 'trend', 'yhat', 'yearly', 'weekly', 'holidays', ...]
# Plot seasonal components
prophet_model.plot_components(forecast)
Holiday Event Planning & Marketing Theory¶
Overview¶
Multi-period optimization with seasonality is particularly powerful for planning around major retail events like Black Friday, Christmas, Prime Day, or other promotional periods. This section explains how AMMM handles holiday events and provides guidance based on marketing science research.
How Holidays Are Captured¶
Holiday effects appear in Prophet’s holidays component when:
Holidays file is configured - Your
holidays.xlsxcontains the event datesEvents fall within planning window - The holiday date is within your optimization horizon
Historical data shows patterns - Prophet learns the effect from your training data
Example:
Planning window: 13 weeks starting Oct 17, 2025
Black Friday: Nov 28, 2025 (6 weeks ahead)
Result: Black Friday IS captured (within window)
Planning window: 13 weeks starting Oct 17, 2025
Christmas: Dec 25, 2025 (10 weeks ahead)
Result: Christmas IS captured (within window)
Planning window: 13 weeks starting Oct 17, 2025
Easter 2026: April 20, 2026 (26 weeks ahead)
Result: Easter NOT captured (beyond window) ⚠️
To include more holidays: Extend your planning window
# 26 weeks captures Q4 holidays + Q1
n_time_periods=26
Configuring Holidays¶
Add events to your holidays.xlsx file:
ds,holiday,country,year
2024-11-29,Black_Friday,US,2024
2025-11-28,Black_Friday,US,2025
2026-11-27,Black_Friday,US,2026
2024-12-25,Christmas,US,2024
2025-12-25,Christmas,US,2025
Prophet will learn the average effect of each holiday from your historical data and project it forward.
Checking Holiday Effects¶
Verify Prophet has captured your holidays:
# After model fitting
prophet_model = driver.model.prophet_model
forecast = prophet_model.predict(prophet_model.history)
# Check if holidays component exists and has variation
if 'holidays' in forecast.columns:
print(f"Holiday effect range: {forecast['holidays'].min():.2f} to {forecast['holidays'].max():.2f}")
# Show which dates have holiday effects
holiday_dates = forecast[forecast['holidays'] != 0][['ds', 'holidays']]
print(holiday_dates)
The Black Friday Planning Problem: Buildup vs Event Allocation¶
The Strategic Question: Should you allocate more budget to the weeks before Black Friday or during Black Friday week itself?
The Short Answer: Marketing science suggests weighted toward the buildup (2-3 weeks before), but the optimal split depends on your brand. Let your MMM discover the answer empirically.
Marketing Theory & Research¶
1. The Carryover Effect (Adstock)¶
Principle: Advertising has lagged effects. Spend in weeks 1-3 before Black Friday continues to influence purchases during the event.
Research:
Naik, Mantrala & Sawyer (1998) - “Planning Media Schedules in the Presence of Dynamic Advertising Quality”
Pulsing strategies (concentrated bursts before events) outperform continuous spending
Pre-event concentration builds awareness that converts during the event
Sethuraman, Tellis & Briesch (2011) - Meta-analysis of advertising elasticity
Advertising effects peak 0-1 weeks after exposure but persist for several weeks
Carryover effects mean early spending has compounding impact
Implication for AMMM: If your MMM shows strong carryover parameters (high adstock), the optimization will naturally shift budget earlier to capture these compounding effects.
2. The Customer Journey¶
Consumer behaviour research shows:
3-4 weeks before Black Friday:
Research phase - Consumers browse deals, create wish lists
Ad effectiveness: Brand awareness advertising has high ROI
Channel focus: Upper-funnel (display, video, social brand)
1-2 weeks before:
Consideration phase - Comparing options, checking reviews
Ad effectiveness: Persuasion advertising critical
Channel focus: Mid-funnel (search, comparison shopping, retargeting)
Black Friday week:
Conversion phase - Executing planned purchases
Ad effectiveness: Reminder advertising, but decisions largely made
Channel focus: Lower-funnel (branded search, remarketing)
Research:
Google/Ipsos (2019) - “Black Friday Shopping behaviour”
72% of Black Friday shoppers research 2+ weeks in advance
54% create shopping lists 1-2 weeks before
Only 28% make spontaneous same-day decisions
Implication: The majority of purchasing decisions are made before Black Friday, suggesting buildup spending is critical.
3. Competitive Saturation¶
During Black Friday itself:
Extreme competition for attention (every brand advertising heavily)
Diminishing returns kick in faster due to saturation
Cost inflation (higher CPMs/CPCs during peak periods)
Research:
Bruce, Foutz & Kolsarici (2012) - “Dynamic Effectiveness of Advertising and Word of Mouth in Sequential Distribution of Short Lifecycle Products”, Journal of Marketing Research
Advertising effectiveness varies significantly based on competitive intensity
Early timing in product lifecycle (or event window) can establish stronger market position
Word-of-mouth effects amplify early advertising investments
Implication: Your advertising may face less competition and achieve stronger effectiveness in the buildup weeks compared to the highly saturated Black Friday period itself.
Typical Optimal Allocation Pattern¶
Based on marketing literature and industry practice:
Week -4 to -3: 15-20% of total budget (awareness building)
Week -2 to -1: 30-35% of total budget (peak consideration)
Week 0 (BF): 25-30% of total budget (conversion + share of voice)
Week +1: 10-15% of total budget (Cyber Monday + stragglers)
However: This is a starting point. Your MMM will discover the optimal pattern for YOUR brand from YOUR data.
How AMMM Discovers Optimal Timing¶
Your multi-period optimization will empirically determine the best allocation by:
Prophet learns lag structures from historical data:
If you historically spent more in buildup weeks and saw strong Black Friday sales
Prophet encodes that lag relationship in its forecast
Response curves capture dynamics:
The saturation curves show how spending in week T affects outcomes in weeks T, T+1, T+2
Multi-period optimization accounts for these cross-period effects
Seasonal multipliers show when channels are most effective:
If buildup weeks have higher multipliers than Black Friday week
Optimization allocates more budget to those higher-ROI weeks
Validating Your Black Friday Strategy¶
After running your multi-period optimization, validate the results:
import pandas as pd
import matplotlib.pyplot as plt
# Load results
df = pd.read_csv('results/csv/multiperiod_optimization_results.csv')
# Extract Black Friday window (adjust period numbers to your dates)
bf_window = df[(df['period'] >= 4) & (df['period'] <= 8)]
# Check budget allocation pattern
budget_by_period = bf_window.groupby('period')['budget'].sum()
print("\nBudget allocation around Black Friday:")
print(budget_by_period)
# Check seasonal multipliers
multipliers = bf_window[bf_window['channel']=='google'][['period', 'period_date', 'seasonal_multiplier']]
print("\nSeasonal effectiveness:")
print(multipliers)
# Visualize
plt.figure(figsize=(10, 6))
budget_by_period.plot(kind='bar')
plt.title('Budget Allocation: Black Friday Window')
plt.xlabel('Week')
plt.ylabel('Total Budget')
plt.show()
Questions to ask:
Is budget ramping up in weeks leading to Black Friday?
Where is the peak allocation - before or during the event?
How do seasonal multipliers change across the window?
Case Study: 26-Week Black Friday Planning¶
Scenario: Retail brand planning from mid-October through Q1
# Capture full Black Friday season + post-holiday period
results = ammm.optimize_marketing_budget(
model=driver.model,
data=driver.processed_data,
config=driver.config,
results_dir='results',
n_time_periods=26, # Oct through April
multiperiod_mode=True,
use_seasonality=True,
frequency='W',
start_date='2025-10-17' # Mid-October
)
Expected seasonal pattern:
Weeks 0-5 (Oct-Nov): Baseline/ramping (multipliers ~0.95-1.05)
Weeks 6-7 (Black Friday/Cyber Monday): Peak event (multipliers >1.10)
Weeks 8-10 (Dec - Christmas): Sustained high (multipliers 1.05-1.15)
Weeks 11-12 (Post-Christmas): Drop-off (multipliers ~0.90-0.95)
Weeks 13-26 (Jan-Apr): Q1 baseline (multipliers 0.85-1.00)
Optimization will:
Front-load budget to Q4 (higher seasonal multipliers)
Concentrate spend 2-3 weeks before Black Friday (based on learned carryover)
Reduce budget post-holidays (lower multipliers)
Channel-Specific Holiday Strategies¶
Different channels may have different optimal timing:
Search (Google, Bing):
Peak effectiveness: During event (high-intent queries spike)
Strategy: Moderate buildup, heavy during Black Friday
Social (Facebook, TikTok, Instagram):
Peak effectiveness: 2-3 weeks before (awareness and consideration)
Strategy: Heavy buildup, maintain during event
TV/Display:
Peak effectiveness: 3-4 weeks before (brand awareness, long lead)
Strategy: Very heavy buildup, reduce during event
Your MMM learns this automatically from historical data and will allocate accordingly.
Extending Beyond Black Friday¶
The same principles apply to other retail events:
Prime Day (Amazon):
Shorter customer journey (impulse/deal-driven)
Suggest: 60% buildup (1-2 weeks), 40% during event
Christmas:
Very long planning window (starts October)
Suggest: Steady buildup from Oct, peak mid-December
Back to School:
Medium planning window (3-4 weeks)
Suggest: 70% buildup, 30% during peak weeks
Let your MMM’s optimization discover the optimal timing for each event based on your specific customer behaviour.
Further Reading¶
Peer-Reviewed Academic Research:
Naik, Mantrala & Sawyer (1998) - “Planning Media Schedules in the Presence of Dynamic Advertising Quality”, Marketing Science
Sethuraman, Tellis & Briesch (2011) - “How Well Does Advertising Work?”, Journal of Marketing Research
Tellis & Weiss (1995) - “Does TV Advertising Really Affect Sales?”, Journal of Marketing
Bruce, Foutz & Kolsarici (2012) - “Dynamic Effectiveness of Advertising and Word of Mouth in Sequential Distribution of Short Lifecycle Products”, Journal of Marketing Research
Industry Research (Non-Peer-Reviewed):
Google/Ipsos (2019) - “Black Friday Shopping Behaviour” - Consumer research study
Google Retail Insights - Annual Black Friday shopper behaviour studies
Facebook Holiday Marketing Guide - Customer journey research
Kantar - Seasonal advertising effectiveness studies
Key Takeaway: Multi-period optimization with seasonality lets your MMM empirically determine optimal timing based on YOUR data, rather than relying solely on industry averages.
Troubleshooting¶
Issue: “No Prophet model found”¶
Cause: Your MMM model doesn’t have Prophet seasonality fitted.
Solution: Ensure Prophet is configured in your model config:
baseline_model: 'prophet' # Use Prophet for baseline/trend
Workaround: Run without seasonality:
use_seasonality=False
Issue: Optimization fails with “Infeasible constraints”¶
Cause: Per-period budget limits are too restrictive given total budget.
Example:
Total budget: £500K
Per-period limits: (£200K, £300K) per week
4 weeks: Minimum needed = 4 × £200K = £800K (exceeds £500K!)
Solution: Adjust constraints:
period_budget_limits=(100_000, 200_000) # More flexible
Or increase total budget.
Issue: Results show same budget for all periods¶
Cause: Seasonality not enabled or no seasonal variation in data.
Solutions:
Enable seasonality:
use_seasonality=TrueCheck if Prophet has seasonal components (see “Checking Seasonal Patterns” above)
If data truly has no seasonality, this is expected behaviour
Issue: Budget allocation seems unrealistic¶
Cause: Budget bounds may be too wide or too narrow.
Solution: Adjust bounds:
from src.driver.opt import optimize_multiperiod_budget
results = optimize_multiperiod_budget(
...,
lower_bound_pct=0.10, # Channels can go 10% below historical mean
upper_bound_pct=0.30 # Channels can go 30% above historical mean
)
Issue: Slow optimization (>5 minutes)¶
Cause: Large number of periods × channels creates many variables.
Example: 52 weeks × 20 channels = 1,040 optimization variables
Solutions:
Reduce planning horizon:
n_periods=13instead of 52Use faster frequency: monthly instead of weekly
Simplify constraints (remove per-period limits)
Advanced Topics¶
Channel-Specific Budget Bounds¶
Currently all channels use the same bound percentages (±20%). To customize per-channel:
# Directly call core function with custom bounds
from src.core.opt import optimize_multiperiod_budget_distribution
budget_ranges = {
'google': (5000, 25000), # Google: £5K-£25K per period
'facebook': (10000, 50000), # Facebook: £10K-£50K per period
'tv': (50000, 200000) # TV: £50K-£200K per period
}
optimized_budget = optimize_multiperiod_budget_distribution(
method='sigmoid',
total_budget=12_500_000,
budget_ranges=budget_ranges,
parameters=sigmoid_params,
channels=list(budget_ranges.keys()),
n_periods=13,
seasonal_effects=seasonal_effects
)
Equal Budget Per Period¶
To force equal budget across all periods:
period_budget = total_budget / n_periods
period_budget_limits=(period_budget, period_budget) # Force exact amount
Weekly vs Monthly Planning¶
Weekly Planning (more granular):
Pros: Accounts for within-month variation, precise allocation
Cons: More variables (slower), requires weekly data
Monthly Planning (aggregated):
Pros: Faster optimization, simpler to execute
Cons: Misses intra-month patterns, less flexible
Best Practices¶
Start with fewer periods - Test with 4-8 periods before running full year
Validate seasonality - Check Prophet components make sense for your business
Compare with/without seasonality - See impact of seasonal adjustments
Monitor constraint feasibility - Ensure constraints don’t over-restrict optimization
Cross-validate results - Compare multi-period results with historical performance
Integration with Main Pipeline¶
Multi-period optimization is integrated into runme.py and can be enabled via CLI:
# Enable multi-period mode (13 weeks, with seasonality)
python runme.py --multiperiod
# Or modify runme.py directly for custom settings
See runme.py for full integration example.
Examples in Code¶
See test_multiperiod.py for comprehensive examples testing:
Basic multi-period optimization
With seasonality adjustments
With per-period budget constraints
Validation of results
Run tests:
python test_multiperiod.py
Further Reading¶
Optimisation Guide - Single-period budget optimization
Prophet Documentation - Seasonality configuration
Configuration Guide - Setting up your model config
Support¶
For issues or questions:
Check the Troubleshooting section above
Review GitHub Issues
Create new issue with reproducible example