Robinhood · Data Analytics
Marketing Channel Performance
The work
What they wrote about it
Robinhood Marketing Channel Performance - H1 2025
Analyze six months of paid marketing against free account signups and Robinhood Gold subscriptions, then recommend an H2 2025 budget. This is an educational analysis of the supplied challenge data.
Recommendation: prioritize brand search and retargeting, which return $1.91 and $1.34 in first-90-day revenue per dollar of ad spend. Their combined proposed allocation is about 61%. Reduce generic search and prospecting video, and keep display paused unless a redesigned, capped experiment is approved. ROAS measures revenue efficiency; it does not include operating costs or prove profitability.
Reproduce the analysis
Requires Python 3.11+.
python -m pip install -r requirements.txt
python main.py
python -m unittest discover -s tests -vRun these commands from the repository folder. Paths resolve relative to the project, and running the script recreates the result tables, nine charts, and this README. No credentials or external data services are required.
Data validation and methodology
Datasets/ad_spend.csv: 1,046 raw records; 7 repeated date/channel/campaign records removed, leaving 1,039. Raw spend is $125,806.46; cleaned spend is $124,886.22. All metrics use cleaned data.Datasets/signups.csv: 18,840 users with unique IDs. Both files cover January 1 through June 30, 2025.- Normalize channel names by stripping whitespace, lowercasing, and replacing spaces with underscores. Match the channel/campaign pairs, rather than campaign names alone, before joining.
- The six paid campaigns match across files. Reject unmatched pairs, duplicate user IDs, invalid dates, missing or negative ad metrics/revenue, and payments before signup.
- Empty campaigns belong to organic/referral users. Empty payment dates indicate users who have not paid; missing campaigns are represented as
nonefor aggregation. - A paying subscriber has a nonempty
subscription_start_date. This counts observed payments, not specifically conversions within 90 days. ROAS uses the supplied first-90-day revenue field. - Aggregate spend and outcomes independently, then join on normalized channel and campaign. Channel ratios use summed numerators and denominators, not averages of campaign ratios.
| Metric | Formula |
|---|---|
| CTR | clicks / impressions |
| CPC | spend / clicks |
| CPA (cost per signup) | spend / signups |
| CPP (cost per paying subscriber) | spend / paying subscribers |
| Signup-to-paying conversion | paying subscribers / signups |
| 90-day ROAS | first-90-day revenue / spend |
Undefined ratios with zero denominators remain blank rather than being treated as zero cost. Organic/referral acquisition costs are unavailable, so their CPA and ROAS are not estimated.
Campaign performance
| Campaign | Spend | Signups | Paying | CPA | CPP | ROAS | Conversion |
|---|---|---|---|---|---|---|---|
| brand_search | $10,196.99 | 2,177 | 465 | $4.68 | $21.93 | 1.91x | 21.36% |
| retargeting | $12,987.22 | 2,465 | 418 | $5.27 | $31.07 | 1.34x | 16.96% |
| generic_search | $35,939.18 | 2,008 | 432 | $17.90 | $83.19 | 0.47x | 21.51% |
| finance_blogs | $15,392.44 | 864 | 162 | $17.82 | $95.02 | 0.41x | 18.75% |
| prospecting_video | $33,915.73 | 6,484 | 213 | $5.23 | $159.23 | 0.26x | 3.29% |
| display_network | $16,454.66 | 268 | 8 | $61.40 | $2,056.83 | 0.03x | 2.99% |
| Campaign | Impressions | Clicks | CTR | CPC |
|---|---|---|---|---|
| brand_search | 267,763 | 12,089 | 4.51% | $0.84 |
| retargeting | 1,084,880 | 17,245 | 1.59% | $0.75 |
| generic_search | 898,188 | 22,153 | 2.47% | $1.62 |
| finance_blogs | 706,611 | 7,752 | 1.10% | $1.99 |
| prospecting_video | 5,662,300 | 68,085 | 1.20% | $0.50 |
| display_network | 6,605,031 | 7,844 | 0.12% | $2.10 |
Paid campaigns generated 14,266 signups, 1,698 paying subscribers, and $69,443.91 in 90-day revenue on $124,886.22 spend: approximately 0.56x aggregate ROAS. Only brand search and retargeting have 90-day revenue above their ad spend.
Full metrics: campaign table, ad metrics.
Paid channel performance
| Channel | Spend | Signups | Paying | CPA | CPP | ROAS | Conversion |
|---|---|---|---|---|---|---|---|
| paid_search | $46,136.17 | 4,185 | 897 | $11.02 | $51.43 | 0.79x | 21.43% |
| paid_social | $46,902.95 | 8,949 | 631 | $5.24 | $74.33 | 0.56x | 7.05% |
| affiliate | $15,392.44 | 864 | 162 | $17.82 | $95.02 | 0.41x | 18.75% |
| display | $16,454.66 | 268 | 8 | $61.40 | $2,056.83 | 0.03x | 2.99% |
Search leads at the channel level, but its aggregate ROAS remains below 1 because generic search consumes much more budget than brand search. Social similarly combines strong retargeting with low-converting prospecting video. Allocate within channels at campaign level to avoid hiding these differences.
The complete paid-channel table also includes impressions, clicks, CTR, CPC, and revenue.
Paid versus unpaid signups
| Source | Signups | Paying | Conversion | 90-day revenue/user |
|---|---|---|---|---|
| referral | 1,063 | 212 | 19.94% | $8.91 |
| paid | 14,266 | 1,698 | 11.90% | $4.87 |
| organic | 3,511 | 345 | 9.83% | $4.12 |
Referral converts at about 1.68 times the combined paid rate and generates the highest revenue per signup. It outperforms aggregate paid traffic, but paid search converts slightly better than referral. Explore referral incentives separately; no referral costs are supplied, so these results do not establish that referrals are free or have better ROI.
Detailed channel comparison | Source comparison
Six-month performance
- Brand search: monthly CPA ranges from $4.36 to $5.00; monthly ROAS ranges from 1.74x to 2.02x. It remains above 1x in every month.
- Retargeting: monthly ROAS remains above 1x, but falls from 1.41x in January to 1.20x in June. June CPA rises to $5.69 from $4.97 in May; monitor before increasing spend.
- Generic search: CPA rises from $17.50 in January to $18.88 in June, with a $19.83 peak in May. ROAS is below 1x every month.
- Prospecting video: CPA improves from $5.44 to $4.99, while CPP improves from $226.51 to $148.14. June ROAS is still only 0.29x, so cheaper signups alone do not justify scaling.
- Finance blogs: monthly ROAS ranges from 0.35x to 0.50x, below ad-spend recovery throughout the period.
- Display: the last recorded ad date is May 14, 2025; there are no June ad records. Its May spend reflects a partial month. The data show cessation, but do not establish why it stopped. March and May have no paying subscribers, so CPP is undefined.
Monthly outcomes are grouped by signup month. Monthly revenue is each cohort's supplied 90-day revenue, not cash collected during that calendar month. The monthly table is anchored on observed spend; absent campaign months are listed separately in the activity table.
Monthly CPA, CPP, conversion and ROAS | Campaign activity and last ad dates
Which metric should guide the decision?
Use 90-day ROAS as the primary metric, supported by CPP and signup-to-paying conversion. ROAS compares attributed revenue with spend; CPP identifies how efficiently each campaign attracts paying users. CTR and CPC diagnose ad engagement and auction cost, but do not measure downstream value.
| Campaign | CPA rank | CPP rank | ROAS rank | CTR rank | CPC rank |
|---|---|---|---|---|---|
| brand_search | 1 | 1 | 1 | 1 | 3 |
| retargeting | 3 | 2 | 2 | 3 | 2 |
| generic_search | 5 | 3 | 3 | 2 | 4 |
| finance_blogs | 4 | 4 | 4 | 5 | 5 |
| prospecting_video | 2 | 5 | 5 | 4 | 1 |
| display_network | 6 | 6 | 6 | 6 | 6 |
The cheapest campaign is brand search for both CPA and CPP. The grading checklist's suggestion that the winner must differ is not supported by these data. The useful distinction is that the overall ranking changes: prospecting video is second by CPA but fifth by CPP, while retargeting moves from third by CPA to second by CPP. Prospecting video converts only 3.29% of signups, compared with retargeting's 16.96%. Optimizing CPA alone would favor low-cost signups without considering subscriber quality.
H2 2025 budget recommendation
Assume the next six-month budget equals cleaned H1 spend: $124,886.22. Reserve 5% per campaign, then distribute the remaining 70% in proportion to historical campaign ROAS:
allocation_i = 5% + 70% * (campaign_ROAS_i / sum_of_campaign_ROAS)This is a transparent starting allocation, not a forecast or an optimized response model. The 5% reserve is a deliberate exploration policy, not evidence that every campaign deserves continued spending. Percentages total 100% before rounding, and every allocation meets the minimum.
| Campaign | H1 actual | H2 allocation | H2 budget |
|---|---|---|---|
| brand_search | 8.17% | 35.22% | $43,983.20 |
| retargeting | 10.40% | 26.22% | $32,742.04 |
| generic_search | 28.78% | 12.46% | $15,560.80 |
| finance_blogs | 12.33% | 11.49% | $14,354.93 |
| prospecting_video | 27.16% | 9.09% | $11,352.46 |
| display_network | 13.18% | 5.52% | $6,892.80 |
Aggregated channel split:
| Channel | H2 allocation | H2 budget |
|---|---|---|
| affiliate | 11.49% | $14,354.93 |
| display | 5.52% | $6,892.80 |
| paid_search | 47.68% | $59,544.00 |
| paid_social | 35.31% | $44,094.50 |
- Brand search: increase its share, but release funds gradually and test incrementality because branded demand is finite.
- Retargeting: scale with audience-size and frequency limits; monitor the softer June performance.
- Generic search: reduce broad spending and test tighter keyword intent, bids, and landing pages.
- Finance blogs: negotiate rates and test partner-level quality before expanding.
- Prospecting video: reduce volume buying; test creative and audiences against paying conversion and ROAS.
- Display: keep the existing campaign paused. Its allocation is a capped reserve for a redesigned test, released only with a measurable hypothesis and stop rule; otherwise hold it unspent pending the next review.
The table allocates available funds; conditional reserves may remain unspent. Referral experiments require a separately costed proposal rather than assuming zero-cost acquisition.
Exact campaign budget | Exact channel budget
Risks and controls
| Risk | Implication and response |
|---|---|
| Attribution and incrementality | Credited signups are not necessarily caused by the ads. Brand search may capture organic demand, and upper-funnel ads may assist other channels. Use holdouts before committing the full increase. |
| Saturation | Historical average ROAS will not necessarily hold at higher spend. Increase budgets in stages; inspect marginal CPA, CPP, and ROAS. |
| Retargeting dependency | Retargeting needs an eligible audience, potentially supplied by prospecting. Preserve controlled acquisition tests and monitor audience size/frequency. |
| Revenue window and maturity | 90-day revenue understates lifetime value. Confirm all cohorts have complete observation windows; compare matured cohorts before acting on recent performance. |
| Revenue versus profit | ROAS excludes product, servicing, incentive, and other costs. Add contribution margins and a business-specific hurdle before calling a campaign profitable. |
| Seasonality and concentration | H1 market conditions may differ in H2, and about 61% is allocated to two campaigns. Review allocations monthly and keep exploration funds available. |
| Small samples | Display has only eight paying users; its results are noisy. Any restart should be a capped experiment with predefined stop rules. |
Review spend and acquisition metrics weekly and mature-cohort revenue monthly. As a proposed operational guardrail, investigate if brand-search CPA exceeds 130% of its H1 baseline (about $6.09). Pause further increases until CPP and mature-cohort ROAS support them. Do not automatically transfer funds to a historically weak campaign.
Repository contents
main.py Reproducible analysis entry point
src/config.py Paths and plotting style
src/data_loader.py Name alignment, validation and deduplication
src/metrics.py Campaign and paid-channel metrics; rankings
src/comparison.py Paid, organic and referral comparisons
src/trends.py Monthly metrics and campaign activity
src/visualizations.py Nine chart functions
src/report.py README generation from calculated results
src/eda.py Optional deeper exploration; not part of default run
Datasets/ Two supplied CSV inputs
outputs/tables/ Campaign/channel metrics, comparisons, trends and budgets
outputs/figures/ Nine PNG figures
tests/ Pipeline regression checksSubmission
The scripts, input data, result tables, charts, and written recommendation form the local deliverable. Publish this folder to a public GitHub repository, verify its URL works when signed out, and submit that URL in the challenge's Solutions tab. Public repository availability is a separate submission step; it cannot be established from these local files.
The brief this answers
Compare six months of ad spend against signups and paying customers, then recommend where the next budget should go.
Try it yourselfOther answers to this brief
The same problem, solved by somebody else.
Discussion
Nothing here yet. Ask how they approached it, or tell them what you would have done differently.