← Back to the budget tool Signed in as -
Phase 1  /  Pre-MMM  /  Confidential

Methodology & Assumptions

How the FY27 media budget model actually calculates its numbers, formula by formula, including the parts that are assumptions rather than facts.

On this page

  1. What this model is, and isn't
  2. The building block: MER and the seasonality index
  3. Blending three years of history
  4. The Google demand signal
  5. The demand correction strength slider
  6. The efficiency vs. effectiveness dial
  7. Locking months, actuals, and reforecasting
  8. Manual overrides
  9. Known limitations
  10. Sensitivity checks worth running

01What this model is, and isn't

This is a seasonality-and-efficiency allocation model. Given a fixed annual media pool and a month-by-month revenue (TTV) target, it phases that pool across the 12 months so the months historically best at converting spend into TTV carry more of the load, adjusted for how current market demand compares to history.

The tool labels itself Pre-MMM. This is important because it is not a marketing mix model, and it does not model:

What it does do with real rigour: take three years of actual month-by-month efficiency, blend them with adjustable recency weighting, correct that blend for an independent read of current market demand, and use a single slider to set how much weight that efficiency read gets against simply mirroring the revenue target's own shape.

02The building block: MER and the seasonality index

Everything starts from Media Efficiency Ratio (MER), TTV divided by spend, calculated per month and per fiscal year for three years of TripADeal history (FY23/24, FY24/25, FY25/26).

mer[year][month] = ttv[year][month] / spend[year][month]

The annual baseline for each year is spend-weighted, not a simple average of the 12 monthly MERs:

annual[year] = sum(ttv[year]) / sum(spend[year])
Note Because it's spend-weighted, months where TripADeal historically spent more pull the annual baseline toward their own efficiency more than low-spend months do. Two years with identical average monthly MER but different spend distributions produce different annual baselines.

Each month is then indexed against its own year's baseline. This is the number used everywhere downstream:

idx[year][month] = mer[year][month] / annual[year]

An idx of 1.20 means that month ran 20% more efficiently than its year's average month. An idx of 0.80 means the opposite. This is a relative read: it strips out year-on-year growth or decline in overall efficiency, so the three years become comparable regardless of whether one year was simply a better year overall.

Best-ever and the Proven/Stretch split

For every calendar month, the tool takes the single best MER any of the three years achieved:

best[month] = max(mer[FY23/24][month], mer[FY24/25][month], mer[FY25/26][month])

The Recommendation table's Proven/Stretch tag compares the model's required MER for that month (TTV target divided by recommended spend) against this ceiling. Proven means the plan asks for no more efficiency than history has already delivered at least once. Stretch means it asks for something no year has ever actually achieved in that month.

Spread and signal confidence

For each month, the tool also measures how much the three years' seasonality indices disagree with each other:

spread[month] = max(idx across 3 years) - min(idx across 3 years) signal = spread < 0.25 -> HIGH confidence spread < 0.60 -> MEDIUM confidence otherwise -> LOW confidence

A small spread means the three years broadly agree that a month is seasonally strong or weak, so the pattern is probably real. A large spread means the years disagree, which usually points to one-off campaign timing or a single unusual year masquerading as seasonality.

03Blending three years of history

The three years' seasonality indices combine into a single blended index per month using adjustable recency weights, the numbers in the "Weighting & seasonality" grid, one weight per year per month:

blend[month] = (idx[FY23/24][month]·W[0][month] + idx[FY24/25][month]·W[1][month] + idx[FY25/26][month]·W[2][month]) ──────────────────────────────────────────────────────────────────────── W[0][month] + W[1][month] + W[2][month]

Because the formula divides by the sum of the three weights, they don't need to add to 100%. The result is driven by the ratio between them, not their absolute size, and the tool renormalises automatically. If all three weights for a month are 0, the formula falls back to a blend of exactly 1: perfectly average, no seasonality signal at all. That default is silent, so it's easy to miss if a month's weights are ever cleared entirely.

The default starting weights

The tool doesn't open on flat 20/30/50 recency weighting (FY23/24 / FY24/25 / FY25/26) for every month. The default "Current" preset applies 20/30/50 as a baseline, then overrides five specific months to 30/50/20 instead (Jul, Dec, Feb, Mar, Apr), and further overrides July uniquely to 30/70/0. That means zero weight on FY25/26 for July specifically.

