Stock League - Serverpod Hackathon Submission
Inspiration
I pick stocks. I think I'm good at it. But I had no way to prove it.
Tracking picks in a spreadsheet? Boring. Comparing performance against friends? Required manual work. The picks get forgotten. The bragging rights never materialize.
When I saw the Serverpod hackathon, it clicked: I could finally build the app I'd been wanting: A hackathon for stocks. Create a challenge, pick 5 stocks, compete with friends on real returns. Simple, competitive, fun.
What it does
Stock League turns stock picking into a social competition:
- Create Challenges - Set a start date, duration, and invite friends
- Pick 5 Stocks - Everyone locks in their picks before the challenge begins
- Real-time Leaderboard - Track portfolio returns as prices update throughout the day
- Bragging Rights - Winner gets eternal glory (and screenshot-worthy proof)
No fake money. No complex trading mechanics. Just pure stock-picking skill, measured against your friends + worldwide.
The Math Behind Rankings
Each user's portfolio return is calculated as the average return across their 5 picks:
$$R_{portfolio} = \frac{1}{n} \sum_{i=1}^{n} \frac{P_{current,i} - P_{start,i}}{P_{start,i}} \times 100$$
Where:
- $P_{start,i}$ = Price of stock $i$ when challenge started (locked via Future Call)
- $P_{current,i}$ = Current price of stock $i$
- $n$ = Number of stocks picked (5)
How we built it
Frontend: Flutter for a smooth cross-platform experience. Custom motion system with haptics for that satisfying tap feedback.
Backend: Serverpod running on Serverpod Cloud. PostgreSQL for persistence.
The Secret Sauce — Future Calls: Each challenge has its own timeline. I needed:
- Start prices captured exactly when a challenge begins (not at midnight)
- Leaderboards recalculated during market hours
- Final rankings frozen when challenges complete
Serverpod's Future Calls handled this perfectly. I schedule lifecycle events per challenge, and they fire precisely when needed:
// Schedule challenge activation at exact start time
await session.futureCall(
'activateChallenge',
challenge.startDate,
{'challengeId': challenge.id},
);
No cron hacks. No polling loops. Clean, event-driven automation.
Challenges we ran into
1. Smart Fetching
My first version fetched prices for all stocks on every update:
$$API_{calls} = |S_{all}| \approx 500+$$
Refactored to only fetch distinct stocks users actually picked:
$$API_{calls} = |S_{picked}| = \bigcup_{u \in U} picks(u) \approx 50$$
A 10x reduction in API calls while maintaining complete data coverage.
2. Price Capture Timing
Stock prices must be captured at market open ($t = 09:30$ ET), not midnight. Each challenge $C_i$ has a unique start time $t_{start,i}$. Future Calls let me schedule precisely:
$$\forall C_i : \text{schedule}(\texttt{captureStartPrices}, t_{start,i})$$
3. Leaderboard Performance
Naive approach—loop through users, calculate returns individually:
$$O(u \times s)$$ where $u$ = users, $s$ = stocks per user
Optimized to batch SQL with window functions:
RANK() OVER (ORDER BY portfolio_return DESC)
Result: $O(1)$ query regardless of user count. Instant leaderboards.
4. API Rate Limits
Alpha Vantage limit: 5 requests/minute. With smart caching:
$$\text{Cache TTL} = 30s, \quad \text{Effective calls} = \frac{|S_{picked}|}{12} \text{ per hour}$$
Stays well under quota while keeping data fresh.
Accomplishments that we're proud of
- Future Calls in production — Not just a demo, but powering real challenge lifecycles with precise timing
- Active challenges running (one dedicated to ServerPod) — My friends and I actually use this. The group chat trash talk has never been better
- Sub-second leaderboard updates — Batch SQL ranking: $O(1)$ query time
- Full-stack Dart — Shared models between Flutter and Serverpod eliminated serialization bugs
What we learned
Future Calls > Cron for dynamic scheduling — When events don't follow a fixed schedule, Future Calls are the right abstraction. Game-changer.
Start simple, optimize later — First version: $O(n^2)$. Production version: $O(1)$. Real usage teaches you where to optimize.
Serverpod's full-stack Dart is underrated — Same language, same models, same types. The productivity boost compounds daily.
Build for yourself first — The best features came from my own frustration using the app.
What's next for Stock League
| Feature | Description |
|---|---|
| Public Challenges | Join challenges with friends and strangers, compete globally |
| Themed Challenges | "Tech stocks only" or "Dividend kings" constraints |
| Historical Replays | Backtest: "What if you picked in March 2020?" |
| Push Notifications | Personalized Alert |
Built With
- dart
- flutter
- postgresql
- serverpod
- serverpod-cloud
- serverpod-future-calls
Log in or sign up for Devpost to join the conversation.