Inspiration

I kept running into the same frustration personally. My savings were in one app, crypto in another, stocks somewhere else — and I had no single answer to the most basic question: what is my actual net worth right now? Every time I wanted to make a real financial decision — am I saving enough? What happens to my portfolio if markets crash? When can I actually retire? — I either had to cobble together spreadsheets or pay for advice I couldn't afford.

Financial advisors charge $200–500/hr. Robo-advisors are generic. Bank apps show you transactions but never tell you what to do. 73% of millennials have no investment strategy, not because they don't care, but because the tools available to them are either too fragmented, too expensive, or too passive. The hackathon was the forcing function to actually build the solution I wished existed.


What it does

OmniWealth is a full-stack AI wealth management dashboard that gives any retail investor the tools previously reserved for people who could afford a financial advisor.

  • Unified net worth tracker — all assets, liabilities, income, and savings in one live number, persisted across devices via Supabase
  • Felix — an AI copilot powered by Llama 3.2 that answers questions built from your actual portfolio data, streaming responses in real time
  • Strategy lab — drag allocation sliders and watch a 25-year historical backtest (2000–2024, through dot-com, GFC, COVID, and crypto winter) update live alongside a 15-year Monte Carlo projection running 300 simulations
  • Wealth health score — a 0–100 score calculated from six dimensions: savings rate, diversification, debt ratio, crypto exposure, emergency fund, and goal alignment
  • Financial toolkit — debt payoff optimizer (avalanche vs snowball), financial independence timeline, goal planner, and tax loss harvesting detector with live Yahoo Finance prices
  • Money Pulse — market news feed with AI sentiment analysis, savings tips, weekly challenges, and a subscription audit

How we built it

The stack is a deliberate split between a Next.js 14 frontend and a FastAPI Python backend, with all AI inference running locally.

Frontend: Next.js 14 App Router, TypeScript, Tailwind CSS, and Chart.js for all visualisations. Supabase handles authentication (Google OAuth + email/password) with row-level security on the portfolios table.

Backend: FastAPI with Uvicorn, organised into six routers — assets, scenario, AI, backtest, portfolio builder, and news. All data validation through Pydantic.

AI: Llama 3.2 via Ollama running entirely on local hardware — no API key, no cost, no data leaving the machine. The copilot uses Server-Sent Events so responses appear token by token. The news router uses Llama to classify headlines as bullish, bearish, or neutral and generate a portfolio-specific impact statement.

The backtester uses 25 years of real annual return data across six asset classes and simulates any custom allocation month by month. The Monte Carlo engine runs $n = 300$ simulations drawing monthly returns from:

$$r_m \sim \mathcal{N}\left(\frac{\mu}{12},\ \frac{\sigma}{\sqrt{12}}\right)$$

where $\mu$ is the allocation-weighted mean annual return and $\sigma$ is the weighted annual volatility, producing p10, p50, and p90 wealth trajectories.


Challenges we ran into

Chart.js and conditional rendering was the most stubborn bug. Chart.js requires the canvas element to be in the DOM with non-zero dimensions before it can acquire a 2D context. Rendering charts inside conditional JSX blocks destroys and recreates the canvas on every state update, causing the ref to be null at draw time. The fix was extracting each chart into its own component so the canvas mounts once and stays mounted.

Auth routing and data sync needed careful architecture. The session redirect page had to check Supabase for real portfolio data — not just auth metadata — to decide whether to send a user to onboarding or straight to the dashboard. Getting .maybeSingle() instead of .single() (which throws a 406 on missing rows) and building a loadPortfolio() function that correctly prefers Supabase over stale localStorage took several debugging cycles.

Streaming AI responses required careful handling on both ends. FastAPI's StreamingResponse and the frontend's ReadableStream reader needed to handle partial JSON chunks, [DONE] signals, and Ollama's specific /api/generate format — which behaves differently from /api/chat and caused silent 500 errors until we identified the mismatch.

Payload shape mismatches between endpoints — the backtest router expects a nested allocation: {} object while the builder router expects flat fields — required building a unified payload function that satisfies both without breaking either.


Accomplishments that we're proud of

The Strategy lab is the feature we're most proud of. Dragging a single allocation slider and watching three things update simultaneously — the live portfolio score, a 25-year historical backtest with real crash events marked, and a 300-simulation Monte Carlo projection — in under a second feels genuinely powerful. It turns abstract allocation theory into something visceral and immediate.

We're also proud of the fact that the entire AI layer runs locally with zero API cost. Llama 3.2 via Ollama means the copilot, the news sentiment analysis, and the portfolio guidance all run on the user's machine. No rate limits, no data sent to third parties, no recurring AI cost.

And shipping a production-quality full-stack app — real auth, real database, real AI, real financial math — in hackathon time, with a polished UI that doesn't look like a prototype.


What we learned

The biggest technical lesson was about client-side rendering boundaries in Next.js. Anything that touches the DOM or browser APIs needs to either live in a useEffect or be loaded with dynamic(() => import(...), { ssr: false }). Fighting SSR hydration errors taught us to think much more carefully about what runs on the server versus the client.

On the product side, we learned that the hardest part of a financial tool is trust. Every number needs to be clearly sourced, every AI response needs to be grounded in real data, and the UI needs to communicate uncertainty honestly — hence the p10/p50/p90 bands in the Monte Carlo view rather than a single projected number that might feel more impressive but would be misleading.

And the meta-lesson: constraints are clarifying. Having to make fast decisions about what to cut, what to fake with good fallbacks, and what to build properly forced a level of prioritisation that unlimited time wouldn't have produced.


What's next for OmniWealth

Real account linking via Plaid — connect Robinhood, Coinbase, Chase, and other institutions so the net worth updates automatically without manual entry.

Recurring insights — a weekly email or push notification that surfaces one actionable insight based on portfolio changes: "Your cash allocation crossed 30% this week — here's what that means."

Social benchmarking — anonymised comparison against users in the same age bracket and income range. "Your savings rate is in the top 20% for your age group" is far more motivating than an abstract number.

Tax optimisation engine — move beyond harvest detection to proactive tax-loss harvesting recommendations with wash-sale tracking and year-end tax projection.

Mobile app — the dashboard is already responsive but a native iOS/Android app with biometric auth and widget support for net worth on the home screen would complete the experience.

The core insight that drives all of it: financial clarity should not be a luxury. The tools exist to give every person the same quality of guidance that used to cost $500 an hour — we just need to build them.

Built With

Share this project:

Updates