SPB Git

Repository Public

PLAYGROUND.md 7.6 KB

# Playground — catalog, deep links, how to extend

The playground (/playground, also embedded in /dashboard/playground) is a single reusable component:

jsx
import Playground from './src/playground/Playground.jsx'
<Playground apiKey={sessionKey} embedded />   // apiKey optional → keyless mode

apiKey is sent as Authorization: Bearer … on every run and on the WebSocket URL (?api_key=). It lives in React state only — never localStorage, sessionStorage, cookies or the address bar. Code exports always print $HFMD_API_KEY.

# Files

File Role
src/playground/catalog.js The request types (data only) + resolution/validation helpers
src/playground/Playground.jsx The component: catalog nav, form, URL preview, run, result views, quota panel, code export, presets
src/playground/RequestForm.jsx Form generated from entry.params
src/playground/SymbolInput.jsx Debounced autocomplete (/v1/{asset}/tickers?search=, /v1/futures/roots, /v1/options/tickers)
src/playground/views/{JsonView,TableView,ChartView}.jsx Collapsible JSON · virtualised sortable table (50 000 rows) · lightweight-charts (candles / line, roll markers)
src/playground/RateLimitPanel.jsx X-RateLimit-* bars, reset countdown, 429 Retry-After countdown
src/playground/WebSocketTab.jsx /v1/stream client (disabled without key)
src/playground/codegen.js curl / Python (requests + pandas) / JavaScript (fetch) generators
src/playground/presets.js One-click examples
src/components/Pg{Tabs,CodeBlock,Callout,CopyButton}.jsx Local UI primitives (swap for web-core's shared ones when available)

# Catalog schema

js
{
  id: 'futures-continuous',            // stable, URL-safe — used in deep links and tests
  group: 'Futures',                    // one of GROUPS: Bars · Futures · Options · Fundamentals · Symbols · Stream
  title: 'Futures continuous (v2)',
  method: 'GET',                       // or 'WS' for the stream tab
  path: '/v1/futures/{root}/continuous',
  operationIds: ['futures_continuous_v1_futures__root__continuous_get'],  // OpenAPI ids that map here
  description: 'Markdown-free one-liner shown above the form',
  fixed: { asset: 'crypto' },          // optional: values injected in the path/query but hidden from the form
  requiresKey: false,                  // stream only
  params: [
    { name: 'root', in: 'path', type: 'symbol', required: true, source: 'futures_root', default: 'ES', placeholder: 'ES', help: '…' },
    { name: 'roll', in: 'query', type: 'enum', enum: ['', 'volume', 'open_interest', 'calendar'], default: 'volume' },
    { name: 'depth', in: 'query', type: 'number', default: 1, min: 1, max: 12 },
    { name: 'from', in: 'query', type: 'date' },                   // native <input type=date>
    { name: 'start', in: 'query', type: 'datetime' },              // native datetime-local, sent as "YYYY-MM-DD HH:MM:SS"
    { name: 'adjustment', in: 'query', type: 'enum', enumBy: { asset: { stock: [...], futures: [...] } }, enum: [''] },  // dependent enum
    { name: 'symbol', in: 'path', type: 'text', pattern: '^[A-Z]{1,3}[FGHJKMNQUVXZ]\\d{2}$' },
  ],
  result: {
    kind: 'bars' | 'series' | 'table' | 'list' | 'object' | 'ws' | 'auto',
    timeKey: 'datetime',               // x axis hint for the chart
    valueKeys: ['close', 'settle'],    // preferred y column(s) for line charts
    listKey: 'tickers',                // for `list` kinds: JSON key holding a flat array
    envelope: 'v2',                    // rows live in `data`, extras in `meta` (roll_dates → chart markers)
  },
}

Param types → controls: symbol (autocomplete), enum (select; '' = server default), number, date, datetime, text (optional pattern), bool. Validation (validate()) enforces required, numeric bounds, date formats, patterns and enum membership; Run is disabled until the form is valid.

Result routing after a run: bars/series with a detectable time column → Chart tab first; any rows → Table; otherwise JSON. format=csv parses the CSV for the table and offers a download; format=parquet offers the download only.

text
/playground?ep=<ref>&<param>=<value>&…
  • ref is either a catalog id (stock-bars, futures-chain, …) or an OpenAPI operationId (bars_v1_bars__asset___ticker__get, opt_chain_v1_options_chain__ticker__get, …). FastAPI's default naming is <function>_<path with / and {} → _>_<method>.
  • Every other query parameter is a form value (path or query param of the entry). Unknown names are ignored for curated entries and kept for spec-generated ones.
  • Resolution order (resolveEntry() in Playground.jsx):
    1. catalog id match;
    2. entry whose operationIds contains ref — when several entries share an operation (e.g. bars_v1_bars__asset___ticker__get → Stock/ETF, Crypto, FX, Index, legacy futures) the one whose fixed values match the supplied params wins (asset=crypto → Crypto bars);
    3. otherwise /openapi.json is fetched, ref is looked up, and the entry with the same method + path template is used;
    4. otherwise a generic form is generated from the spec (path/query parameters, enums from schema.enum or ^(a|b)$ patterns, defaults, descriptions) — a notice says so;
    5. otherwise a warning is shown and the default entry (stock-bars) is loaded.
  • The address bar is rewritten (history.replace) to ?ep=<catalog id>&<non-default values> so links copied from the browser are short and stable. Spec-generated entries keep ep=<operationId>.
  • Docs "Try it" buttons should therefore link to /playground?ep=${operation.operationId}&${exampleParams}. No coordination with the catalog is needed: unknown operations still get a working form.
  • The dashboard mounts the same component under /dashboard/playground — the same query contract applies there.

# Presets (presets.js)

Label Entry Values
AAPL 1-min today stock-bars ticker=AAPL timeframe=1min start=<today>
CL chain futures-chain root=CL
ES continuous back-adjusted 2015-2025 futures-continuous root=ES roll=volume adjust=back_adjusted depth=1 interval=1d from=2015-01-01 to=2025-12-31
NG term structure futures-term-structure root=NG
AAPL income statements (quarterly) fundamentals-statements ticker=AAPL statement=income period=quarterly
Screener: PE<15 & ROE>15% fundamentals-screener filter=pe<15,roe>0.15 sort=-market_cap

# Adding a request type

  1. Append an entry to CATALOG in catalog.js (copy the closest one). Put the FastAPI operationId in operationIds — check it in /openapi.json once the route exists.
  2. If it belongs to a new area, add the group name to GROUPS (ordering of the left column).
  3. Set result so the right view opens first (bars for OHLC, series + timeKey/valueKeys for one line, list + listKey for flat arrays).
  4. Optional: a preset in presets.js, a symbol source in SymbolInput.jsx if a new autocomplete endpoint is needed.
  5. Add an assertion in e2e/playground.spec.js (deep link → URL preview) and run npm run test:e2e.

Errors: any non-2xx JSON envelope {"error":{code,message,docs}} is displayed with a Docs button; a bare FastAPI {"detail":"Not Found"} 404 is rendered as "Endpoint not available yet — coming soon" (the v2 modules are deployed independently).

# Tests

npm run test:e2e (Playwright, Chromium). The config builds the site and serves vite preview on :4173; specs mock every /v1/* and /openapi.json call with page.route, so the suite runs offline.

Files main
Menu

Theme