packages/feed/docs/markets/trending-screener.md
/markets)Browse-first markets surface: perpetuals and predictions in a table layout, with a path into the full trading terminal. Shell navigation label Terminal routes here instead of opening the split-panel terminal immediately.
The page title is Terminal so it matches the nav item users tap to arrive here (shared vocabulary between chrome and page).
| Problem | Approach |
|---|---|
Cognitive load — Landing users on MarketsTradingTerminal forces chart + order UI before they know what is moving. | Browse-first: screener lists momentum and liquidity signals; Trade (perps) / Predict (predictions) opens /markets with the instrument pre-selected. |
| DEX-style expectations — Traders expect a scannable grid (sortable columns, sparklines, many columns). | Visual parity where honest: same density as external screeners; no fake on-chain fields (pool liq, buy/sell tx split, “paid” badges). |
| Data truth — Synthetic Feed perps are not ERC-20s; inventing mcap/tax would mislead. | Columns map to real PerpMarket / prediction fields; tooltips explain OI, 24h % vs chart range, and funding. |
| Performance — Dozens of rows × full history would DDoS our own API. | IntersectionObserver before usePerpHistory; cap rows (100 perps / 100 predictions). |
| Search consistency — Split filters per tab would double-fetch or diverge. | Single search bar, debounced via useMarketsPageData’s deferredSearchQuery, applies to whichever tab is active. |
Sort without backend churn — Re-sorting a table should not hit /api/markets/predictions or perp APIs again; data is already in memory. | Client-side sort for both tables after the initial load: perps via sortPerpsForScreener, predictions via column sort inside PredictionsScreenerTable (useMemo over the row list). |
| Rate limits — Public read tier returns 429 under burst traffic; a single failed fetch should not strand the UI in “loading” forever. | 429 retries with backoff + Retry-After in useMarketsPageData fetchData; Strict Mode mount cleanup resets hasMountedRef so the second mount still fetches predictions (see code comments there). |
| Remember UX choices — Users expect the screener to feel like a tool, not reset every visit. | localStorage for last asset tab, perp sort, and prediction sort (validated keys only; corrupt values fall back to defaults). Why not session-only: return visits and refresh should preserve intent without requiring accounts. |
/markets./markets?marketKind=…&marketId=…&filter=… (see below).Must stay aligned with parseSelected() in MarketsTradingTerminal:
| Asset | Query |
|---|---|
| Perp | ?marketKind=perp&marketId=<TICKER>&filter=perp |
| Prediction | ?marketKind=prediction&marketId=<id>&filter=prediction |
Why filter: Keeps the unified market list in the same “universe” (perp vs prediction) as the selection.
| Key | Value | Default if missing/invalid |
|---|---|---|
screener:assetTab | perps | predictions | perps |
screener:perpSort | JSON { key, dir } per ScreenerSortKey | { key: 'trending', dir: 'desc' } |
screener:predSort | JSON { key, dir } (market | yesPercent | volume) | { key: 'volume', dir: 'desc' } |
Why separate keys: Perp and prediction sort dimensions differ; merging into one object would complicate validation and migrations.
| Path | Role |
|---|---|
apps/web/src/app/markets/page.tsx | Route shell: tabs, shared search, localStorage restore/write, row builders. |
apps/web/src/app/markets/_components/TrendingScreenerTable.tsx | Perp table: sortable column headers, sparklines, tooltips. |
apps/web/src/app/markets/_components/PredictionsScreenerTable.tsx | Prediction table: client-side column sort, empty/loading states, Predict CTA. |
apps/web/src/app/markets/_components/PerpSparklineCell.tsx | Lazy sparkline from usePerpHistory. |
apps/web/src/app/markets/_lib/sortPerpsForScreener.ts | Pure perp sort + cap; trending weights mirror dashboard logic. |
apps/web/src/app/markets/_hooks/useMarketsPageData.ts | Perp store, debounced filter, one predictions fetch, 429 retry, Strict Mode–safe mount effect. |
packages/testing/unit/markets/sort-perps-screener.test.ts | Unit tests for perp sort. |
Reference UIs show mcap, pool liquidity, txn splits, etc. We intentionally do not show those unless the backend exposes them.
volume24h.usePerpHistory after the row enters the viewport.href: /markets./markets, or legacy /markets/perps/* / /markets/predictions/* so the item stays highlighted across the markets journey.sortPerpsForScreener modes.ROUTES.MARKETS_TRENDING, data-testid="markets-trending-screener" / markets-trending-predictions.Prioritized by impact and dependency on backend work.
markets-api-caching.md.usePerpMarketsPolling on trending?page=N&limit=M on both endpoints for external consumers.sort-perps-screener.test.ts for regression safety.Items we are not planning without domain support: DEX-style “paid listing”, tax %, buy/sell txn ratios, chain social links.
See the root CHANGELOG.md [Unreleased] / dated sections for release notes. This file is the design + rationale source; the changelog is the what shipped log.
apps/web/src/app/markets/README.mdapps/web/src/app/markets/page.tsx → MarketsTradingTerminalpackages/api/src/rate-limiting/README.mdCLAUDE.md at repo root