Skip to content

Guide: Budget Optimisation

This guide covers single-period budget optimisation in AMMM V2.

  • Running optimisation from runme.py
  • Reading optimisation artefacts in 70_optimisation/
  • Practical checks before using recommendations

AMMM solves a constrained allocation problem:

$$ \max_{{S_i}} \sum_i C_i(S_i) \quad \text{subject to} \quad \sum_i S_i = B $$

Where:

  • $S_i$ is spend on channel $i$
  • $C_i(\cdot)$ is the fitted response curve
  • $B$ is total budget

Scenario planning (default):

Terminal window
python runme.py
python runme.py --scenarios "-20,-10,0,10,20"

Single-period optimisation:

Terminal window
python runme.py --no-scenarios

All optimisation artefacts are saved in 70_optimisation/.

Primary files:

  • optimization_results.csv
  • budget_optimisation.png
  • budget_scenario_results.csv (scenario mode)
  • scenario_budget_allocation.png
  • scenario_total_contribution.png
  • scenario_roi_comparison.png

Optimisation should be consumed only after diagnostic review:

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

If diagnostics are weak, treat optimisation outputs as exploratory.

  • Single-period optimisation uses SciPy constrained solvers (SLSQP).
  • Channel bounds are enforced.
  • Total budget is fixed; optimisation redistributes spend across channels.
import core.opt as opt
channels = ["tv", "search", "social"]
parameters = {
"tv": (50000.0, 1.0e-5),
"search": (60000.0, 8.0e-6),
"social": (40000.0, 1.2e-5),
}
result = opt.budget_allocator(
method="sigmoid",
total_budget=100_000.0,
channels=channels,
parameters=parameters,
)
print(result)

For planning across time with seasonality and ramp constraints, see Multi-Period Optimisation.