Daily campaign report — 2026-08-03, against 2026-08-02

Two campaigns bought inside this UTC day and they did not share a clock hour. The incumbent worldwide campaign delivered from 04:00 UTC until it was switched off at 11:20; a newly built campaign holding five ad sets, one creative each at $100/day, began delivering at 12:44 and ran to midnight. Together they spent $445.83 and bought 766 installs on Meta's count, 805 on ours. The report clock, the three revenue measures and regime are defined in the appendix.

1Overview

2026-08-022026-08-03
spend$278.28$445.83
impressions28,955213,053
CPM$9.61$2.09
link clicks2,8535,649
CTR9.85%2.65%
installs (Meta)745766
click → install26.1%13.6%
cost per install$0.374$0.582
installs (ours)795805
booked$559.00 · ROAS 2.01 (Meta $610.49 / 2.19)$709.35 · ROAS 1.59 (Meta $738.83 / 1.66)
within-day$272.13 · ROAS 0.98$397.69 · ROAS 0.89
cohort @ 24h$360.61 · ROAS 1.30 · 100% covered$474.05 · ROAS 1.06 · 100% covered
within-day payers3239
within-day ARPU / ARPPU$0.342 / $8.50$0.494 / $10.20

⚠ The blended delivery columns for 08-03 are unusable and must not be quoted. One ad (spill) bought 85.7% of the day's impressions, and 99.6% of its own impressions landed inside a single hour at a CPM of $0.52. That one hour drags account CPM to $2.09, CTR to 2.65% and click→install to 13.6%, none of which describes the day. Excluding that one ad:

08-0208-03 blended08-03 less spill
spend$278.28$445.83$336.11
impressions28,955213,05330,405
CPM$9.61$2.09$11.05
CTR9.85%2.65%8.61%
click → install26.1%13.6%26.5%
cost per install$0.374$0.582$0.485

On the honest row the day got dearer, not worse at converting. 5% more impressions at a 15% higher CPM, the same click-to-install rate (26.5% against 26.1%), and 30% more per install. Spend rose 21% while Meta-counted installs fell 7%, which is the whole of the move.

All three revenue measures fall, so the direction is not a basis artifact. Booked 2.01 → 1.59, within-day 0.98 → 0.89, cohort@24h 1.30 → 1.06.

2Hourly, 2026-08-03

Delivery columns are Meta's on both sides; payer counts, ARPU and ARPPU are ours on both sides. The two install counts are never crossed. Booked sits on the payment hour; within-day and cohort sit on the install hour. Hours are UTC.

hrspendimprCPMclicksCTRinstclk→iCPIbkd$bROASwd$wROASwPaycoh$cROAS
00.000038.750.000.0000.000.00
10.0000160.760.000.0000.000.00
20.0000041.690.000.0000.000.00
30.0000126.210.000.0000.000.00
45.6551510.975811.26%1017.2%0.5656.471.140.000.0000.000.00
519.642,0099.781899.41%3820.1%0.51710.730.554.990.2514.990.25
624.632,4719.972228.98%6127.5%0.40426.321.0720.060.81320.060.81
727.852,8049.932428.63%5422.3%0.51625.870.9336.631.32440.191.44
830.143,4208.812667.78%6223.3%0.48623.950.7978.622.61578.622.61
929.743,7267.982947.89%7726.2%0.38638.961.3129.981.01348.721.64
1029.543,2039.222507.81%6124.4%0.48419.710.6714.710.50214.710.50
119.081,0468.68999.46%3535.4%0.25952.185.7549.725.48449.725.48
120.29338.79412.12%4100.0%0.07315.5653.670.000.0000.000.00
1318.841,59011.851116.98%2522.5%0.75421.601.1520.411.08326.311.40
14107.17182,8050.593,0001.64%612.0%1.75721.960.200.000.00019.160.18
158.108239.84769.23%2127.6%0.38638.224.725.630.7015.630.70
1614.191,08013.14948.70%2425.5%0.59187.446.1623.201.63223.201.63
1716.031,26712.651038.13%2423.3%0.66822.861.435.760.3615.760.36
1818.311,18115.501079.06%3229.9%0.57228.871.5825.771.41434.941.91
1921.441,26316.981219.58%3327.3%0.65018.880.8832.521.52132.521.52
2018.581,14116.2811610.17%4034.5%0.4659.160.490.000.0006.840.37
2119.931,04619.0512912.33%3426.4%0.58623.221.170.000.0000.000.00
2213.6685915.909511.06%3637.9%0.37948.373.5418.101.33321.771.59
2313.0277116.89739.47%2939.7%0.44931.592.4331.592.43240.923.14
ALL445.83213,0532.095,6492.65%76613.6%0.582709.351.59397.690.8939474.051.06

Hour 14 is the whole anomaly of the day, in one line. $107.17 bought 182,805 impressions at a $0.59 CPM, against $8 to $19 in every other delivering hour, converted 2.0% of clicks to installs against 17% to 40% in every other hour that bought more than a hundred clicks, and returned zero within-day revenue from 61 installs. That one hour is 24.0% of the day's spend and 85.8% of its impressions.

