Data model
The Supabase/Postgres tables behind your workspace — accounts, holdings, transactions, strategies, backtests, watchlists, alerts, orders — plus the leaderboard view and row-level security model.
Your workspace is persisted in Postgres (via Supabase). Every table has row-level security (RLS) enabled, so the database itself enforces who can see and change what — the app never grants blanket access. Sensitive columns like weights and metrics are stored as JSONB.
Tables
accounts
id, user_id, name, broker, currency (default USD), type (default
brokerage), timestamps. RLS: owner-only for select/insert/update/delete.
holdings
id, account_id, symbol, qty, cost_basis, timestamps. RLS: authorized
through the owning account — you must own the account. Deleting an account
cascades to its holdings.
transactions
id, account_id, symbol, side (buy/sell/dividend/deposit/withdrawal),
qty, price, executed_at. RLS: through the owning account.
strategies
id, user_id, name, type (default momentum), params (JSONB), code,
is_public (default false), timestamps. RLS: readable if is_public or
owner; only the owner writes. Powers the public strategy browser.
backtests
id, user_id, strategy_id (nullable), config (JSONB), metrics (JSONB),
equity_curve (JSONB), is_public (default false), created_at. RLS:
readable if is_public or owner; only the owner writes. See
publishing.
watchlists
id, user_id, name, symbols (text[]), timestamps. RLS: owner-only.
alerts
id, user_id, symbol, condition (JSONB), is_active (default true),
timestamps. RLS: owner-only.
orders
id, user_id, account_id, symbol, side (buy/sell), qty, status
(draft/submitted/filled/cancelled), timestamps. Schema only —
reserved for future live trading, no UI wires it up today. RLS: owner-only.
broker_connections(encrypted broker credentials) is intentionally deferred — not created in the current schema.
leaderboard
leaderboard is a view, not a table. It joins public backtests with public
profiles and exposes ranking columns — sharpe, annualized_return,
max_drawdown — pulled from the metrics JSONB.
Two safety properties:
security_invoker = true— the view runs with the caller’s privileges, so the underlying RLS still governs visibility. It never leaks private runs.- Safe numeric coercion — because
metricsis user-written, the view parses each numeric with a guard (util.safe_numeric) that returnsNULLon anything that isn’t a finite decimal. One malformed row becomes blank cells instead of taking down the whole leaderboard.
Row-level security in one line
- Owner-only tables:
accounts,holdings(via account),transactions(via account),watchlists,alerts,orders. - Public-or-owner tables:
strategies,backtests(gated onis_public). - Nothing is readable by an anonymous visitor except explicitly public strategies/backtests and the leaderboard view.