About One Portfolio
What Inspired Us
We wanted one place to see our full financial picture—stocks, ETFs, crypto, and life assets like gold, real estate, or recurring income—without juggling multiple apps or spreadsheets. Existing tools were either too narrow (only stocks), too heavy (full brokerage UIs), or not mobile-first. We were inspired to build a single portfolio app that treats every asset type as a first-class citizen: track listed securities with live prices, add custom assets (property, side income, precious metals), set goals, and get simple, AI-powered insights—all from your phone, with data stored locally so you stay in control.
What We Learned
- Expo + React Native can ship a full-featured portfolio app to iOS, Android, and Web from one codebase, with file-based routing (Expo Router), safe areas, and dark mode out of the box.
- Local-first data with expo-sqlite keeps the app fast and usable offline; we learned to design schema and repositories so that market data (Finnhub, CoinGecko) and AI insights (OpenAI) layer on top without replacing the source of truth.
- Structured AI outputs matter: we use OpenAI’s JSON schema mode so portfolio insights (sector breakdown, geographic exposure, tips, opportunities) come back as typed, cacheable data we can store and reuse instead of re-calling the API on every open.
- Context and provider design in React—assets, goals, user preferences, subscription state, theme—taught us how to keep screens simple and logic testable while sharing state across tabs and flows.
How We Built It
We built One Portfolio as a React Native (Expo) app with TypeScript and a clear split between UI, data, and services:
Navigation & UX
Expo Router drives file-based routes: onboarding (portfolio preview → premium → track-all), currency selection, then a tabbed home (holdings, total value, allocation), goals, analytics, and settings. Add-asset flows support both listed (stocks/ETFs/crypto via ticker search) and custom assets (name, quantity, price, optional recurring income).Data layer
SQLite holds assets, goals, and cached portfolio insights. We defined a single schema (e.g. assets withtype IN ('listed', 'custom'), goals with categories and target dates) and repository functions for CRUD. Asset value is computed as quantity × current price; for listed assets we refresh prices from Finnhub (stocks/ETFs) and CoinGecko (crypto), with support for gold and batch requests to respect rate limits.Portfolio math
Total portfolio value and allocation are computed from the same basis: [ V_{\text{portfolio}} = \sum_{i} q_i \cdot p_i\,, \qquad w_i = \frac{q_i \cdot p_i}{V_{\text{portfolio}}}\,, ] where (q_i) is quantity and (p_i) is current price for asset (i), and (w_i) is the weight used for allocation charts and goal progress.Goals
Users define goals (e.g. “Emergency fund”, “Retirement”) with target amount, currency, optional date, and which asset categories count. Progress is ( \min(100\%, 100 \cdot V_{\text{relevant}} / V_{\text{target}}) ), with relevant value coming from assets whose category matches the goal.Analytics & AI
The analytics tab shows sector breakdown, geographic exposure, a simple health score, tips, and upcoming events. For premium users we call an analytics service that (1) checks a 24h cache keyed by current assets, (2) if stale or missing, sends the portfolio to OpenAI with a strict JSON schema, (3) parses and caches the result in SQLite. That keeps insights fast and API usage predictable.Monetization & polish
We integrated RevenueCat for subscription and paywall UI so premium insights are gated cleanly. Settings handle base currency, profile, and subscription state; the app uses a theme context (light/dark) and StyleSheet-based styling with theme colors throughout.
Challenges We Faced
- Unifying listed and custom asset
- Listed assets have tickers and market prices, while custom assets do not. Creating a single model that supports both cleanly (and keeps the UI simple) was key.
- Keeping the portfolio accurate offline
- Users might not open the app daily. I needed a robust approach for recurring-income “catch-up” so the portfolio doesn’t silently drift behind.
Built With
- coingeko
- expo.io
- finnhub
- javascript
- openai
- react-native
- revenuecat
- sqlite
- typescript
Log in or sign up for Devpost to join the conversation.