The first four rows carry booked revenue against no spend. Those are hours in which nobody was buying but yesterday's and the day before's installs were still paying, which is what the booked basis counts and the other two do not.

Hourly payer counts run 0–5, so an hourly ARPPU is one person's basket rather than a rate. Read the ALL row for level and the hours for shape only. The CPM path is the clean signal in this table, because its denominator is impressions rather than payers: the incumbent's hours (04:00–11:00) run $7.98 to $10.97 with no trend, and the new campaign's hours climb from $11.85 at 13:00 to $15.50 and $19.05 across 18:00–23:00.

2026-08-05T10:30:27.056632 image/svg+xml Matplotlib v3.11.1, https://matplotlib.org/ 0 3 6 9 12 15 18 21 hour (UTC) 0 20 40 60 80 100 USD Spend by hour 0 3 6 9 12 15 18 21 hour (UTC) 0 25000 50000 75000 100000 125000 150000 175000 impressions Impressions and installs by hour impressions installs 0 10 20 30 40 50 60 70 80 installs (Meta Leads) The day's motion — whole account, 2026-08-03 (UTC)
The day's motion: spend by hour, and impressions against installs
2026-08-05T10:30:27.279759 image/svg+xml Matplotlib v3.11.1, https://matplotlib.org/ 0 4 8 12 16 20 hour (UTC) 2 4 6 8 10 12 CTR % CTR 0 4 8 12 16 20 hour (UTC) 0.0 2.5 5.0 7.5 10.0 12.5 15.0 17.5 USD per 1,000 impressions CPM 0 4 8 12 16 20 hour (UTC) 5 10 15 20 25 30 35 40 installs / clicks % Click → install 0 4 8 12 16 20 hour (UTC) 0.2 0.4 0.6 0.8 1.0 1.2 1.4 1.6 1.8 USD Cost per install Delivery by hour — whole account, 2026-08-03 (UTC)
CTR, CPM, click-to-install and CPI by hour, whole account

Against 2026-08-02, hour matched to hour

Only hours both days delivered in are compared. The diurnal shape is the largest confound in a day-over-day read on this account, so an hour one day is missing is dropped rather than paired with nothing. This runs on the report clock, from the same loader as the table above, because spend, impressions, clicks and installs sit on Meta's delivery hour and restitch across two account days exactly.

Hours both days delivered in, on the UTC clock: 08, 09, 10, 11, 13, 14, 15, 16, 17. 2026-08-03 additionally delivered in 04, 05, 06, 07, 18, 19, 20, 21, 22, 23. 2026-08-02 additionally delivered in 00, 12. Only the shared hours are compared.

Counting which way each shared hour points, which needs no weighting and asks only for a direction:

metrichours 2026-08-03 ran higherhours it ran lowersign test
CPM5 of 94 of 9p = 1.000
CTR1 of 98 of 9p = 0.039
click→install5 of 94 of 9p = 1.000
CPI7 of 92 of 9p = 0.180

A sign test throws magnitude away and buys weight-free direction with it: it answers which way the day moved and never by how much.

One ratio separates, and it is CTR. It ran lower on 08-03 in 8 of the 9 shared hours, p = 0.039, which is a property of the day rather than of any one hour in it. CPM and click→install are coin flips on the unweighted count at p = 1.000 apiece, and CPI leans dearer in 7 of 9 hours without clearing 0.05. That last one is the shape a rising price takes on a nine-hour sample: visible in the count, not yet decidable. §4's pooled row reads click→install −62.8% at p = 0.000 over these same nine hours, which the hour count contradicts outright, and the disagreement is the point of running both.

The full hour-by-hour pairing, 2026-08-02 → 2026-08-03

Every shared hour, 2026-08-02 → 2026-08-03. All columns are Meta's instrument on both sides of each ratio. Revenue is deliberately absent: per-hour payer counts run 0-3, so an hourly revenue column would be one person's basket read as a rate.

hrimpressionsCPMCTRclick→installCPI
081,512 → 3,4208.79 → 8.8110.98% → 7.78%17.5% → 23.3%0.458 → 0.486
094,266 → 3,7268.46 → 7.988.74% → 7.89%22.0% → 26.2%0.440 → 0.386
104,119 → 3,20310.05 → 9.229.42% → 7.81%24.7% → 24.4%0.431 → 0.484
113,119 → 1,0468.89 → 8.689.43% → 9.46%27.2% → 35.4%0.347 → 0.259
132,752 → 1,59011.32 → 11.8511.12% → 6.98%23.5% → 22.5%0.433 → 0.754
142,578 → 182,8059.88 → 0.5910.78% → 1.64%28.4% → 2.0%0.322 → 1.757
152,670 → 8239.09 → 9.8410.19% → 9.23%24.6% → 27.6%0.362 → 0.386
163,024 → 1,0809.11 → 13.1410.35% → 8.70%22.4% → 25.5%0.393 → 0.591
171,543 → 1,2678.71 → 12.659.07% → 8.13%30.7% → 23.3%0.313 → 0.668