The tool's own interface requires an explanation for any non-default weight, logged in the "Event & dates" row, and flags a month if one is missing.

04The Google demand signal

Historical seasonality alone can't distinguish "this month is efficient because of something structural about the calendar" from "this month was efficient three years running by coincidence, and market conditions have since changed." The demand signal is the model's attempt to correct for the second case, using external data TripADeal's own spend doesn't directly control.

Seven possible sources, each independently switchable:

SourceDefaultWhat it measures
Google TrendsOnSearch interest in the package/tour category generally. The only source fully independent of TripADeal's own spend, ranking, or bidding.
Search Console, brandOffOrganic impressions on TripADeal-brand searches. Partly caused by TripADeal's own brand media spend, so not fully independent.
GA4 direct sessionsOffPeople who typed the URL directly. A clean brand-demand read, unaffected by ranking or bidding.
Search Console, non-brand RiskyOffImpressions on category searches TripADeal ranks for. Moves when SEO ranking changes, independent of any real change in market demand.
Paid search, non-brand themes RiskyOffDriven directly by TripADeal's own paid search budget and bidding. This is a media output, not a market read.
GA4 organic sessions RiskyOffSits downstream of demand, ranking, and click-through all at once, so it moves for reasons unrelated to the market.
Google Ads impressions RiskyOffDirectly driven by TripADeal's own ad budget and bidding strategy. The tool's own description marks this diagnostic only.
Default state, as shipped Only Google Trends is switched on by default. Every number in the model, unless someone has changed the toggles, is demand-corrected using search-volume trend data alone. None of the other six sources contribute anything unless manually enabled.
Circularity risk Four of the seven sources are flagged "risky" in the tool's own copy specifically because they're driven by TripADeal's own spend or ranking rather than the underlying market. If any of those are switched on, a demand signal that's partly caused by past media spend ends up feeding a decision about future media spend, which can produce a self-reinforcing loop rather than a genuine correction. The arithmetic itself isn't broken here, and the tool's "risky" labelling already discloses the issue. The impact is on interpretation, not calculation: which sources are switched on determines whether the resulting number reads as an independent market signal or partly as an echo of past spend.

How the sources are blended

For every active source, both its available years of data get pulled in and pooled, weighted by the same recency weights (W) used for the historical MER blend above, into one running numerator and denominator per month:

for each active source, for each year that source has data: numerator[month] += source_index[year][month] × W[year][month] denominator[month] += W[year][month] demand_raw[month] = numerator[month] / denominator[month]
Note This pools every active source-year combination into one shared sum. It does not average per-source results together afterward. A source with three years of coverage contributes three terms to the sum; a source with only two years (several do: Search Console only has FY24/25 partial and FY25/26) contributes two. Sources with more historical coverage carry more implicit weight in the blend simply because they contribute more terms, not because anyone weighted them higher on purpose.

The pooled result is then indexed so the 12-month average sits at exactly 1.0:

demand.idx[month] = demand_raw[month] / average(demand_raw across all 12 months)

05The demand correction strength slider

This is the "Demand correction strength" slider on the main screen. It controls how hard the demand index above is allowed to bend the historical seasonality blend:

adj[month] = blend[month] / demand.idx[month] ^ strength strength = slider value / 100 (0.00 to 1.00)

At 0%, demand.idx is raised to the power of 0, which equals 1 for any number, so the division does nothing. adj equals the pure historical blend from Section 2, completely unadjusted for current demand.

At 100%, the full demand index divides straight into the historical blend.

Mechanically: a month where current demand runs above its own annual average (demand.idx greater than 1) gets divided down. Its adjusted index falls, and that later translates into more budget for that month, not less. A month where demand is currently depressed gets pushed the other way.

Interpretation, not documented intent The model contains no comment or specification explaining the intended reasoning here, so the description above is a best-effort reading of the arithmetic, not a documented design choice. One reasonable interpretation: a month that was historically efficient and is currently seeing elevated independent demand deserves more confidence and more budget, while a month that was historically efficient partly because of temporarily favourable demand that isn't repeating this year should be corrected down. A Deloitte-trained reviewer will likely ask why more demand means more budget rather than less, given diminishing returns, since this model doesn't account for diminishing returns at all (see the limitations section). The model as it stands has no documented answer to that question.

