The challenge
r/wallstreetbets produces a constant stream of confident stock calls, and nobody keeps score. A post with ten thousand upvotes looks the same whether its author has been right nine times or wrong nine times.
The options-flow tools that could add context are expensive and built for professionals. I wanted one calm dashboard that shows what the crowd is saying, what the options market is doing and who has actually been right.
The approach
Unusual is a Next.js app in front of a single Node process. The backend polls option chains, scrapes WSB every ten minutes and keeps everything in memory plus one SQLite file. Three pages read from it: a flow tape, a sentiment radar and an analyst leaderboard.
The leaderboard is the core. Every DD-flaired post is parsed for its ticker and direction, entered at the first close on or after the post and scored at six horizons from one day to one year. Authors are ranked by the Wilson 95% lower bound of their win rate, so a few lucky calls cannot outrank a long record.
- Options flow tapeContracts with unusual volume, graded active, warm or hot by volume against open interest and premium, streamed to the browser.
- WSB sentiment radarTicker mentions scored with a custom WSB lexicon and set beside real call and put volume and price.
- Analyst leaderboardEvery DD author’s calls, scored against daily closes and ranked by a statistically cautious win rate.
- Plans and alertsClerk sign-in, Stripe subscriptions on two tiers and an opt-in email digest sent through Resend.
From Reddit post to scored call
- 01ScrapeWSB posts and comments, every 10 minutes
- 02ExtractTickers, direction and options chatter
- 03ClassifyClaude Haiku picks the real bet
- 04PriceEntry and exits at daily closes
- 05RankWilson lower bound, 3-call minimum
How it’s built
Two deployables: a Next.js app on Vercel and one long-running Node process on Render. The backend uses no web framework and no build step; TypeScript runs natively on Node 22.
- 01Snapshot diff engineThe free feed gives point-in-time option chains, not trades. The poller remembers each contract’s cumulative volume and emits an event only when it rises by 25 contracts or more.
- 02Live streamEvents reach the browser over Server-Sent Events: the whole tape on connect, then one message per event, with a heartbeat every 15 seconds.
- 03Sentiment pipelineA fallback chain of Reddit sources feeds ticker extraction, lexicon scoring and a parser for options chatter such as “TSLA 420c 6/20”. Each cycle is written to SQLite in one transaction.
- 04Analyst scoringDD posts become directional calls, scored on the stock’s move after 1 day, 1 week and 1, 3, 6 and 12 months, then aggregated per author.
- 05Accounts and billingClerk handles sign-in and holds each user’s plan and watchlist in its metadata, so there is no user database. Stripe webhooks update the plan.
- 06Next.js front endThree signed-in pages and a landing page. The flow page keeps the 80 newest contracts and flashes each new print as it arrives.
Technical decisions
- Signal tiers a trader can check
- A contract is hot when volume is at least 4× open interest or premium passes $25M, and warm at 2.2× or $8M.
- A lexicon instead of a model
- Sentiment is tanh(sum ÷ 6) over about a hundred WSB terms, bigrams and emoji with negation handling, scored on the 120 characters around each ticker. It is fast, free and explainable.
- Spikes against a ticker’s own baseline
- Chatter is flagged by z-score against that ticker’s previous 24 hours, warm at 2σ and hot at 3σ, so a normally quiet stock can still stand out.
- Regex first, LLM second
- A regex pass finds every ticker in a post. Claude Haiku only chooses which of those the author is betting on, so the model cannot invent a ticker.
- Wilson score for ranking
- Authors are ranked by the lower bound of a 95% confidence interval on win rate, with a three-call minimum, which rewards a long record over a hot streak.
- The paywall lives on the backend
- The server verifies the Clerk token and blanks any horizon above the caller’s plan before responding, so locked data never reaches the browser.