Hour 14 owns the pooled row. In the pairing it runs 2,578 → 182,805 impressions, CPM 9.88 → 0.59 and CTR 10.78% → 1.64%, while the other eight shared hours move by ordinary amounts and in both directions. Of the 198,960 impressions §4's pooled row carries for this day, 182,805 are that single hour. It is the rejected spill build (DECISION_LOG.md #50), and any pooled comparison of these two days is mostly a report about it.

2026-08-05T10:30:27.528711 image/svg+xml Matplotlib v3.11.1, https://matplotlib.org/ 8 10 12 14 16 hour (UTC) 2 4 6 8 10 CTR % CTR 2026-08-02 2026-08-03 8 10 12 14 16 hour (UTC) 0 2 4 6 8 10 12 USD per 1,000 impressions CPM 8 10 12 14 16 hour (UTC) 5 10 15 20 25 30 35 installs / clicks % Click → install 8 10 12 14 16 hour (UTC) 0.2 0.4 0.6 0.8 1.0 1.2 1.4 1.6 1.8 USD Cost per install Delivery by hour — 2026-08-03 against 2026-08-02, shared hours only (UTC)
The same four ratios with the prior day laid over the day, shared hours only

3Country

This table is a META-day cut on its spend and install columns. Meta serves no hour × country grid, so per-country delivery exists only on the ad account's own UTC-7 day and cannot be restitched. Its revenue columns are bucketed on the UTC clock like the rest of the report, so the ratio columns cross a seven-hour offset. Read this table for mix, never for level against §1 or §2.

Top twelve by spend, all three measures.

ccspendinstCPIbookedbROASwithinwROAScohort@24cROAS
IN121.991930.632147.931.2197.680.80125.731.03
US41.57291.43333.940.824.990.124.990.12
GB16.69240.69516.901.0116.901.0116.901.01
PH16.44460.35715.670.955.630.345.630.34
MX14.55270.53911.420.7811.420.7814.821.02
ID14.15300.47221.951.5515.961.1315.961.13
MY13.86160.86611.740.8511.740.8511.740.85
TR13.18190.69453.224.0453.224.0453.224.04
FR13.08190.68831.152.3831.152.3831.152.38
AR12.34360.3432.990.240.000.000.000.00
AU8.9481.11719.762.210.000.000.000.00
ES8.3481.04238.844.6638.844.6638.844.66

India is a quarter of the account day's buy at 24.4% of the META-day spend, and it is the only country here with a sample worth reading: 193 installs at $0.632, cohort@24h 1.03. The whole day carries 39 within-day payers at a mean of $10.20 each, so every country below India is a handful of baskets: Turkey's $53.22 and Spain's $38.84 are the largest of them and neither can be more than five or six people. Those ratios are directions and not magnitudes, and where a country carries a headline the largest single payer's share is what decides whether it means anything.

Thailand and Brazil are absent from the top of this table for a reason that is not performance. The account lost the ability to serve them, and the only cells in either country with more than a dollar of spend on this day belong to the incumbent campaign, which stopped buying before noon. See _runs/2026-08-04_e2_start_and_0803_report/th_br_what_its_worth.md.

2026-08-05T10:30:27.683191 image/svg+xml Matplotlib v3.11.1, https://matplotlib.org/ IN US GB PH MX ID MY TR FR AR AU ES 0 20 40 60 80 100 120 USD spend and installs are a META-day cut (no hour x country grid exists); revenue is the report day Spend against within-day revenue by country — 2026-08-03 spend within-day revenue
Spend against within-day revenue, by country
2026-08-05T10:30:27.739141 image/svg+xml Matplotlib v3.11.1, https://matplotlib.org/ IN US GB PH MX ID MY TR FR AR AU ES 0.0 0.2 0.4 0.6 0.8 1.0 1.2 1.4 USD per install Cost per install by country — 2026-08-03 (META day)
Cost per install by country

4Aggregate, both days

Pooled over the 9 hours both days delivered in, on the UTC clock. Meta's instrument on both sides of every ratio.

spendimpressionsCPMclicksCTRinstallsclick→installCPI
2026-08-02$240.3925,5839.402,5309.89%61824.43%$0.389
2026-08-03$262.83198,9601.324,2932.16%3909.08%$0.674

Pooling across hours is a mix comparison. It weights each hour by how much it delivered, so a day that bought differently across the clock can move these rows without any hour changing. The hour-by-hour pairing in the hourly section is the unweighted read of the same two days; where the two disagree, the mix moved.

The two reads disagree on click→install and agree on CTR. Pooled, click→install falls 62.8% at p = 0.000; counted hour by hour it rose in 5 of the 9 shared hours and the sign test reads p = 1.000. The pooled fall is hour 14 by itself, where 182,805 impressions converted 2.0% of their clicks, so it is not a statement about how the account converts and must not be quoted as one. CTR is the other case: pooled it falls 78.2%, and the unweighted count agrees that it fell, in 8 of 9 hours at p = 0.039. Even there the size belongs to hour 14 and only the direction survives both reads. Where the two agree, the day moved; where only the pooled row moves, the mix did.

