Two products. One funnel.
TraderDaddy shows what the smart money is doing in real time โ sweeps, blocks, golden sweeps, decoded by AI. TickerTrace is the daily ETF positioning layer underneath: what 50+ institutional funds are actually buying, normalized and free.
Both products run on the same open API. No signup required to see today's data.
Speed of information. By the time the public sees institutional positioning, the trade is months old.
13F filings. Updated quarterly, published with a 45-day delay. By the time you see it, the trade is 90+ days old and the move already happened.
Institutional ETFs publish full holdings every market day. TickerTrace scrapes + normalizes; TraderDaddy layers on real-time options flow.
Two layers, one workflow. The free data layer feeds the paid execution layer.
Real-time options flow detection. Sweeps, blocks, golden sweeps tracked as they hit the tape โ with institutional conviction scoring and an AI coach that translates each signal into plain English.
Daily ETF holdings, normalized across providers. 50+ institutional funds tracked, every position every day, with cross-fund conviction scoring and divergence detection. No login, no paywall.
The free data layer hands you everything below. The paid execution layer turns it into trades.
Signals weighted by fund AUM. A $6.8B ARK buy ranks higher than a $50M fund's identical move.
Know when an institution has been accumulating a position for 3, 5, or 10 consecutive days.
Visual grid: tickers ร funds. Color intensity = weight delta. See the whole market in one glance.
Look up any stock โ see every fund holding it, their weights, recent changes, and options exposure.
Flagged when funds within the same family take opposite positions. That's rare. That's a signal.
When 3+ independent fund families each open the same brand-new position within days, that's smart money quietly agreeing. The entry order is the part quarterly 13Fs can never show you.
We translate covered calls and cash-secured puts into plain English โ so you know what the fund actually thinks.
Other tools charge $19โ$500/mo for ETF data. TickerTrace gives it away free โ and TraderDaddy turns it into trades. This compares the free data layer to what else is out there.
| Feature | TickerTrace | 13F Filings | ETF Research Center | Cathie's ARK Tracker |
|---|---|---|---|---|
| Update frequency | Daily | Quarterly | Daily | Daily |
| Data delay | Same day | 90+ days | Same day | Same day |
| Cross-fund coverage | 50+ funds, 10 providers | All 13F filers | Broad | ARK only (6 ETFs) |
| Conviction scoring | โ | โ | โ | โ |
| Streak tracking | โ | โ | โ | โ |
| Option flow decoded | โ | โ | โ | โ |
| Divergence alerts | โ | โ | โ | โ |
| Layering Radar | โ | โ | โ | โ |
| Discord alerts | โ | โ | โ | โ |
| Activity heatmap | โ | โ | โ | โ |
| JSON API | โ | โ | $29/mo | โ |
| Price | 100% Free | Free | $29/mo | Free |
Pair the free daily holdings feed with TraderDaddy's live options flow. Same edge institutions pay six figures for โ minus the prime brokerage.
Referral link to TraderDaddy disclosed. We use this stack ourselves โ we built TickerTrace to feed it.
We ship constantly and document it honestly. Here's what landed recently.
Two days ago I added counts to the sector pills ('Technology (47)') and provider pills ('ARK Invest (12)') on the Stocks page. Somehow the signal pills were left out. They've had the same problem all along: clicking 'โ Net buying' or '๐ฅ Has streak' felt like a leap of faith โ you had no idea if you'd get 40 results or 2. Now they show counts the same way the other pills do: 'โ Net buying (23)' means 23 tickers are being net-bought today. The counts are context-aware โ if you've already filtered to Avantis + Technology, the signal pill counts only reflect stocks within that slice. All three filter rows now behave consistently.
When I fixed the signals engine to use active weight on July 10, I noted the free-tier dashboard was 'next in line.' That's today. The bug was the same one: raw portfolio weight change looks like buying when a stock rallies and looks like selling when it drops, regardless of what the fund actually did. The Changes page Buys/Sells tabs, the 'Increased/Trimmed' sections on individual fund pages, and the per-fund 'ฮ today' column on stock detail pages were all using raw weight delta for direction. Now they all use active weight โ the same price-drift-removed measure the API has used since July 10. Practically speaking: a position that drifted up because the stock had a big day will no longer show as 'ADD'; a position a fund was quietly trimming into strength will no longer show green. If the buy/sell counts on the Changes page look a bit different than yesterday, that's why โ it's showing you what the managers actually chose to do.
Small one, but it was making me guess. The /stocks page has a row of sector pills (Technology, Healthcare, etc.) and a row of provider pills (ARK Invest, Avantis, etc.), and clicking one filters the list โ but until now there was no way to know if 'Industrials' had 5 stocks or 50 before you committed to the click. Now each pill shows the count right on the face: 'Technology (47)' or 'ARK Invest (63)'. The counts are context-aware too โ if you've already filtered to 'net buying' signals, the provider counts reflect only stocks being bought today, so 'ARK Invest (12)' means 12 ARK names are moving up right now, not 63 across the whole book. Three active filters and you can read the whole picture without clicking anything.
I have to be straight with you: the signal engine has been measuring the wrong thing, and it took a good question to make me actually check. Every 'buy' and 'sell' signal ran off change in portfolio weight. But a stock's weight moves when its price moves, even if the fund never touched a single share. If TSLA rips 10%, its weight goes up in every fund that holds it, and the old engine dutifully reported all of them as 'buying TSLA' โ when not one share changed hands. I measured it against a full day of real data: raw weight agreed with what funds actually traded about as often as a coin flip, and roughly 70% of the moves above our signal threshold had ZERO shares traded. Yesterday's board had ARKK as the top BUYER of AMD on a day it SOLD 6,338 shares, and HOOD as the biggest sell signal on the board driven by a fund that BOUGHT it. Embarrassing, and worth fixing properly. The new measure โ I'm calling it active weight โ compares each position to where it would've drifted on price alone and reports only the difference, which is the part the manager actually chose. It cancels price moves and fund inflows/outflows at the same time (a fund taking in new cash and spreading it across every holding is not 'buying' anything, and it no longer shows up that way). On real trades, direction accuracy went from 59% to 81%. Along the way I found CrowdStrike's 4:1 split was being read as a fund quadrupling its stake โ that's handled now too. Net effect: the top signals reshuffle a lot, conviction scores come down to earth, and what's left is closer to real institutional intent. The public API and MCP tools use it today; the free-tier dashboard math is next in line.
Owning this one. From July 2 to today, api.tickertrace.pro handed back July 2 data โ signals, changes, sector flow, all of it stale โ while the scraper ran green every single morning and committed fresh CSVs to the repo like clockwork. The site looked alive. It wasn't. Here's the dumb part: the live box keeps itself current by fast-forwarding to main every 15 minutes, and someone (me) left it parked on a documentation branch. A branch that has its own commits can't fast-forward from main โ it's not a bug, it's just git. So the sync script did exactly what we designed it to do: it refused to nuke what looked like local work, logged 'ff-only merge BLOCKED,' and bailed. Every 15 minutes. For eight days. Into a logfile nobody opens. The freshness canary caught it on day one and failed four weekdays straight โ straight into an inbox nobody reads. Two working alarms, zero humans. Fixed twice over: the box is back on main and serving current data, and sync_data.sh now checks it's actually on main before it tries anything. If it finds itself on a stray branch with a clean tree, it walks itself back to main and carries on. If there's real uncommitted work sitting there, it still refuses to touch it โ it just says so in a way that doesn't pretend everything's fine. Next on the list: making the canary yell somewhere we'll actually hear it.
The /stocks index has had a sector filter and a signal filter for a while โ but there was no way to answer the obvious question: what stocks does a specific fund family hold? You'd have to open the fund's profile page and scan the holdings table, one fund at a time. Now there's a provider filter row: click ARK Invest and you get only the tickers that appear across ARKK, ARKQ, ARKW, ARKG, ARKF, or ARKX. Click Avantis and you see the small-cap and large-cap value names they're accumulating. All 10 fund families are available. The filter stacks with everything else โ 'ARK Invest ยท Technology ยท Net buying' is three clicks and the URL updates, so you can bookmark or share the filtered view. Nothing changed in the data, just a cleaner way to ask a question that was always answerable, just tediously.
Kind of embarrassing that this lasted as long as it did. Every other data page in the app โ Stocks, Changes, Holdings, Layering, even individual stock detail pages โ has the site nav at the top so you can move around freely. Fund profile pages (/fund/ARKK, /fund/AVUV, etc.) had nothing โ just a lone 'Back to Dashboard' button, no matter how you got there. Clicked in from /funds? Back to Dashboard. Clicked in from a stock's holders table? Also Back to Dashboard. It was the only page that trapped you. Fixed: fund profiles now have the same nav bar as everything else, and the contextual back link now says 'Back to Funds' so it takes you to the fund directory instead of forcing a detour through the main dashboard.
The dashboard header has always shown Funds Tracked, Underlyings, and P/C Ratio โ three counts that don't change much day to day. I added a fourth: New Today, which counts how many (fund, ticker) pairs went from zero to holding something this session. It's a real activity gauge. A slow rebalancing day might show 3 or 4. A day when three fund families all pile into the same new name โ the kind of thing the Layering Radar catches โ might show 30+. The number is computed from the same change detection that drives the Signals board, so it's always consistent with what you see below it. The data was always there, just never surfaced at a glance.
'Institutional blended weight: 0.800%' is technically accurate and practically meaningless to most people. I kept looking at it and thinking 'okay but how much money is that?' So now it tells you: the stock detail page shows '~$890M est. exposure' (or '$1.2B' for the bigger names) right next to the weight figure. It's computed from each holding fund's reported weight times their known AUM, so it's an estimate โ the AUM figures lag a bit โ but it gives you the right order of magnitude. Knowing that Avantis has roughly $600M deployed in a single small-cap value stock is a different kind of signal than knowing its blended weight is 0.4%.
There's a difference between five ARK funds all holding the same name (one shop's call) and five funds from three different families each independently owning it (three separate stock-pickers agreeing). The current UI showed '5 funds' and left you to squint at the fund chips and mentally sort them by shop. Now there's a small purple '2 fam' or '3 fam' badge right next to the ticker whenever two or more distinct fund families hold a stock. Nothing changed in the underlying data โ it was always in there. This is just the first time the cross-family conviction signal is surfaced directly on the index. If you see '3 fam' next to a ticker you haven't looked at, that's worth clicking.
Small one, but it was quietly confusing. When you'd open /stocks or /funds, the header showed how many tickers/funds you were looking at โ but not WHEN. If the scrape ran this morning, you were looking at today's snapshot. If it ran yesterday, you were looking at yesterday's. Either way, the page said nothing about it. I've added the as-of date right in the subtitle on both pages โ same YYYY-MM-DD format the Changes page already uses. 'Stocks' now reads '2026-07-05 ยท 147 tickers ranked by most widely held' and 'Funds' reads '2026-07-05 ยท 34 funds ยท 10 providers ยท $37.2B combined AUM'. Nothing changed in the data โ you're just seeing when it's from. The date pulls from the API, so it's always accurate to whatever the latest scrape produced.
The /stocks index has had a sector filter for a while, but you couldn't answer the obvious other question: out of everything institutions hold, what's actually moving toward me today vs. away from me? Added a 'signal' filter row with four pills โ All, โ Net buying, โ Net selling, and ๐ฅ Has streak. 'Net buying' shows every ticker where institutional weight moved up today, net across all funds. 'Net selling' is the opposite. 'Has streak' narrows to anything with a 2+ day consecutive buying or selling streak โ which is the subset actually showing conviction, not just noise. All three filter cleanly with whatever sort and sector filter you already have active, so 'Healthcare, net buying, sorted by biggest move today' is three clicks. The URL updates, so you can bookmark or share the filtered view. The same empty-state message that tells you when sector filter finds nothing now also covers the signal filters.
We track two very different types of funds: active-equity funds (ARK, Avantis, Corgi, Sprott) that actually pick stocks, and option-income funds (YieldMax, Roundhill, REX, Kurv, NestYield) that sell options for yield. They've always been mixed together on the /funds page with no way to split them out โ so if you just wanted to see 'all the stock-picking funds sorted by AUM,' you were scrolling past 30+ income funds to find them. Added two category filter pills โ Active Equity and Option Income โ above the table, same pattern as the sector filter on /stocks. The sort works across both: you can do 'Active Equity, AโZ' or 'Option Income, Most holdings' and the URL updates cleanly so you can bookmark or share the filtered view. When a category is selected, the Type column also clears out since repeating it would just be redundant.
Every signal row, briefing item, divergence ticker, and active streak on the dashboard used to open an inline search popup when you clicked the ticker. The Changes page got this fix last week, the fund profile pages got it the day after โ somehow the main dashboard's own signals kept the old behavior through both rounds. Fixed: clicking NVDA in your Buying signals, Briefing top buys, or any active streak now goes straight to /stocks/NVDA โ the page with the conviction chart, blended-weight history, and full fund holder table. Same fix for Notable Options in the briefing, which now land on the underlying stock page instead of searching an option contract string. Also updated the '13F delay' example on the landing page โ it was still showing Q4 2025 โ Feb 2026, which in July 2026 reads like we stopped updating the site.
We shipped Layering Radar on June 9 and it's probably the most 'only-we-have-this' thing on the site. When 3+ independent fund families each open the same brand-new position within days of each other, we surface the pile-up โ with entry order โ before any quarterly 13F would even hint at it. It was in the feature cards, it was in the nav, but somehow it never made it into the comparison table at the bottom of the landing page. Fixed. Added it as its own row: TickerTrace โ, everything else โ. Because they can't do it โ you'd need daily scraped data and cross-fund tracking to even try.
Two small things today. First: on days with no divergences, the Divergences section on the dashboard used to just disappear โ no card, no context, just nothing where a section used to be. That's confusing: you don't know if divergences isn't a feature, the data's missing, or today's just quiet. Now it always shows, and when there's nothing to report you get 'No divergences today โ all tracked funds are aligned on direction.' Way less mysterious. Second: an internal data quality fix โ if a fund ticker wasn't in our provider lookup table (e.g. a newly added fund we haven't catalogued yet), the layering patterns endpoint was returning provider: 'Unknown' while every other endpoint correctly fell back to the fund ticker itself. Now they're consistent: unknown provider = fund ticker, everywhere.
The /stocks list has shown streak badges for a while (amber โฒ for buying, red โผ for selling), but if you clicked through to a stock's detail page โ /stocks/NVDA, say โ there was no streak anywhere. Just the signal label and the Day/Week/Month net flow numbers. Fixed: the detail page now shows a streak banner inside the Net flow card when there's a streak of 2+ days. The streak on the detail page is computed from the aggregate blended weight across all institutional funds (not the per-fund streak that powers the index badges), which is the right measure for the page you're on โ you're looking at the combined institutional view, so the streak should be too.
Added a fourth sort to the /stocks index: 'Longest streak.' Click it and the list reorders by the strongest multi-day institutional accumulation or distribution streak, biggest first. If NVDA has been bought by funds for 7 consecutive trading days straight, it floats to the top โ same streak engine that powers the existing badges, just now sortable. The streak badges are still there (amber โฒ for buying, red โผ for selling) so you can see the count without clicking. Before this you had to scroll past 150 rows hoping to spot the badges; now they're stacked right at the front. Works with the sector filter too, so 'longest-streak Healthcare names this week' is two clicks.
Yesterday we fixed ticker links on the Changes page to go to /stocks instead of the dashboard search popup. Turns out the fund profile pages had the same bug, in four separate spots: New Entrances, Total Exits, Position Sizing, and Top Holdings all linked to the inline dashboard search. So you'd click NVDA on ARKK's profile and get a popup โ no trend chart, no sparkline, no blended-weight history. Fixing one page and not the other is the kind of inconsistency that drives me crazy once I notice it. All four sections now route straight to /stocks/TICKER.
Small but it was bugging me. When you're looking at today's position changes and click a ticker like SMCI or NVDA, it used to bounce you over to the dashboard with a search query loaded โ you'd see the inline popup and miss the full trend chart, the blended-weight sparkline, and the fund holder table that live on the dedicated /stocks page. Now it goes straight there. For options, it links to the underlying stock instead (clicking 'TSLA 260718C00300000' takes you to /stocks/TSLA), which is what you actually want to see. No data changed, just the right destination for the right page.
The Signals Hero cards and the Briefing top-buys/top-sells rows now show a small sector label under each name โ Technology, Healthcare, Energy, whatever the data has. The sector field was already in the API, just never surfaced on the dashboard where it matters most. Now you can see at a glance whether today's institutional buying is concentrated in one sector before you go digging. Also fixed a long-standing inconsistency: Top Sells in the Briefing was missing the conviction score and streak badge that Top Buys has always shown. They're symmetric now.
Added small streak badges to the /stocks index. When any fund has been continuously buying a stock for 3+ consecutive trading days, you'll now see an amber 'โฒ Xd' badge right next to the ticker โ same row, no clicking. Selling streaks get a red 'โผ Xd' badge. The streak counts come from the same streak engine that already powers the fund detail pages and the dashboard signals โ this is just the first time it shows up on the index. The threshold is 3 days minimum; 2-day moves are too common to be interesting, and we don't want the list turning into a confetti cannon. Pairs well with the NEW badge from yesterday โ you can now see 'this is a fresh entry AND they've been adding to it for 5 days' all in one row.
When a fund opens a stock position for the first time today, there's now a small green NEW badge right next to the ticker on the /stocks page. Hover over it and you'll see which fund(s) just bought in. Before this you'd have to sort by 'Biggest ฮ today' and then click through each ticker to see if the weight move was a fresh entry or an existing position being added to โ two steps, and easy to miss entirely. Fresh entries are often the most interesting part of the daily data. Now they're surfaced immediately in the list. Nothing changed on the stock detail page โ this is purely the index getting smarter.
Two fixes to the individual stock pages (/stocks/TSLA, /stocks/NVDA, etc). First, the 'ฮ today' column in the holder table was showing values like '+0.031' with no unit โ when every other table on the site correctly shows '+0.031%'. That % was just missing. Easy fix, embarrassing oversight โ the same issue was caught on the main /stocks list page last week and I somehow missed that the detail page had the same bug. Second, added a Shares column to the holder table. Weight % tells you relative conviction (how much of the fund is in this stock), but shares tells you absolute commitment โ ARKK owning 1.2 million shares of TSLA vs. a fund holding 8,000 reads very differently even if the percentage is similar. The data was already there, just not shown. Both columns hide on small screens to keep things readable on a phone.
The /stocks index lists every ticker institutions hold, but if you wanted to see, say, what Healthcare names they're piling into this week, you had to scroll past 150 rows and squint at the name column. Now there's a row of sector pills right above the table โ click Technology (or Healthcare, or Energy, or whatever the data has today) and the list narrows instantly to just that slice. Works with all three sort options, so 'biggest-moving Healthcare names today' is two clicks. When you're looking at a filtered view the sector badge under each name disappears too, since it'd just repeat what you already know. Click All to get everything back.
Embarrassing one. If the API ever hiccupped while you were loading /funds, the page would cheerfully render '0 funds ยท 0 providers ยท $0.0B combined AUM' โ as if we'd quietly shut down. The /stocks page has had a proper 'couldn't reach the API, try refreshing' error card for a while. /funds just... didn't. Same 10-line fix, same pattern. Now both pages behave like adults when the network isn't cooperating.
The dashboard header has three stats: Funds Tracked, Underlyings, and P/C Ratio. P/C Ratio has always had a tooltip explaining what it means. Funds Tracked and Underlyings just sat there looking confident, expecting you to guess. Fixed: both now have hover tooltips. The Underlyings one in particular needed the note โ it's not 'unique stocks held across all portfolios,' it's specifically the set of stocks that option-income funds are writing covered calls and puts against today. Very different number, very different meaning. Also: the landing page provider list said '... Sprott ยท more' like we were being mysteriously cagey about the tenth provider. It's NicholasX. We track NicholasX. Added.
Added sector badges to the Name column in the Dashboard's Daily Activity table. The Changes page got them last week and it turns out they're just as useful there โ when you're scanning today's moves in the activity section and see AVUV added SMCI, you now see the 'Technology' tag under the name without having to click through or remember every ticker's sector. Options are excluded (the tag would just repeat the underlying you can already read from the ticker). Same tiny badge, same exact style as the Changes page, because it should have been consistent from the start.
Small one, but it was annoying me. When you're scanning through today's institutional moves on the Changes page, the table would show you AVUV added SMCI and you'd have to click through to figure out 'is that tech or industrial?' Now there's a tiny sector tag right under the company name โ Technology, Healthcare, Energy, whatever โ so you know what you're looking at without leaving the page. Options don't get a tag since their 'sector' is just the underlying, which you can already read from the ticker. Works across daily, weekly, and monthly views.
Two small fixes to the /stocks page that were bothering me. First: if the API ever goes quiet for a moment, the page was silently rendering a table with zero rows and the header '0 tickers ranked by...' โ which reads like we track nothing. Now it shows a real 'couldn\'t reach the API, try refreshing' message instead of an empty shell. Second: tickers that had no institutional move today were showing '0.000' in the ฮ today column โ every other table on the site shows 'โ' for no change, and this one was the odd one out. Fixed to match.
Small but important: the green pulsing 'DATA UPDATED' pill was cheerfully green ALL the time โ even if the scraper had been down for three days. It just showed the last date and moved on. Now it reads the actual data age and flips amber if we're more than one business day behind. So a normal weekend never trips it (Friday data is fine on Saturday and Sunday), but a real freeze โ the kind we lived through in early June โ turns it amber and tells you the last date we have. Same logic as the server-side freshness canary we run daily, just now surfaced right in the UI where you can see it without digging into GitHub Actions. The dashboard's 'LAST UPDATED' label got the same treatment, plus it now shows a human-readable date ('Jun 12, 2026') instead of a raw ISO string.
The Layering Radar has been live since early June. It's probably the most interesting thing on the site โ when three or more independent fund families each open the same brand-new stock position within days of each other, that's the kind of signal that a quarterly 13F will never catch in time. And it wasn't on the landing page. At all. Replaced the 'Discord Alerts' feature card (still works, just not the marquee pitch) with a proper Layering Radar card. Also added it to the Reddit share text because if someone's posting a writeup they should probably mention the best feature. Sometimes you ship a thing and then forget to tell anyone it exists.
After the ULTI rotation tracker I went looking at whether the YieldMax and Kurv funds rotate enough to be worth the same treatment. Honest answer: mostly no. KYLD and KQQQ (Kurv) hold a basically fixed basket of large-caps and just write options on them โ KQQQ kept the exact same 19 names start to finish, KYLD swapped maybe three in three months. Flat lines, nothing to watch, so I left them alone. ULTY (YieldMax) does rotate, but gently โ it shuffles a handful of high-IV names per quarter, not a whole-basket rebuild like ULTI. So ULTY's fund page now has its own rotation panel, but grouped by SECTOR instead of theme (it holds normal large-caps โ NVDA, CAT, AMZN, GOOGL โ not crypto miners and quantum lottery tickets). You can see it sits ~48% tech and has been quietly leaning into industrials lately. Same deal as before: it regenerates daily, and any name I haven't sorted yet just shows as 'Uncategorized' instead of breaking. Under the hood the whole thing got generalized so adding another fund later is a one-liner.
If you watch ULTI you already know it doesn't buy-and-hold anything โ it rebuilds its whole speculative basket constantly. Digging through it I found it had rotated nuclear/uranium names in during May and then dumped them for a fresh quantum sleeve by June, plus turned over half its book in a single week. You couldn't see any of that at a glance, so I built it: ULTI's fund page now has a Theme Rotation panel โ current theme mix (bitcoin miners, space, quantum, clean energy, semis, drones, rare earth), a week-by-week heatmap of how those weights drift, and a 'latest churn' row showing exactly which tickers rotated in (green) and out (red). It regenerates itself every morning off the fresh holdings, so it's always current. And before you ask โ if ULTI buys some brand-new name I haven't categorized yet, it just shows up as 'Uncategorized' instead of breaking anything. Nobody has to remember to do anything.
A user told me they've been liking ULTI and asked me to pull up its effectiveness grade to sanity-check the feeling. I went to do it andโฆ ULTI had no grade at all. Pulled the thread, and the whole scorecard needed work. Four fixes. (1) The grader only ever looked at the single most recent day โ so when one bad scrape dropped ULTI's option legs, the fund silently vanished from the analysis instead of showing a number. Now it falls back to the last day that actually has data and stamps an amber 'as of [date]' badge so you know it's not live. And I chased down WHY ULTI kept going dark: REX quietly started serving its options in a second CSV format some days (a name like 'CLSK US 06/12/26 C16' instead of the usual option ticker), and our parser only understood the old one โ so on those days ULTI's whole options book got silently dumped into the 'OTHER' bucket. Taught the parser the new format; ULTI's options come through on both now. (2) The 'personality profile' each fund gets graded against had drifted badly โ ULTI was literally labeled as a YieldMax fund (it's REX), and scored as if it sold slightly-OTM weekly covered calls when it actually sells AT-the-money 4-leg spreads. I pulled the real strikes and expirations across the last two months and re-tuned all nine option funds to what they genuinely do. (3) It was grading some funds off a SINGLE option because the strike data was missing for the rest โ one outlier, confident F. Now if too few positions have usable data it just says 'insufficient data' instead of inventing a grade. (4) It was blending a fund's weekly income calls together with its long-dated protective puts into one number and scoring it a zero โ now calls and puts get judged on their own cadence. Honest part: a few grades got WORSE after this, on purpose. The goal was to make the number mean something, not to flatter the funds. ULTI grades out a B now โ and that's a real B.
Honest update: I submitted TickerTrace to the Whop App Store and it got bounced. Two fair hits. First, the embedded app was hard-locked to dark mode and ignored whatever theme the Whop community was running โ so on a light-mode Whop it looked broken. Fixed: it now reads the host's theme and follows it, light or dark, no flash. Built out a real light palette while I was in there, since I'd genuinely never made one. Second, and the bigger note: they said a dashboard that just shows stock data isn't enough on its own โ it needs to actually help a creator run their community. So I added a Broadcast tab that only community admins see: it auto-builds the day's institutional brief (top buying/selling conviction, multi-day streaks), lets you add a note in your own voice, and pushes it to your whole community in one tap โ landing them straight on the live signals. It's a daily piece of content you don't have to write, and a daily reason for your members to come back. No new accounts, no database, no setup beyond flipping on one permission. Resubmitting now โ we'll see.
Two things that were bugging me. (1) The /stocks index had two sort options โ most widely held and most weight โ but no way to answer the obvious question: what's actually MOVING today? Added a third sort, 'Biggest ฮ today,' that reorders everything by the size of today's institutional weight shift, biggest absolute move first. If something got hit hard or loaded up on by the funds this morning, it floats right to the top. (2) While I was in there I noticed the ฮ today column was showing values like '+0.012' with no unit, sitting right next to the 'Total wt' column that correctly shows '+0.012%'. Same bug on the accumulation/distribution trend bars on the dashboard โ those little D/W/M values were also unitless. Fixed both: they now read '+0.012%' so you know it's a percentage-point weight shift, not a price change or anything else.
Went back through the new Layering Radar with fresh eyes now that it's live. Good news: it's solid โ the links work, the fund-family colors line up, the API and AI-agent hooks all behave. Two small things while I was in there: the 'strength' number now has a hover tooltip that actually tells you what goes into it (how many funds, how many separate families, combined AUM), so it's not just a mystery number anymore โ same treatment I gave the conviction score yesterday. And I gave the AI-agent version of the tool a 'limit' knob so an agent can ask for just the top few patterns instead of the whole list. Boring plumbing, but that's how it should be.
The conviction score is the most important number on the signals board and I'd never explained it โ it just sat there as 'conv 5.2' with no context. Hover over it now and you get a plain-English breakdown: it's (# of funds in the move) ร (weight % moved) ร (average fund AUM). Basically a dollar-weighted measure of how hard institutions are pushing in one direction. More funds, bigger moves, larger funds = higher score. While I was at it, the 'P/C Ratio' KPI at the top now has a hover tooltip too โ because 'P/C' reads as Price/Cost as easily as Put/Call, which is not great for a stat that's trying to show you directional positioning.
The comparison table said '14 ETFs, 6 providers.' That was accurate back in early March when ARK and Avantis were basically the whole show. Since then we've added Corgi Funds (16 ETFs on their own), a full YieldMax suite, Roundhill's weekly-pay lineup, two more REX single-stock funds, Sprott, NestYield, and NicholasX. Actual count today: 53 funds across 10 providers. The '14' number was sitting right next to the live dashboard KPI that says the real number, which made us look like we couldn't do basic arithmetic. Fixed. Also updated the provider list in the feature card to include Sprott, which had been quietly tracking away without a mention.
Embarrassingly obvious in hindsight: if you clicked a provider pill on the heatmap โ say, you clicked 'ARK' to narrow it down โ and ARK had no moves that day, the grid would blank out and tell you 'No activity to display. Data updates on weekday mornings.' Which reads like the site is broken or the data is stale, when really you just filtered it to zero and nothing suggested you try a different provider or clear the filter. Fixed it: when a provider filter is active and returns nothing, it now says 'No ARK activity today โ try All to see moves across every provider' (or whichever one you picked). Small thing, but the old message was actively misleading.
This is the feature I'm most excited about in a while, and it only exists because we scrape DAILY instead of waiting for quarterly 13F filings. 'Layering' is when three or more institutional stock-pickers each open a brand-new position in the SAME stock within a few days of each other โ independent shops quietly arriving at the same idea at the same time. The new Layering Radar (in the top nav) ranks these live, and the part I love: it shows you the ENTRY ORDER. Right now it's flagging GOOGL (ARK and NestYield both piled in within a week) and CRWV (three different fund families โ Avantis, Corgi, NestYield โ all opened it fresh). A 13F site literally cannot show you this; by the time those filings drop the move is 45+ days cold. Cross-family pile-ups rank highest, because three separate shops agreeing is a very different signal than one shop spreading a pick across its own lineup. It's also wired into the API (/api/v1/layering-patterns) and the MCP server, so your AI agent can pull it too. Early days on the scoring โ I'll tune it as we watch it run โ but the core idea is the most 'only-we-have-this' thing on the site.
After spending today untangling why the data silently froze, I wired in the thing that should've existed all along: a daily canary that pokes the live site, reads the date it's actually serving, and only makes noise if that date is genuinely stale (more than a business day behind). Quiet when everything's fine, loud the moment it's not โ so a freeze gets caught in hours, not a week. I also added a guard on my own end that runs a quick check before any code leaves the server, so a dumb typo can't break the build and spam failure emails again (which, full disclosure, I did to myself twice today before this existed). Boring plumbing, but it's the difference between 'the site is always right' and 'the site is right until it quietly isn't.'
REX is shutting down a batch of its Growth & Income single-stock funds โ trading halts today (June 9) and they liquidate June 16. Four of them were on our board: the MSTR one (MSII), the COIN one (COII), the HOOD one (HOII), and the PLTR one (PLTI). Tracking a fund that's about to stop existing just clutters the signals with noise, so I've pulled all four from the scraper and filtered them out of the dashboard. Two things worth saying: (1) the REX NVDA and TSLA funds (NVII, TSII) are NOT part of this liquidation, so they're staying. (2) ULTI โ REX's flagship high-income fund โ is totally unaffected and isn't going anywhere. I double-checked REX's actual liquidation notice before touching anything, because 'I think these are the ones' is how you accidentally delete a live fund. Their historical data is kept on disk, just hidden, so nothing's lost if any of this changes.
Okay, the full autopsy. The server that powers this site is supposed to pull each morning's fresh data automatically โ there's a scheduled job that's been running every single day to do exactly that. Problem: the little script that job runs had been accidentally deleted months ago during an unrelated cleanup, and because it lived only on the server and was never saved into the project, nothing flagged it. So every day the scheduler dutifully fired, looked for the script, found nothing, shrugged, and moved on. Silent. For a week the site served stale numbers while a 'successful' job ran on schedule doing absolutely nothing. I rewrote the script, and โ the important part โ I saved it into the project itself this time, so the next cleanup can't delete it. I also sped it up: instead of syncing once a day, the server now checks for fresh data every 15 minutes. So from now on, when the morning scrape finishes, you'll see the new numbers within the quarter hour, automatically, forever, with nobody touching anything. The thing that should've been true this whole time is finally true.
Yesterday I fixed how the live box gets fresh data. Today I found why this morning's scrape didn't produce any. The daily run has a little extra step that backtests our signals against real prices, and Yahoo's data library quietly changed its output shape โ even when you ask for one stock it now hands back a table instead of a single column, and our code choked on it, threw an error, and took the whole morning run down with it. The nasty part: that backtest runs BEFORE the step that commits and ships the day's holdings, so one hiccup in a nice-to-have chart was blocking the actual data everyone comes here for. Two fixes: patched the code to handle Yahoo's new shape, and โ more importantly โ demoted the backtest so it can never again block the data. If that chart breaks tomorrow, the holdings still ship and you'll never notice. Defense in depth, learned the hard way.
This one stung. The 'DATA UPDATED' date had been frozen for about a week, and I almost didn't believe it because the daily scrape never failed once โ green every single morning. Turns out the failure was on the other end. The scraper runs in GitHub Actions and commits each day's fresh holdings to the repo, but the live API box reads its own copy of those files, and that copy only ever moved forward when I manually deployed by hand. So the moment I stopped hand-deploying after the early-June feature push, production quietly froze on that day's data while the repo marched on without it. Fix: every scrape now finishes by syncing the box itself โ no human in the loop โ and it hard-resets to match the repo so the server's perpetually-messy working tree can't make the update silently bail like it used to. I also wired the deploy to ping the API's health check at the end and fail loud if anything's down, so the next time production drifts I hear about it that morning instead of a week later. Lesson filed under 'green doesn't mean working.'
Click any ticker โ on Stocks, the institutions board, or the trend overlay โ and you land on a real stock page: a chart of its AUM-blended institutional weight over the last 30 trading sessions (so you can watch a name get accumulated or bled out over weeks, not just today), the day/week/month net flow with the same accumulation/distribution signal, and the full who-holds-it table with each fund's move today. NVDA's line ramps; AMZN's rolls over. Two smaller things shipped alongside: the trend overlay now lives on the dashboard home too (not just Changes), and its Day/Week/Month toggle re-sorts each group by the horizon you pick โ tap 'Day' and the biggest movers of the session float to the top of each bucket. Also squashed a dumb bug where a stock's name sometimes showed an option contract string instead of the company name.
New overlay on the Changes page that answers the question you actually care about: is this a real trend or a one-day blip? For the biggest movers, I lay the institutional buying/selling over three horizons on the same axis โ Month (faint), Week, Day (bright) โ extending right for buying, left for selling. Three green bars marching right and you're looking at sustained accumulation (NVDA right now: the day bar is nearly as long as the whole month's, so most of the buying is happening NOW). The one I'm proud of: when a name's been accumulated all month but the bright Day bar suddenly flips red, it gets tagged 'Selling starting' โ that's distribution beginning, the thing you want to catch before everyone else does (HOOD tripped it today). There's also 'Bottoming?' for the reverse. It's the first real read on institutional support/resistance over time, and it's only going to get better as the history deepens. (Rows are grouped by signal so all the accumulators sit together, and there's an All/Day/Week/Month toggle that spotlights one horizon across every name when you want to scan a single timeframe.)
If you've used a 13F tracker, you reach for two pages on instinct: a list of every fund, and a list of the most-owned stocks. We didn't have either as a standalone page โ you had to know a ticker to look anything up. Now there's /funds (every fund we track, sortable by AUM / holdings count / AโZ, each row showing its top holding and whether it's an active-equity or option-income shop) and /stocks (every underlying ranked by how many funds hold it, with total weight across funds and today's net move โ NVDA's in 11 of them, for the record). Both are in the top nav next to Dashboard, and every row clicks through to the fund or ticker detail you already had. This is the navigation backbone the site was missing.
Two things people kept asking for. (1) There's now an 'Institutions as a whole' board โ I blend every stock-picking fund we track (Avantis, ARK, Corgi, Sprott, plus the BLOX and NestYield equity books) into one AUM-weighted portfolio and show you what that combined book is actually adding to and trimming, by day, week, or month. The pure option-income funds (YieldMax, Kurv, REX, Roundhill) are deliberately left out โ their stock holdings churn for the options overlay, that's not conviction. It's on the /changes page and it's now the first thing you see on the dashboard. (2) Navigation: one consistent top bar across every page (Dashboard, Changes, Holdings, Fund Scores, Scanner) instead of the ad-hoc 'Back to Dashboard' links, and the dashboard now leads with the institutional board + a tracked-funds grid you can click straight into. If you've ever used HedgeFollow, it should feel familiar โ minus the ads, because this is still free.
Housekeeping that was quietly wrong. (1) Avantis (AVUV/AVLV/AVMV) wasn't showing up under /changes โ turns out that page was reading a payload capped at the 50 biggest moves, and a value fund holding 800 names moves each one by a hair, so they never made the cut. The page now reads the full, uncapped change list โ and it gained a Day/Week/Month toggle, which is the right horizon for slow funds like these anyway. (2) Conviction Streaks were counting consecutive history *files*, not trading days โ so a stray weekend or holiday scrape (including one corrupt Saturday file with 4x the normal rows) could pad a '5-day streak' with days the market was closed, or break a real one. Streaks and the week/month windows now skip non-market days entirely. (3) Added TBILL to the junk-ticker filter so it stops masquerading as a real position. (4) The dashboard was auto-scrolling you a screen-and-a-half down to the 'Ask TickerTrace' box the instant it loaded โ the chat's scroll-to-bottom was firing on an empty conversation. Now it only scrolls once you've actually asked something, so the page opens where it should: on the leaderboard.
Yesterday's commit said "TickerTrace is now a Whop app" โ technically true, but it lived on a tickertrace-whop.vercel.app URL nobody could remember, had no icon, and was asking for a comically over-privileged scope list (Create apps, Manage OAuth, Manage webhooks โ all "Required", with the justification "Um because I'm making apps duh". Dev-tooling boilerplate that leaked in from my own scaffold.) Today: drew an actual TT monogram icon, pointed tt.mphinance.com at it via an Apache reverse-proxy on Vultr (the subdomain was sitting there from a tastytrade tool I haven't opened in months), and stripped every requested scope to zero โ a read-only ETF dashboard does not need to manage your webhooks. Install it into your Whop, click through Signals, watch your members actually use it. It's a real app now.
Spent the day porting the dashboard into a Whop app. Same data, same conviction scoring, same fund and ticker pages, just wrapped in Whop's iframe SDK so it slots into any community as a tab. Five top-level tabs (Signals, Briefing, Changes, Divergences, Sectors), plus full per-fund and per-ticker drill-downs. JWT verification on every page so members are identified through Whop's own auth, no separate signup. Free, no tier gate, no upsell โ pointed straight at the public api.tickertrace.pro so there's no second backend to maintain. Lives in whop-app/ in the repo. If you run a Whop and want to give your community a live read on what institutions are buying without making them leave Discord, this is the easy way.
Two cleanups from the design review. (1) The Divergences section โ where one fund is buying a name another fund is dumping โ now has a tug-of-war bar under each row: green for total buying pressure, red for total selling, sized to scale. A lopsided fight ('everyone's loading up, one fund is bailing') is now obvious without doing mental math on the percentages. (2) The CBOE Options Scanner had no way to narrow things down โ now there's a category toggle (Newly Optionable / Weekly ETFs / Weekly Equities) and a ticker search, so you can jump straight to what you care about instead of scrolling the whole timeline.
Option-income funds got two new panels and everyone got a flow stat. (1) The Option Strategy Map shows, per underlying, where the spot price sits versus the strike the fund wrote โ so you can see at a glance whether a covered call is safe (out of the money, fund keeps the premium) or about to get run over (in the money, shares called away). (2) An Option Rolls panel: when a fund closes one contract and opens another on the same name, that's a roll, not an exit, and it finally reads that way โ 'ADI C437.5 โ C415'. (3) Net Flow โ the change in shares outstanding over the past week, as a percent. Shares outstanding only move when a fund creates or redeems units, so it's a clean read on whether money is coming in or leaving. We show a percent on purpose: the share counts are trustworthy, the per-share prices our scraper pulls are not โ a bad price briefly had a $50M fund showing a $2.9B flow before we caught it. Net Flow appears wherever the provider reports shares outstanding (most option-income and Corgi funds); ARK and Avantis don't publish it, so it stays hidden there.
The big buying/selling matrix on the dashboard was eating most of the screen on a phone, and it wasn't obvious you could swipe it sideways. Trimmed its height on small screens so it's a panel and not a takeover, and added a little 'swipe for all funds โ' nudge. The ticker column stays pinned while you scroll, so you never lose which row you're on. Desktop is untouched.
Every fund page used to look identical, which never made sense. What you want from AVUV โ what has Avantis been quietly accumulating? โ is nothing like what you want from ULTY โ how is the option book positioned? So there are two layouts now, and each fund gets the right one automatically. Active-equity funds (Avantis, ARK, Corgi, Sprott) lead with a Daily/Weekly/Monthly toggle, a New Entrances / Total Exits scoreboard, and a conviction-streak tracker โ built to surface what's moving over a week or a month, not just today's noise. Option-income funds (YieldMax, Kurv, REX, Roundhill, the EGG funds) lead with the option book itself: contracts laid out as an expiration ladder, each tagged ITM / OTM / ATM so you can tell at a glance whether the fund's written calls are safe or about to get run over. Also quietly fixed: option open/close activity was being filtered out of the data entirely โ it's back, so you can actually watch contracts get opened and closed.
Click through to AVUV's profile lately and you got a dead page. Embarrassing, and entirely our fault. The site pre-builds every fund page ahead of time, and if our API so much as hiccuped during that build โ a timeout on AVUV's chunky 800-holding payload, a one-off blip on BLOX โ a 404 got baked into that page permanently, and it stayed broken until the next deploy. Fixed the whole class of bug: fund pages now build on demand instead of all at once, the API client retries a transient failure three times before giving up, and we finally tell a real 'this fund doesn't exist' apart from 'the API blipped for a second.' The first gets a proper branded page with a search box; the second gets a Try Again button and quietly heals itself on the next visit. No more tombstones.
Three changes, all aimed at making a fund's page tell you what matters. (1) The 'weekly' view was counting files, not days โ and since we don't scrape on a perfectly clean Mon-Fri rhythm (weekends sometimes sneak in, holidays leave gaps), 'a week ago' could drift anywhere from five days to nine. It's calendar-aware now: weekly compares against the snapshot closest to 7 days back, and monthly โ brand new โ against 30. For slower movers like Avantis and ARK, a month is the horizon where conviction actually shows up. (2) New positions and fully-closed ones now wear bright NEW / EXIT badges, with closed tickers struck through, so the real portfolio decisions stop hiding behind the daily +0.02% noise. (3) Options got honest labels โ OPENED / CLOSED / ADDED / TRIMMED instead of buy/sell language โ because an option closing is usually just an expiry or a roll, not a fund turning bearish. All of it is groundwork: every fund is now tagged either active-equity (the stock pickers โ Avantis, ARK, Corgi, Sprott) or option-income (the YieldMax / Kurv / REX / Roundhill yield machines), because those two very different kinds of fund are about to get two very different pages.
Borrowed this one from TraderDaddy and ported the whole thing to Python. Every weekday morning we pull two CSVs straight from CBOE โ the full Symbol Directory (~5,300 optionable stocks) and the Available Weeklys list โ and diff them against yesterday. Two things it catches: a stock getting options listed for the very first time (rare, and a real tell that liquidity is showing up), and a ticker getting promoted to โ or dropped from โ weekly expirations. The new /options-listings page shows a timeline of what changed when, running totals, and an MWF Elite box that lights up on Monday/Wednesday/Friday 0DTE days. It doesn't touch any fund holdings data โ it's pure CBOE market data, same for everyone. It's in the nav under 'Options'.
Open any options-income fund โ ULTY, KYLD, the EGG funds, the YieldMax and REX weeklies โ and there's a new Portfolio section on its profile page. The idea came from a sister project that tracks a personal options book; here we pointed the same layout at the fund itself. You get four metric cards (equity holdings, option contracts, equity weight, top-sector concentration), a sector-exposure bar built from the underlying equity holdings, and every option position rendered as a card โ labeled covered call or cash-secured put, with strike, weight, and days-to-expiry. Negative weight means a written/short contract. It's all computed from the daily holdings snapshot we already had โ no new data, just finally showing it properly. Plain-vanilla funds like AVUV and ARKK don't get the panel since they don't write options.
Every time I tried to share TickerTrace on Reddit, the spam filter ate it โ turns out Reddit auto-flags link posts to the same domain as advertising, and we'd wired the share button to do exactly that (a bare link drop). Fixed it properly: the Reddit button now opens a TEXT post with a real writeup pre-filled, including an honest 'full disclosure, it's my project' line โ which is the thing that actually keeps a subreddit from nuking it. Substance plus transparency reads as a contribution, not an ad. Same fix on the dashboard's share row. Also added a plain 'Copy link' button everywhere, because the one share method no algorithm can flag is the one where you paste it in yourself.
Friend with actual design taste sent over a critique; he was right on every count. Four changes: (1) Activity heatmap pivoted โ tickers are now ROWS, fund columns grouped by provider with a colored band header and thin separators between families. Two-tier sticky header (provider band + fund row), sticky ticker column, dense 16px cells with a real hover tooltip instead of the browser's slow native title. Click a ticker to jump to its cross-fund view; click a fund to open its profile. (2) Backtest section is now three proper KPI tiles with win-rate progress bars, not text-heavy paragraphs. (3) Every ticker in Top Buys, Top Sells, Multi-Provider, Streaks, Notable Options, the Buying/Selling hero list, AND divergences is now a real link โ click it and the cross-fund lookup populates instantly. (4) The Discord webhook configurator was hogging prime real estate right under the search bar โ moved into an 'Integrations & sharing' collapsible at the bottom of the page, alongside the X/Reddit share buttons. Config doesn't belong above the fold.
Bottom-left of every page now shows 'โ 7 live ยท 412 today ยท 2.8k/wk ยท 18k total' (or whatever the actual numbers are). It's a tiny FastAPI endpoint backed by a SQLite table โ fires a sendBeacon on each page load and polls /api/v1/visits/live every 30 seconds. Visitor identity is sha256(ip + salt)[:16] so no IPs are stored. Rolls a 30-day window for the rolling stats; lifetime counter persists. The Sam-voice version: turns out the most engaging analytics is the one you don't have to log into.
Provider dropdown for Gemini, Anthropic, OpenAI, or OpenRouter. Pick one, paste the matching key, ask away โ each provider's API gets called direct from your browser, no server in the middle, key stored only in localStorage. The settings panel shows a 'key saved' dot next to providers you've already configured so you can switch around without re-pasting. Model field is optional with a sensible default per provider, but you can override it (gpt-5-mini, claude-sonnet-4-5, google/gemini-2.5-flash on OpenRouter, whatever). All eight TickerTrace tools work with all four providers โ same tool definitions, three adapters that translate to each one's native function-calling format.
Turns out we already had a Google Analytics 4 property from the old Firebase setup โ it just wasn't wired to the site. Added @next/third-parties Google Analytics component reading NEXT_PUBLIC_GA_ID from env. Set the measurement ID in Vercel and we get real funnel data alongside Vercel's pageview counts. Two analytics tools talking past each other is fine; they're complementary.
Original version used Claude Sonnet through a server route with our API key. Cost-per-question would have been real money at scale, and Sonnet is overkill for 'who's buying GOOGL'. Pivoted: swapped to Gemini 2.5 Flash, ripped out the server route entirely, made the whole thing browser-side with a BYOK API key. Get a free key at aistudio.google.com, paste it in, your key never leaves your browser (literally stored in localStorage, we have no way to see it). The Anthropic SDK got deleted alongside the change.
Embarrassingly: until now we had no idea how many people were hitting this site, what pages they were bouncing on, or where they came from. Added Vercel Web Analytics (privacy-respecting, no cookies, free tier covers anything we're doing). One import in the layout, one component. Should have done this on day one.
New 'Ask TickerTrace' card on the dashboard. Powered by Claude with tool use, backed by 8 tools that wrap our own API: signals, changes, fund detail, ticker detail, sector flow, divergences, signal performance, fund list. Ask 'who's accumulating GOOGL', 'show me ARK's biggest moves', 'do these signals actually make money' โ get real answers with real numbers pulled from real holdings data. The reason most AI chat boxes on fintech sites are useless is that they have no proprietary data to ground on. We do. Requires ANTHROPIC_API_KEY in Vercel env to enable; gracefully shows an error if not configured.
Backtested every historical buy and sell signal against the underlying's price 30 days later. 23,365 graded signals so far. Headline: buying signals win 56.8% of the time with a +2.09% median return. Selling signals lose money (the underlying went UP +4.06% on average after a sell, win rate just 33.8%). Per-fund-family breakdown on the dashboard. New card right at the top because if our signals don't work, you should know. Cron regenerates this daily and the JSON ships as a static file โ the dashboard reads it directly, no API call needed. Options-based signals are excluded for v1 (their P&L depends on ฮ/ฮธ/ฯ, not just spot price).
The patch notes have said 'Stripe + Firebase ripped out' for weeks but the components, the AuthProvider wrapping the whole tree, and firebase as a dependency were all still riding along. Cleaned house: deleted auth-context / auth-modal / auth-button / pro-gate / lib/firebase / the entire shared-auth/ parallel implementation. Removed firebase from package.json (and 67 transitive packages with it). Side effect catch: the dashboard had been double-rendering signals (top-3 'free' slice followed by the full list inside a no-op ProGate). Build dropped from 49s to 19s. Net: -3,046 lines.
If you searched any stock that a Corgi fund holds, the result card was repeating the same fund up to 8 times with slightly different weights โ 6,294 duplicate rows across 619 (fund, ticker) keys, all from Corgi Funds. Turns out the Corgi JSON API returns the full historical time-series, not just today's snapshot, and the scraper was dumping every row into the daily CSV. Fixed at the source (scraper now collapses to the latest holding_date per fund/ticker before writing the CSV) and added a defensive dedup at the API read layer so existing dirty data displays correctly without waiting for tomorrow's scrape. Added regression tests so this exact shape of bug can't sneak in via a future provider.
The search bar was tucked into a cramped row with the Discord webhook and share buttons; first-time visitors had no idea you could look up any ticker. Promoted it to its own labeled row with a clearer purpose hint. The result card was also a dead-end โ you'd see CMAG and CQTM listed as holding GOOGL but had to copy-paste 'CMAG' into the URL to actually see what else CMAG holds. Now the fund badges in the result are clickable links to the fund's profile page.
The dashboard was pointed at api.tickertrace.mphinance.com โ which has no Apache vhost on Vultr, so requests fell through to a default vhost that serves the wrong TLS cert AND can't reverse-proxy to FastAPI. Result: 503s on every API call, and the build failing because it couldn't fetch fund metadata. The actual working domain has been api.tickertrace.pro all along (real vhost, valid cert, ProxyPass to localhost:8100). CLAUDE.md had been lying about it being the 'only working' domain. Flipped lib/api.ts, next.config.ts, fund-effectiveness.tsx, effectiveness/page.tsx, and the two landing/dashboard API doc links back to .pro. Local build now generates all 56 fund pages cleanly.
The dashboard and fund pages had empty-state fallbacks for null payloads, but the default apiFetch was throwing on network errors and non-2xx responses โ so the fallback never got a chance to render. Made apiFetch swallow network errors when throwOnError:false is set, threaded that option through dashboard's signals/activity calls, and wrapped fund/[ticker]'s generateStaticParams in try/catch. Builds now pass even if the API is hung, 503-ing, or unreachable. Pages render on demand once it's back.
The README was last touched March 2 and was lying about basically everything: said we tracked 15 funds (it's 56), said the API needed an API key (it's been fully open since v2), said the data layer was holdings.ts (it's lib/api.ts now), still had a whole Authentication section for Stripe and Firebase (both ripped out months ago). Rewrote it. Added the missing endpoints, the MCP server section, three screenshots near the top, and the current gotchas list. The old one was a trap.
Two preview rows in the dashboard embed card still did s.funds.map(f => f.fund), assuming the pre-review-#10 object shape. ApiSignal.funds is a plain string array now. The equivalent fix landed for the text-format path in commit 969b874 but missed these two JSX lines. Same one-liner: just .join(', ') the array.
Fund profile page had ApiApiChangeRecord in the import โ Api repeated, classic. TypeScript caught it, Vercel red-X'd the deploy, the fund pages would have been broken if it hadn't. Renamed to ApiChangeRecord (the actual export) in the import and the function signature. Build's green again.
The free TickerTrace dashboard is no longer the headline product on the landing page. TraderDaddy is the execution layer, TickerTrace is the free data layer underneath. Two CTAs above the fold โ try the data, or pay for the trades. Dropped the Founders Partner section and the 'request a fund' callout (nobody used them); kept the comparison table and Patch Notes because both still earn their space.
The dashboard, changes page, and fund profile pages all fetch from the FastAPI server via lib/api.ts instead of recomputing in TypeScript. One source of truth, real customer of our own API, no more drift between TS and Python. Added /api/v1/briefing, /api/v1/activity, and /api/v1/holdings endpoints to cover everything the dashboard needs. Ported the significance threshold, streak tracking, and option-signal decoder to Python with snapshot tests. holdings.ts is now reduced to static reference data (FUND_PROVIDERS, FUND_AUM, PROVIDER_ORDER) plus the single raw-row consumer at /holdings.
Manually triggered today's scrape to bring in the Corgi Funds family that was added to the scraper this morning. EUV, CMAG, CQTM, XA, EYES, KYC, GNMX, AV, DOCK, WATS, GLAM, NYNY, STYL, WNDR, FDRS, FDRX. NYNY returned 530 holdings on the first try. Whoever built their API actually shipped it before launch โ that's rare.
The legacy /api/signals route used to recompute the entire signal payload in TypeScript from the CSV files โ a parallel implementation of api/data.py. It now proxies through to the FastAPI server. One source of truth for the public JSON, and external consumers (agents, your other service) get the exact same data as the dashboard. Also shipped lib/api.ts โ a typed TS client for the whole FastAPI surface, ready for future migrations.
Added a GitHub Actions workflow that runs pytest and next build on every push and PR. Also runs an AST parse check on the key Python files because the one time I trusted myself, I shipped a half-truncated data.py. Never again. Catches breakage before it hits Vultr.
The Swagger page used to be one undifferentiated wall of endpoints. Now they're grouped: public (the data), marketing (the TraderDaddy hand-off), auth (vestigial email/password endpoints). Easier to scan when you're trying to figure out what this API actually does.
Wrapped every HTTP fetch in the scraper with exponential-backoff retry (3 attempts, 2-10s waits). Used to be: REX returns a transient 503, we lose ULTI for the day, dashboard goes dark on that fund until tomorrow. Now: it retries quietly and keeps going. Skipped parallelizing the loop โ the CUSIP resolver caches to a JSON file and concurrent writes would corrupt it. Worth doing eventually.
The API now emits JSON log lines with a request_id, path, method, status, and duration_ms attached to every entry. docker logs tickertrace-api is now greppable. Every response also carries an x-request-id header โ when something breaks you can correlate the client error to the exact server log line that produced it.
Added tests/fixtures/ with two days of synthetic holdings CSVs, plus tests for compute_daily_changes, get_signals, get_divergences, get_full_payload, and 38 cases on the junk-ticker filter. Includes a regression guard that fails the build if get_full_payload ever recomputes the changes list more than once (the bug we just fixed). pytest tests/ now exists as a thing.
GitHub Actions was happily git add'ing data/holdings.db every day. A binary file that diffs to garbage. Bloating the repo by ~1MB/day. Gitignored it. The scraper rebuilds it from CSVs anyway.
Added a Next.js rewrite so the Vercel domain proxies /api/v1/* to the FastAPI server on Vultr. One domain to advertise instead of two. The Next.js /api/signals route (no v1) still works as before โ Next prefers local routes over rewrites.
If we're not charging anyone, we don't need Stripe. If everyone's getting in, we don't need Firebase. Both are gone from the API and the Docker image. Also: tightened CORS to an actual allowlist, threaded SQLite connections (no more open/close per call), moved DB init into FastAPI lifespan (no more racing migrations on startup), and added per-IP rate limits to the public endpoints because the whole thing is open now and we'd rather not get hammered.
The /api/v1/signals endpoint was calling compute_daily_changes() four times โ once for signals, once for changes, once for sector flow, once for divergences. Each call re-read the CSV. Now it computes once and threads the result through. Free speed.
The old filter said 'if it ends in XX, it's junk'. That's true for FGXXX and SPAXX. It's also true for any random ticker someone might list ending in XX. Replaced the tower of heuristics with a positive allowlist on ticker shape (starts with a letter, 1-10 alphanumeric chars). CUSIPs and identifiers fail this naturally; real tickers pass.
Auth UI is gone. No login, no API key, no Pro tier. Everything's free โ every endpoint, every signal, every fund. We're feeding this data into TraderDaddy.Pro anyway, so we figured we'd just open the spigot. Code for auth is still there; we just stopped rendering it. If we change our minds, it's one import away.
Added a ribbon, a button, and a hand-off card on the landing page. TickerTrace tracks what they're buying. TraderDaddy is where you do something about it. Yes, the link has a referral code. Yes, we tell you it's a referral link. We're trying to be decent.
The pricing card was lying to people. Replaced it with a 'The Data โ The Trade' card pair that's actually honest about what's free (everything) and where to go next (TraderDaddy). Also caught one comparison table row still saying 'Free / $15mo' โ that's been there for weeks.
Every option-income fund now has a strategy profile pulled from its actual prospectus. BLOX selling ATM calls? That's on-strategy โ not a penalty. EGGS mandates hedging? The hedge ratio metric now weighs 25% for them, 8% for funds that don't. Scoring finally matches what the fund says it does.
Rebuilt the entire scoring system from scratch. Black-Scholes Greeks, notional-weighted scoring, continuous Gaussian curves instead of step-function cliffs. Seven metric categories: strike selection, DTE management, spread efficiency, roll behavior, premium capture, hedge ratio, concentration risk. A DTE of 6.9 and 7.0 now score almost identically. As they should.
Three new active equity + options overlay ETFs from NestYield (Tidal). EGGQ does OTM call spreads on tech. EGGY targets 25% yield with selective covered calls. EGGS hedges with laddered puts and targets capital preservation. Scraper, API, dashboard, and effectiveness engine all updated in one session.
Found stale provider maps and inconsistent junk filtering. Some tickers were getting through that shouldn't have been, others were being eaten. Centralized everything into _is_junk_ticker() and applied it everywhere. Also cleaned up the fund profile categorization because the old one was held together with hopes and dreams.
Added X and Reddit share buttons right next to the Discord webhook. Your friends should know what ARK is doing. Also quietly fixed every single URL still pointing at the old Vercel subdomain โ 10 references, 6 files. The domain has been tickertrace.pro for weeks. We noticed.
The REX CSV returns holdings under the internal name 'REX_ULTI'. Our scraper trusted the source data. That was a mistake. Then our CUSIP resolver labeled every ticker 'OTHER'. 'OTHER' is in our junk filter. 88 rows, zero visible. Three engineers, zero brain cells.
We bought the domain, pointed DNS at Namecheap, then our AI tried to fight Apache for port 80 by installing nginx. On a server that already had Apache. It lost. We used Apache instead. SSL via Let's Encrypt.
Replaced our hand-rolled auth with Firebase. Had to generate a service account key from the console because gcloud wasn't installed. The AI tried to navigate the Firebase console in a browser. It could not. Instructions were given instead.
Three new ETFs in one session. ULTI (REX Shares) required a POST request to download CSV. SLTY (YieldMax) just worked. BLOX was fine until we realized their CSV uses 'Account' as their ticker column. Scraper didn't care. Dashboard did.
The 'Upgrade to Pro' button was pointing to localhost:8100. On production. For 12 hours. Nobody noticed because we didn't have any users yet. Fixed by someone who was definitely not the same person who wrote it.
Some funds only publish CUSIP identifiers (9-char codes), not ticker symbols. Built a cache + OpenFIGI fallback. Cache hit rate: ~95%. Cost: $0. Time spent debugging why 'N97284108' wasn't resolving: too long.
Share it โ the Reddit button posts a real writeup, not a flagged link