diff --git a/services/experimentAnalysis.ts b/services/experimentAnalysis.ts index 3ad6918..2c4f071 100644 --- a/services/experimentAnalysis.ts +++ b/services/experimentAnalysis.ts @@ -149,6 +149,31 @@ function splitPeriods( function avgMetric(data: ComputedWeeklyMetrics[], metric: string, durationWeeks: number): number { if (data.length === 0 || durationWeeks <= 0) return 0; + + // For rates and ratios, we must sum the raw absolute components across all included weeks + // and THEN calculate the ratio, otherwise averaging percentages yields mathematically incorrect results. + if (metric === 'cvr') { + const totalUnits = data.reduce((s, w) => s + w.units, 0); + const totalSessions = data.reduce((s, w) => s + w.sessions, 0); + return totalSessions > 0 ? (totalUnits / totalSessions) * 100 : 0; + } + if (metric === 'ctr') { + const totalClicks = data.reduce((s, w) => s + w.clicks, 0); + const totalImpressions = data.reduce((s, w) => s + w.impressions, 0); + return totalImpressions > 0 ? (totalClicks / totalImpressions) * 100 : 0; + } + if (metric === 'roas') { + const totalRevenue = data.reduce((s, w) => s + w.revenue, 0); + const totalCost = data.reduce((s, w) => s + w.cost, 0); + return totalCost > 0 ? totalRevenue / totalCost : 0; + } + if (metric === 'acos') { + const totalRevenue = data.reduce((s, w) => s + w.revenue, 0); + const totalCost = data.reduce((s, w) => s + w.cost, 0); + return totalRevenue > 0 ? (totalCost / totalRevenue) * 100 : 0; + } + + // For absolute quantities (units, revenue, sessions), we sum them and divide by the duration const sum = data.reduce((s, w) => s + getMetricValue(w, metric), 0); return sum / durationWeeks; }