Held to the incumbent campaign's three creatives, on the ad account's own clock. Matched-hour delivery for a single creative has to be cut on the clock the exports are stamped in, so the hours below are the ad account's own UTC-7 hours. They land at 08:00–11:59 UTC inside both report days, so the block sits inside this report's day and inside the comparison day, but its hour labels are not the ones in §2, and its pooled rows are pooled over four Meta hours rather than the nine UTC hours above.

Delivering META hours — 08-02: [1–10, 21–23]; 08-03: [0–4]. Shared: [1, 2, 3, 4]. Maturity coverage at H=6 is 100% on both days. Only the incumbent campaign's three creatives existed on both days, so the comparison is held to them.

held tometric08-0208-03changepverdict
pooledclick → install25.63%29.37%+14.6%0.055not separable
pooledpayer rate2.88%5.24%+82.4%0.145not separable
pooledCPI$0.379$0.369better
pooledRPI$0.338$0.479better
pooledROAS0.891.30better
bodysuitpayer rate1.81%6.45%+257.0%0.041BETTER
bikinipayer rate7.94%3.23%−59.4%0.252not separable
pool5payer rate1.19%3.57%+200.0%0.341not separable

On matched hours the incumbent is flat to better. Every pooled term moves in the account's favour and none of them separates. The one result clearing 0.05 is bodysuit's payer rate, which rests on 3 payers against 8, is one significant result out of eight tests, and does not survive a Holm correction. It is not promoted here.

This matters because the whole-day picture in §1 shows ROAS falling on all three bases. Both are true: the day as a whole delivered worse than 08-02, and the hours in which the same creatives ran on both days did not. The difference is composition. The report day bought twelve hours of a brand new campaign the comparison day has no counterpart for, at rising CPM, with one broken hour in the middle of it.

The three measures side by side, with the unbounded cohort figure below them for reference only:

measure08-0208-03moves after the day closes?
booked$559.00 · 2.01$709.35 · 1.59settles a few hours after midnight, then fixed
within-day$272.13 · 0.98$397.69 · 0.89never
cohort @ 24h$360.61 · 1.30$474.05 · 1.06frozen once covered, 100% both days
cohort to date$542.23 · 1.95$524.63 · 1.18rises ~72 h, reference only, never compared across days

Booked revenue split by the UTC day its payer installed, whose top row is the within-day figure:

payer installed on08-02's booked08-03's booked
the day itself$272.13 (48.7%)$397.69 (56.1%)
the day before$239.67 (42.9%)$109.64 (15.5%)
two days before$47.20 (8.4%)$137.54 (19.4%)
three days before$58.74 (8.3%)
four days before$5.74 (0.8%)

Inherited revenue fell from 51.3% to 43.9% of the day's bookings. That is the entire gap between booked and within-day, and it is why a day whose spend is rising reads better on booked than on the measures that seal.

2026-08-05T10:30:27.809664 image/svg+xml Matplotlib v3.11.1, https://matplotlib.org/ booked within-day cohort @ 24h 0 100 200 300 400 500 600 700 USD 2.01 0.98 1.30 1.59 0.89 1.06 the number above each bar is that measure's ROAS against the same day's spend The three revenue measures, 2026-08-02 against 2026-08-03 (UTC) 2026-08-02 2026-08-03
The three revenue measures on both days, ROAS above each bar
2026-08-05T10:30:27.883889 image/svg+xml Matplotlib v3.11.1, https://matplotlib.org/ 2026-08-02 2026-08-03 0 100 200 300 400 500 600 700 USD the bottom segment IS the within-day figure: the same number seen twice Booked revenue by the day its payer installed (UTC) the day itself one day earlier two days earlier three days earlier four or more
Booked revenue by the day its payer installed

5Per ad set, hourly

Rows are labelled by asset. On the new campaign each ad set holds one ad holding one asset, so the ad-set row is the asset row; the incumbent's three creatives are three ads inside a single ad set, so those rows sit one level below it.

Full per-asset hourly grids are in _runs/2026-08-05_0804_settled/utc_2026-08-03/measures_out.txt. The shape that matters is when each asset delivered, because no two campaigns share a clock hour on this day:

assetdelivering hours (UTC)spend
earner/bodysuit04:00 – 11:00$88.59
earner/bikini, earner/pool505:00 – 11:00$48.16, $39.52
E2/bikini, E2/mugshot12:00 – 23:00$48.91, $54.49
E2/spill13:00 – 22:00 (99.6% of its impressions inside hour 14)$109.72
E2/bodysuit, E2/pool518:00 – 23:00$24.16, $32.28

The incumbent took 39.5% of the day's spend and 55.0% of its Meta-counted installs at a blended CPI of $0.419; the new campaign took 60.5% of spend and 45.0% of installs at $0.781. Held to within-day revenue the incumbent returned 1.35 and the new campaign 0.59; at cohort@24h, 1.51 against 0.77. Excluding spill, the new campaign's CPI is $0.588, still 40% above the incumbent's.

No per-asset day-over-day comparison is available from the new campaign, because it did not exist on 08-02. §4's matched-hour block is the only matched-clock read here and it covers the incumbent's three creatives alone.