The default sits at 50%, a halfway point between ignoring demand entirely and fully deferring to it.

06The efficiency vs. effectiveness dial

Once adj is computed above, it's renormalised so the TTV-weighted average sits at 1 across the year:

nf = sum(adj[month] × TTV27[month] for all months) / sum(TTV27) norm = adj / nf

The dial then blends this fully-adjusted index against a flat baseline of 1, meaning no adjustment at all:

applied[month] = 1 + (norm[month] - 1) × dial dial = slider value / 100 (0.00 to 1.00)

What each end of the dial means, precisely:

The required spend per month, and the raw (pre-lock, pre-override) allocation, follow directly:

need[month] = TTV27[month] / applied[month] raw[month] = need[month] / sum(need) × total pool

The default sits at 25%, closer to the "chase the revenue target's shape" end than the "chase historical efficiency" end.

07Locking months, actuals, and reforecasting

Once a month has actually happened, locking it freezes its number and treats it as effectively spent. The model doesn't re-optimise history, only what's still open:

lockedSpend[month] = if locked: (actual spend, if entered - else the manual override, if any - else the raw model number) committed = sum of all locked spend, plus all fixed (non-locked) overrides left = total pool minus committed

The remaining, still-open months are then scaled so they exactly consume whatever's left in the pool. This is a proportional redistribution of the raw model shape, not a fresh re-optimisation of the open months in isolation:

scale = left / (sum of raw[month] for every still-open month) fin[month] = locked or overridden amount, if set - else raw[month] × scale

The reforecast panel's numbers follow mechanically from this. TTV banked and spend committed sum the locked months (using actual TTV where entered, else the original target). MER to date divides one by the other. Budget remaining and TTV still to find are the pool and target totals minus what's already banked. MER required from here divides the two.

08Manual overrides

A manual override on a month not yet locked pins that month's spend to an exact dollar figure, removed from the "open" pool that gets proportionally scaled. Every other open month absorbs the effect of that override through the scale factor above. The tool ships with one override already set by default: December is pre-set to $1.5m, independent of anything a user has entered.

09Known limitations

Six of these are stated directly in the tool's own "Known gaps" tab. The last three come from reading the underlying code closely.

  1. Several months require efficiency above the best any year has ever delivered. True of the current live plan too. This reflects the level of the FY27 target itself, not how the model phases the budget.
  2. Average returns, not marginal returns. Moving money into a month assumes its next dollar performs like its average dollar, a claim this model never tests. A proper marketing mix model is the only way to answer that.
  3. Booking lag is unmeasured. Every dollar and every TTV figure is bucketed into the calendar month it fell in, with no decay curve for the gap between exposure and booking.
  4. Five months carry non-default weights as shipped (see the blending section). Each should reflect a specific, documented event logged in the "Event & dates" row.
  5. Demand isn't cleanly separated from media-driven effects unless the toggles are deliberately kept to the non-risky sources.
  6. Historical spend reflects earlier planning decisions, not a controlled experiment. A month that was deliberately underspent in the past shows inflated efficiency for that reason alone, so the seasonality index is directional, not a precise physical constant.
  7. Only Google Trends is on by default. Any demand-adjustment number quoted from an unmodified session reflects one data source, not the full suite the tool supports.
  8. The demand-correction direction is an interpretation, not a documented design choice. It should not be presented as settled reasoning without further verification.

10Sensitivity checks worth running

None of these change any saved data. They're safe to try and revert, or just skip "Save checkpoint" while exploring.

Isolate the demand correction Set "Demand correction strength" to 0%, note the plan, then set it to 100%. The gap between the two is the entire effect that slider has, in isolation, on top of pure historical seasonality.
Isolate the efficiency/effectiveness dial Set the dial to 0% (plan mirrors the TTV target exactly) and then 100% (plan follows pure historical and demand-adjusted efficiency). The live plan sits at a blend point between these two extremes.
Test single-source sensitivity With everything else fixed, toggle one Google source on at a time from the default all-off-except-Trends state. Whichever toggle moves the plan the most deserves the closest look, especially the four flagged "risky."
Compare weighting presets Switch between "Current" (the default, with its five month-specific overrides) and "Default" (flat 20/30/50 everywhere) to see how much of the live plan's shape comes from those specific overrides versus the underlying three-year data.