← Back to work
2026 · Full-Stack · Personal Project

Forecast Arena

A multi-user forecasting game with tested scoring logic, seeded question bank, and production-ready Supabase schema — built from a 50-event MSBA workbook that used to live in Excel.

2026 Product & Engineering Team of one
50
Forecasting questions seeded
2
Scoring systems tested
6
Prototype screens shipped
1
SQL migration + RLS
The problem

Forecasting as homework, not as a game.

My UCI MSBA forecasting league started life as a single Excel workbook — fifty events, one row each, with a deadline, an investment amount, and later, an actual result. Students submitted picks in the sheet. The professor scored them by hand. Rankings surfaced days after the deadline, if at all, and nobody could see how their calibration was trending until the league was over.

The workbook worked as a scoring engine. It failed as a product. No live leaderboard, no shared question bank, no lockout at the deadline, no way for a student to see where they stood while there was still time to adjust. I wanted to turn the class exercise into something that behaved like a real forecasting platform — closer to Metaculus or Good Judgment Open — without waiting for someone else to build it.

Approach

Test the math first. Build the interface second.

I split the work into two tracks and refused to conflate them. The scoring math had to be dead right — a leaderboard is only trusted if the arithmetic is trusted — so I ported the workbook's logic into TypeScript and wrote tests around each rule before touching a single screen. Continuous ROI, binary Brier ROI, investment validation, deadline locking, per-user summaries, and league ranking each got their own coverage.

Only once the engine was green did I build interface on top. Next.js App Router for routing, a seeded 50-question dataset as the source of truth for the prototype, and a Supabase-ready schema sitting behind it so the same UI could point at a live database the moment credentials landed. The seed and the schema share the same shape — no throwaway mock layer to rip out later.

What I built

Two audiences, one app.

Students get forecast entry with investment validation and a hard deadline lock, a results view that shows why their score came out where it did, and a leaderboard they can check at any time — not after the deadline. Professors get a roster view, a question-bank editor, and an actual-result publishing screen that recomputes the league the moment a result lands.

Six prototype screens, all wired to the same tested scoring engine and the same seeded 50-question dataset. Underneath sits a single SQL migration that stands up the full schema — users, questions, forecasts, results, memberships — with row-level security scoped so students only see their own picks and professors only publish for their own classes. The UI currently reads seeded data so the prototype is fully clickable without backend wiring.

Illustrative leaderboard LEAGUE · WEEK 6 1 Player 1 +142.6 2 Player 2 +111.8 3 Player 3 +84.2 4 Player 4 +52.9
Live leaderboard mockup — the view every student can check before the deadline, not after.
Next.jsTypeScript SupabasePostgreSQL RLSTailwind
Outcome

A static workbook, shipped as a full-stack app.

The system runs end-to-end on seeded data as a clickable prototype. All scoring rules pass their TypeScript tests, the deadline lock holds, and league ranking is stable across both scoring systems. The Supabase migration applies cleanly with RLS enforced on every table.

The more important outcome is the shape of the work. What began as a class assignment lived in Excel became a full-stack app with two user routes, a tested scoring core, and a production-ready schema. Swapping the seeded data for live Supabase reads is a wiring change, not a rewrite — the analyst-to-product jump landed cleanly.

The scoring engine is only the boring half. The interesting half is what a student does with a leaderboard they can check before the deadline, not after.
Reflection

What I'd do next.

The biggest lesson was that the scoring math is the moat, not the interface. Any bootcamp graduate can ship a Next.js dashboard. Very few will pause to write tests for six scoring rules before drawing a single component — but the moment leaderboard numbers stop matching the workbook, the product is dead. Getting the engine right up front bought the freedom to iterate on the UI without fear.

If I picked it back up, I'd wire Supabase auth end-to-end, add a per-student calibration chart so people can see their forecasting bias trend over time, and turn the professor's publish action into a real-time push so leaderboards animate as results land. A Brier-vs-ROI comparison view would let the class argue which scoring system actually rewards good forecasters — a debate the Excel version could never have supported.

See it in motion

Explore the prototype.

The clickable prototype runs on seeded data with the same UI a live Supabase deployment would use. Walk through the student and professor routes end-to-end.