2026-08-05T10:30:27.958518 image/svg+xml Matplotlib v3.11.1, https://matplotlib.org/ 0 2 4 6 8 10 12 14 16 18 20 22 hour (UTC) 5 10 15 20 CTR % CTR by hour — every asset, 2026-08-03 (UTC) E2/spill earner/bodysuit E2/mugshot E2/bikini earner/bikini earner/pool5 E2/pool5 E2/bodysuit
CTR by hour, one line per asset
2026-08-05T10:30:28.166182 image/svg+xml Matplotlib v3.11.1, https://matplotlib.org/ 0 2 4 6 8 10 12 14 16 18 20 22 hour (UTC) 0 10 20 30 40 50 60 installs / clicks % Click → install by hour — every asset, 2026-08-03 (UTC) E2/spill earner/bodysuit E2/mugshot E2/bikini earner/bikini earner/pool5 E2/pool5 E2/bodysuit
Click-to-install by hour, one line per asset
2026-08-05T10:30:28.264587 image/svg+xml Matplotlib v3.11.1, https://matplotlib.org/ 0 2 4 6 8 10 12 14 16 18 20 22 hour (UTC) 0.5 1.0 1.5 2.0 2.5 3.0 USD Cost per install by hour — every asset, 2026-08-03 (UTC) E2/spill earner/bodysuit E2/mugshot E2/bikini earner/bikini earner/pool5 E2/pool5 E2/bodysuit
Cost per install by hour, one line per asset
2026-08-05T10:30:28.052068 image/svg+xml Matplotlib v3.11.1, https://matplotlib.org/ 0 2 4 6 8 10 12 14 16 18 20 22 hour (UTC) 0 5 10 15 20 25 USD per 1,000 impressions CPM by hour — every asset, 2026-08-03 (UTC) E2/spill earner/bodysuit E2/mugshot E2/bikini earner/bikini earner/pool5 E2/pool5 E2/bodysuit
CPM by hour, one line per asset

6Per ad set × country

A META-day cut on spend and installs, like §3. Cells with spend over $1, ordered by spend; the full table is in _runs/2026-08-05_0804_settled/utc_2026-08-03/measures_out.txt. The head of it:

assetccspendinstCPIwithinwROAScohort@24cROAS
E2/spillIN23.6945.9230.000.000.000.00
E2/mugshotIN23.56191.2400.000.000.000.00
E2/bikiniIN21.93470.46720.830.9520.830.95
earner/bodysuitIN19.59500.39259.573.0481.874.18
E2/pool5IN9.52170.5600.000.000.000.00
E2/pool5US9.2761.5450.000.000.000.00
earner/bikiniIN9.12290.3145.760.635.760.63
E2/bodysuitUS8.8261.4700.000.000.000.00
E2/bodysuitIN8.19170.4820.000.005.760.70
earner/pool5IN6.39100.63911.521.8011.521.80

India is where the arms separate most visibly on cost. The same country bought installs at $0.392 for the incumbent's bodysuit and at $5.923 for spill and $1.240 for mugshot, a fifteenfold spread inside one geography.

India also holds the only three places where the same creative ran in both campaigns. bikini cost $0.314 for the incumbent and $0.467 for the new campaign, bodysuit $0.392 against $0.482, and pool5 $0.639 against $0.560. Two of the three favour the older ad set by 23% and 49%, which is consistent with a fresh-ad-set premium rather than with the creative; the third runs the other way by 12%, so the premium is a tendency on three pairs and not a measured rate.

2026-08-05T10:30:28.398124 image/svg+xml Matplotlib v3.11.1, https://matplotlib.org/ E2/spill E2/mugshot E2/bikini E2/pool5 earner/bodysuit E2/bodysuit earner/bikini earner/pool5 0 20 40 60 80 100 % of that asset's spend Where each asset bought — share of its own spend by region, 2026-08-03 (META day) IN US GB PH MX ID other
Where each asset bought: share of its own spend by region
2026-08-05T10:30:28.501184 image/svg+xml Matplotlib v3.11.1, https://matplotlib.org/ E2/spill E2/mugshot E2/bikini E2/pool5 earner/bodysuit E2/bodysuit earner/bikini earner/pool5 0 1 2 3 4 5 6 USD per install Cost per install by region — every asset, 2026-08-03 (META day) IN US GB PH MX ID
Cost per install by region, every asset
2026-08-05T10:30:28.592297 image/svg+xml Matplotlib v3.11.1, https://matplotlib.org/ E2/spill E2/mugshot E2/bikini E2/pool5 earner/bodysuit E2/bodysuit earner/bikini earner/pool5 0 10 20 30 40 50 60 USD Within-day revenue by region — every asset, 2026-08-03 (META day) IN US GB PH MX ID
Within-day revenue by region, every asset

The three region charts are drawn from the same country export as the table above, so they carry its META-day caveat and each says so on its own title.

7Per ad set, aggregate, all three measures

The three tables below are emitted by report_tables.py and carry the same columns, in the same order, as every other report in this series, which is what lets one day be read against another. They are cut on the 2026-08-05 10:59 UTC payment cut. within-day is sealed at midnight and booked settles a few hours after it, so neither can move; cohort@24h is 100% covered on this day and is therefore frozen too. Rows are labelled by asset, on the same basis as §5.

