Skip to content

Multi-Period Budget Optimisation

This guide explains AMMM V2 multi-period optimisation, including seasonality adjustments and ramp constraints.

  • Enabling multi-period mode from runme.py
  • Interpreting 70_optimisation/ multi-period artefacts
  • Constraint design (ramp-abs, ramp-pct, JSON ramp config)
  • Common failure modes and practical mitigation

Use multi-period optimisation when:

  • planning budgets across several future periods (for example 13 weeks);
  • seasonal effects materially change channel efficiency over time;
  • you need period-level allocation, not a single aggregate recommendation.

Use single-period mode when:

  • you need a fast first-pass allocation;
  • planning horizon detail is not required.
Terminal window
# Default multi-period run (13 periods)
python runme.py --multiperiod
# Custom horizon
python runme.py --multiperiod --multiperiod-weeks 26
# Disable seasonality adjustments
python runme.py --multiperiod --no-seasonality

Important:

  • --use-adstock is not compatible with --multiperiod in runme.py.

AMMM allocates spend by period and channel:

$$ \max_{{S_{p,i}}} \sum_{p=1}^{P}\sum_{i=1}^{n} w_{p,i} , C_i(S_{p,i}) $$

Subject to:

  • total budget constraint;
  • per-channel bounds;
  • optional per-period limits;
  • optional ramp constraints between consecutive periods.
Terminal window
# Absolute ramp limit
python runme.py --multiperiod --ramp-abs 20000
# Percentage ramp limit
python runme.py --multiperiod --ramp-pct 0.25
# Both
python runme.py --multiperiod --ramp-abs 20000 --ramp-pct 0.25
Terminal window
python runme.py --multiperiod --ramp-config constraints.json
python runme.py --multiperiod --ramp-config constraints.json --strict-ramp-config

Related flags:

  • --ramp-eps for safe percentage denominators;
  • --round-increment for rounded budget recommendations.

Seasonality multipliers can be clipped:

Terminal window
python runme.py --multiperiod --seasonality-clip-min 0.7 --seasonality-clip-max 1.3

These clips help avoid extreme multiplier-driven allocations.

All files below are written to 70_optimisation/:

  • multiperiod_optimization_results.csv
  • multiperiod_budget_heatmap.png
  • multiperiod_contribution_over_time.png
  • multiperiod_seasonal_patterns.png (when seasonality is used)
  • multiperiod_period_comparison.png
  • multiperiod_budget_vs_contribution.png

CSV schema (multiperiod_optimization_results.csv)

Section titled “CSV schema (multiperiod_optimization_results.csv)”

Typical columns:

  • period
  • period_date
  • channel
  • budget
  • contribution
  • seasonal_multiplier
  • roi

Review diagnostics first:

  1. 50_diagnostics/convergence_report.json (converged)
  2. 50_diagnostics/calibration_report.json (well_calibrated)
  3. 50_diagnostics/pareto_k_summary.json (ok)

Then inspect multi-period outputs for operational plausibility.

Symptoms:

  • solver does not converge;
  • infeasible or unstable allocation pattern.

Mitigations:

  1. Reduce horizon (--multiperiod-weeks 4 as a smoke test).
  2. Use a single ramp type first (--ramp-abs or --ramp-pct).
  3. Relax very tight ramps.
  4. Reduce optional constraints, then reintroduce incrementally.

Likely causes:

  • seasonality disabled (--no-seasonality);
  • weak seasonal variation in learned baseline.

Likely causes:

  • no ramp constraints;
  • overly broad seasonality clips.
import src as ammm
from driver import MMMBaseDriverV2
driver = MMMBaseDriverV2(
config_filename="data-config/demo_config.yml",
input_filename="data-config/demo_data.csv",
holidays_filename="data-config/holidays.csv",
results_filename="results",
)
driver.main()
ammm.optimize_marketing_budget(
model=driver.model,
data=driver.processed_data,
config=driver.config,
results_dir=driver.results_dir,
multiperiod_mode=True,
n_time_periods=13,
use_seasonality=True,
frequency="W",
)
  1. Fit and validate model diagnostics.
  2. Run --multiperiod with a short horizon first.
  3. Add ramps conservatively.
  4. Scale to full horizon after stable solver behaviour.
  5. Review period-level outputs with business constraints.