A. Per asset — delivery

Meta's instrument on both sides of every ratio. Our registrations never enter this table.

assetspendimpressionsCPMclicksCTRinstallsclick→installCPI
E2/spill$109.72182,6480.603,0301.66%732.41%$1.503
earner/bodysuit$88.5910,8258.188017.40%20825.97%$0.426
E2/mugshot$54.493,39416.052878.46%6924.04%$0.790
E2/bikini$48.914,37511.183748.55%10227.27%$0.480
earner/bikini$48.164,67010.314529.68%12828.32%$0.376
earner/pool5$39.523,69910.683679.92%8523.16%$0.465
E2/pool5$32.281,80917.8421511.89%5324.65%$0.609
E2/bodysuit$24.161,63314.791237.53%4839.02%$0.503

B. Per asset — the three revenue measures

Ours on every column. Meta can express only booked, so it rides bracketed beside that column and never as a row. Payers are distinct people, never transactions. Cohort horizon H=24h, 100% covered on this day. Report clock UTC.

assetbookedbROAS (Meta)within-daywROAScohort@HcROASpayerspayer%ARPUARPPUlargest payer
E2/spill$5.890.05 (0.11)$5.890.05$25.050.2311.3%0.0755.89100.0%
earner/bodysuit$340.673.85 (4.61)$127.821.44$156.951.77125.2%0.54910.6522.2%
E2/mugshot$10.700.20 (0.26)$10.700.20$16.600.3022.9%0.1575.3553.4%
E2/bikini$73.431.50 (1.65)$73.431.50$80.511.6598.6%0.6998.1623.0%
earner/bikini$159.563.31 (2.15)$86.261.79$86.261.7975.3%0.65812.3248.0%
earner/pool5$43.341.10 (1.12)$23.580.60$23.580.6044.5%0.2655.8926.8%
E2/pool5$44.601.38 (1.38)$44.601.38$44.601.3835.8%0.85814.8772.9%
E2/bodysuit$25.411.05 (1.05)$25.411.05$40.491.6812.1%0.52925.41100.0%

The 16-payer bar (#37): any row above whose payer count is under 16 is a direction and never a magnitude.

C. Per asset — country mix

This table is a META-day cut, not a UTC one. Meta serves no hour × country grid, so per-country delivery exists only on the ad account's own UTC-7 day and cannot be restitched. It is a seven-hour-offset window against tables A and B above; read it for mix, never for level against them.

Why a row in B is readable or not. The last column is the share of that asset's own within-day revenue that came from the United States: where it far exceeds the US spend share, the asset bought a geography rather than earning a result.

assetspendIndia %US %other %US % of its revenue
E2/spill$110.0521.5%4.5%74.0%0.0%
earner/bodysuit$57.1134.3%3.0%62.7%0.0%
E2/mugshot$78.3630.1%6.9%63.0%46.6%
E2/bikini$68.5632.0%8.5%59.5%0.0%
earner/bikini$38.3023.8%9.1%67.1%0.0%
earner/pool5$30.9520.6%6.8%72.6%0.0%
E2/pool5$64.6114.7%14.3%70.9%0.0%
E2/bodysuit$53.7715.2%16.4%68.4%0.0%

The incumbent's booked ROAS is inflated by inheritance and the new campaign's is not. The incumbent stopped buying at 11:20 UTC but collected payments all day from earlier install days; earner/bodysuit's booked 3.85 against within-day 1.44 is that gap. The new campaign's arms have no prior cohort at all, so their booked and within-day figures are identical by construction. Compare the arms on within-day or cohort, never on booked.

No row in table B clears the 16-payer bar. earner/bodysuit tops the day at 12 within-day payers, so the whole per-asset revenue ranking on this day is a set of directions.

2026-08-05T10:30:28.700533 image/svg+xml Matplotlib v3.11.1, https://matplotlib.org/ 08-01 08-02 08-03 08-04 day (UTC) 0 1 2 3 4 5 6 7 8 payers / installs % Payer rate by day — every asset (UTC) earner/bodysuit earner/bikini E2/bikini E2/bodysuit E2/pool5 E2/mugshot earner/pool5 E2/spill E2/spill2
Payer rate by day, one line per asset
2026-08-05T10:30:28.797322 image/svg+xml Matplotlib v3.11.1, https://matplotlib.org/ 08-01 08-02 08-03 08-04 day (UTC) 0.0 0.2 0.4 0.6 0.8 1.0 1.2 within-day USD per install ARPU by day — every asset (UTC) earner/bodysuit earner/bikini E2/bikini E2/bodysuit E2/pool5 E2/mugshot earner/pool5 E2/spill E2/spill2
ARPU by day, one line per asset
2026-08-05T10:30:28.890837 image/svg+xml Matplotlib v3.11.1, https://matplotlib.org/ 08-01 08-02 08-03 08-04 day (UTC) 0 5 10 15 20 25 within-day USD per payer ARPPU by day — every asset (UTC) earner/bodysuit earner/bikini E2/bikini E2/bodysuit E2/pool5 E2/mugshot earner/pool5 E2/spill E2/spill2
ARPPU by day, one line per asset
2026-08-05T10:30:29.061875 image/svg+xml Matplotlib v3.11.1, https://matplotlib.org/ 08-01 08-02 08-03 08-04 day (UTC) 0.0 0.5 1.0 1.5 2.0 revenue / spend Within-day ROAS by day — every asset (UTC) earner/bodysuit earner/bikini E2/bikini E2/bodysuit E2/pool5 E2/mugshot earner/pool5 E2/spill E2/spill2
Within-day ROAS by day, one line per asset

Money is plotted per day across the run rather than hourly, because payers run 0-2 per asset per hour and an hourly ARPPU is one person's basket rather than a rate. Hollow markers on the four charts above sit under the 16-payer bar, which on this day is every marker. Assets under 20 installs are left off the composite below and off §6's region charts; all eight assets on this day cleared that gate, so none of them is missing.

2026-08-05T10:30:29.175309 image/svg+xml Matplotlib v3.11.1, https://matplotlib.org/ CTR click→install CPI payer rate * ARPU * wROAS * 0.0 0.2 0.4 0.6 0.8 1.0 normalised across the day (1.0 = best of the pack) * axis sits under the 16-payer bar on most days: direction only, never a level. Assets under 20 installs on the day are not plotted. Composite — every asset across every measure, 2026-08-03 E2/spill earner/bodysuit E2/mugshot E2/bikini earner/bikini earner/pool5 E2/pool5 E2/bodysuit
Composite: every asset across every measure, normalised across the day

The regime-matched, mix-neutral cut

META clock. The five new arms sat in one regime, $100/day each from 08-03 05:44 to 08-04 00:01 PT, so they are budget-matched to each other by construction and directly comparable. mix-neutral re-prices each arm's ROAS on the regime's pooled country mix while keeping its own cost and revenue efficiencies, which removes the geography difference between arms. Cells are matured to H=6.

adregime$/dayinstspendCPMCTRIN%CPIRPIROASmix-neutral
bikiniE2-R110016069.811.328.82%34%0.4360.4901.121.08
bodysuitE2-R110011156.212.586.90%17%0.5060.4230.840.80
pool5E2-R110011166.714.889.93%15%0.6010.5991.000.81
mugshotE2-R11009580.916.978.64%22%0.8520.1790.210.22
spillE2-R110085111.10.611.67%7%1.3080.0690.050.08

Re-pricing on the pooled mix moves no arm by more than 0.19 of ROAS and reorders none of them, so the ranking is not a geography artifact. Cost per install separates the arms across a threefold range ($0.436 to $1.308); their ROAS does not, with the three leading arms sitting between 0.84 and 1.12 on a handful of payers each.

Install-cohort ARPU and ARPPU, at a fixed horizon

Install days on the UTC clock, installs and payers on our own records. All eight arms are 100% covered at H=24 on this day. The payer rate and ARPPU columns are to-date figures and grow with a cohort's age, so they describe this day and may not be read across days; the H=24 column is the one that is comparable.

arminstallspayerspayer rateARPPU (to date)ARPU @ H=24
earner/bodysuit233135.6%$12.890.674
earner/bikini13196.9%$10.950.658
earner/pool58955.6%$7.070.265
E2/bikini105109.5%$9.640.767
E2/bodysuit4836.2%$13.500.844
E2/pool55235.8%$14.870.858
E2/mugshot6834.4%$5.530.244
E2/spill7922.5%$12.530.317

The three incumbent creatives are the only ones with a prior day at the same horizon and the same coverage. At H=24, bodysuit moved 0.449 → 0.674 and bikini 0.407 → 0.658, while pool5 fell 0.499 → 0.265. Two of three up, one down, on 5 to 13 payers each. The new campaign's mugshot and spill sit at 0.244 and 0.317, below every other arm on the account, and they are the same two arms that sit last on cost per install.

8Caveats

9What would settle the open questions

10What this hands to the five-arm test

The five new ad sets became the arms of a randomised test when the budget-matched regime ended at 08-04 00:01 PT. This day is their observational baseline:

11Appendix — the clock, the measures, and regime

The report day is a UTC day. Meta stamps its exports in the ad account's own zone, UTC-7, so this day is Meta's 08-02 17:00–23:59 stitched onto Meta's 08-03 00:00–16:59, and every delivery figure above is restitched across those two account days.

Three revenue measures run through every table and are never mixed.

measurecountsmoves after the day closes?
bookedrevenue that arrived inside the UTC day, whatever day its payer installedsettles a few hours after midnight, then fixed. Meta's own number is this basis
within-daypeople who installed that day and paid before the day closednever. Sealed at midnight
cohort at Hthat day's installs counted only to H hours of each person's own age. House horizon H=24, where both days are 100% coveredrises until every install has lived H hours

All three are read on our own payment records; Meta's figure appears bracketed beside the booked column and never as a row of its own.

The instrument gate runs on the META clock, because Meta's conversion value sits on its conversion hour and is the one column that cannot be honestly restitched into a UTC day. Held there, our records read 0.98 of Meta's booked value on 08-03 and 0.91 on 08-02, both inside the documented 0.91–1.06 band. The bracketed Meta figures in §1's table are shown for traceability, and the ratio between them is not a valid check on this clock.

A regime is a stretch over which an ad set's daily budget did not change. It matters because on this account the budget is a geography dial: raising it buys wider, cheaper countries, so two creatives measured across a budget change are compared on different audiences.

This day sat in two regimes that do not overlap in time. The incumbent campaign held a flat $500/day through 11:20 UTC, the same regime it held for the whole of 08-02, which is what makes the two days comparable at all. The new campaign then ran five ad sets at a flat $100/day each from 12:44 UTC to midnight, a regime with no counterpart on the comparison day. Nothing spans both, so no cross-regime comparison is attempted in this report.

12Reproduce

CFG=_runs/2026-08-05_0804_settled/run_config.json          # tz = UTC
MET=_runs/2026-08-05_0804_settled/run_config_meta.json     # tz = META

# 1 — the three revenue measures, at campaign / hourly / country / asset
PYTHONIOENCODING=utf-8 python -m ad_ops.measures --config $CFG --day 2026-08-03 --prev 2026-08-02

# 1b — the instrument gate, which is only valid on Meta's clock
PYTHONIOENCODING=utf-8 python -m ad_ops.measures --config $MET --day 2026-08-03 --prev 2026-08-02

# 2 — matched clock hours at a fixed horizon, with two-proportion tests (META clock)
PYTHONIOENCODING=utf-8 python -m ad_ops.day_compare --config $MET \
  --today 2026-08-03 --prev 2026-08-02 --h 6 \
  --ads "ww_bikini_10s,ww_bodysuit_10s,ww_pool5_15s" \
  --campaign "bailingxia_meituan_ww_cvr_260730"

# 3 — regimes, horizons, delivery overlap, the decidability gate (META clock)
PYTHONIOENCODING=utf-8 python -m ad_ops.asset_estimator hourly \
  --meta _runs/2026-08-04_e2_start_and_0803_report/exports/ads_hourly_aug3_settled.csv \
         _runs/2026-08-05_0804_settled/exports/ads_hourly_aug4_settled.csv \
  --lum _runs/2026-08-05_0804_settled/lum \
  --fx _runs/2026-08-02_today_read/fx_rates.json \
  --ads "ww_bikini_10s,ww_bodysuit_10s,ww_pool5,ww_mugshot,ww_spill" \
  --campaign "bailingxia_meituan_ww_cvr_260802" --horizon 6

# 4 — install-cohort ARPU and ARPPU, to date and at fixed horizons
PYTHONIOENCODING=utf-8 python -m ad_ops.cohort_arpu --config $CFG \
  --days 2026-07-31,2026-08-01,2026-08-02,2026-08-03

# 5 — §7's three standing tables, §2's matched-hours pairing, §4's pooled funnel,
#     and the nineteen standing charts
PYTHONIOENCODING=utf-8 python -m ad_ops.report_tables --config $CFG \
  --day 2026-08-03 --prev 2026-08-02
PYTHONIOENCODING=utf-8 python -m ad_ops.report_charts --config $CFG \
  --day 2026-08-03 --prev 2026-08-02 \
  --days 2026-08-01,2026-08-02,2026-08-03 --out ad_ops/figures/2026-08-03 \
  --md _runs/2026-08-05_0804_settled/chartblock_2026-08-03.md --relpath figures/2026-08-03

report_tables --prev emits §2's matched-hours pairing and §4's pooled funnel as well as §7's three standing tables. report_charts writes all nineteen charts and a per-section block of image links.

The report clock is UTC and the ad account's is UTC-7, so a UTC pull needs one export more than a Meta-day pull: the previous Meta day's hourly file, every time. This day is stitched from ads_hourly_aug2_settled.csv hours 17–23 and ads_hourly_aug3_settled.csv hours 00–16, and the comparison day from ads_hourly_aug1_settled.csv hours 17–23 and ads_hourly_aug2_settled.csv hours 00–16. The estimator additionally loads ads_hourly_aug4_settled.csv so that post-midnight installs are not carried back into the last delivering cell of 08-03.

Meta exports were pulled after each day closed and settled, and each is verified by its own content, row count and the window read off Reporting starts / Reporting ends, never by filename. Country data comes from ads_country_day_jul1_aug5.csv, which is per Meta day and is the reason §3, §6, table C and the region charts cannot follow the report clock.

Payment records: Telegram exports of both notification channels, parsed with ad_ops/lum_notifications/parse_export.py. Payment data cut 2026-08-05 10:59 UTC, at which point both days' cohorts are 100% covered at H=24; ages at the cut run 35.0–58.1 h on 08-03 and 61.4–82.9 h on 08-02.

Selection is by Ad ID throughout, never by ad or ad-set name. The two campaigns run creatives with byte-identical names and a name filter over-attributes by about 70%.

Internal — noindex. Not for distribution outside the team.