|
1 | | -# XY TERMINAL |
2 | | - |
3 | | -XY TERMINAL is a dense, professional-market workstation built entirely in |
4 | | -[Reflex](https://reflex.dev) with the `xy[reflex]` integration. It demonstrates |
5 | | -finance charts, state-driven figures, fixed and streamed data, and semantic |
6 | | -chart events in one responsive page. |
7 | | - |
8 | | -> **SIMULATED DATA** — every quote, price series, position, news story, |
9 | | -> economic event, and risk result in this example is fictional and generated |
10 | | -> locally from fixed seeds. The app does not contact a market-data service, |
11 | | -> submit orders, or require an API key. It is an interface and charting demo, |
12 | | -> not investment advice. |
13 | | -
|
14 | | -The black-and-amber visual language is inspired by professional market |
15 | | -terminals, but the app does not use third-party brand names, logos, assets, or data. |
16 | | - |
17 | | -## Workspaces |
18 | | - |
19 | | -- **Markets (`MKTS`)** — a landing-page SPY `FinanceChart` with native OHLCV, |
20 | | - studies, oscillator, projection, and finance tools, plus cross-asset quotes, |
21 | | - movers, breadth, a market heatmap, the yield curve, and a live pulse fed |
22 | | - through `reflex_xy.append()`. |
23 | | -- **Security (`DES <symbol>`)** — daily or weekly OHLCV, range controls, |
24 | | - overlays, oscillator panes, finance drawing presets, key statistics, |
25 | | - related stories, and a paper-only position-risk ticket. |
26 | | -- **Portfolio (`PORT`)** — deterministic positions, NAV and P&L, equity and |
27 | | - drawdown, allocation, contribution, and exposure. Choosing a position opens |
28 | | - its Security workspace. |
29 | | -- **Risk (`RISK`)** — return distribution with VaR/CVaR, correlations, factor |
30 | | - exposure, confidence controls, and deterministic stress scenarios. |
31 | | -- **News (`NEWS`)** — simulated stories with sentiment and impact metadata, |
32 | | - story detail, and a fictional economic calendar. |
33 | | - |
34 | | -The persistent shell also includes a ticker tape, watchlist, context rail, |
35 | | -function-key navigation, status line, and a developer drawer. The drawer shows |
36 | | -live Python source, a compact Reflex-state snapshot, and an abbreviated XY |
37 | | -chart/layer specification. |
38 | | - |
39 | | -## Commands |
40 | | - |
41 | | -Type a command in the top command bar and press Enter: |
42 | | - |
43 | | -| Command | Result | |
44 | | -| --- | --- | |
45 | | -| `MKTS` | Open Markets | |
46 | | -| `DES AAPL` | Open the Security workspace for a known symbol | |
47 | | -| `PORT` | Open Portfolio | |
48 | | -| `RISK` | Open Risk | |
49 | | -| `NEWS` | Open News | |
50 | | -| `HELP` | Show the command reference | |
51 | | - |
52 | | -Commands and symbols are case-insensitive. Unknown input stays in the app and |
53 | | -produces an inline status message. |
| 1 | +# XY Reflex showcase |
| 2 | + |
| 3 | +A [Reflex](https://reflex.dev) app built with the `xy[reflex]` integration. |
| 4 | +One page walks through the ways to link chart data into a Reflex app, and each |
| 5 | +section carries a **Code** accordion showing its source via |
| 6 | +`inspect.getsource`. |
| 7 | + |
| 8 | +Chart data rides the app's own websocket as a second socket.io namespace of |
| 9 | +binary columns; Reflex state holds only a token string per chart. |
| 10 | + |
| 11 | +## What it shows |
| 12 | + |
| 13 | +1. **Live figure var + events** — a 1M-point drillable scatter from an |
| 14 | + `@reflex_xy.figure` method, with `on_point_hover` / `on_point_click` / |
| 15 | + `on_select_end` handlers. |
| 16 | +2. **A chart driven by state vars** — a histogram whose bin count is a slider |
| 17 | + and whose data is cross-filtered by the selection above; changing either |
| 18 | + recomputes and re-publishes the figure under a stable token. |
| 19 | +3. **A dynamically updating chart** — a line grown by a background task via |
| 20 | + `reflex_xy.append`. |
| 21 | +4. **Data computed from `on_view_change`** — pan/zoom an overview and a detail |
| 22 | + histogram recomputes from the points in the reported window. |
| 23 | +5. **Fixed data, two ways** — a `xy.Chart` passed straight to `reflex_xy.chart` |
| 24 | + (static payload tier) and a `reflex_xy.inline` token (fixed data served |
| 25 | + through the kernel). |
| 26 | +6. **The 100M drilldown, adapter-native** — the live drilldown scatter |
| 27 | + from [`examples/fastapi`](../fastapi) (identical seed-11 data and mark |
| 28 | + config, a density surface that drills into exact points on zoom) as a |
| 29 | + single `reflex_xy.inline` token. The FastAPI app hand-rolls its transport |
| 30 | + for this chart (a Starlette endpoint plus an HTTP comm bridge); here the |
| 31 | + adapter's websocket namespace and the kernel's density tiers do all of it, |
| 32 | + so behavioral differences between the two apps isolate what that custom |
| 33 | + code adds. |
54 | 34 |
|
55 | 35 | ## Run |
56 | 36 |
|
57 | | -From this directory: |
58 | | - |
59 | 37 | ```bash |
60 | 38 | cd examples/reflex |
61 | 39 | uv run reflex run |
62 | 40 | ``` |
63 | 41 |
|
64 | | -`uv run` resolves this directory's [`pyproject.toml`](pyproject.toml), including |
65 | | -the editable local `xy[reflex]` package. Open the URL printed by Reflex |
66 | | -(normally <http://localhost:3000>). No environment variables or external |
67 | | -services are required. |
68 | | - |
69 | | -## Architecture |
70 | | - |
71 | | -The `xy_reflex_demo` package is split by responsibility: |
72 | | - |
73 | | -- `data.py` defines typed instrument, quote, position, story, calendar, and |
74 | | - scenario models. Cached NumPy generators create three years of seeded daily |
75 | | - OHLCV as of the fixed date displayed in the app. |
76 | | -- `charts.py` contains pure data transforms and chart builders for all five |
77 | | - workspaces, including finance studies and drawings. |
78 | | -- `state.py` keeps only small UI selections and inputs in Reflex state. It |
79 | | - owns command routing, semantic chart events, paper-ticket validation, and |
80 | | - one guarded background quote loop. |
81 | | -- `components.py` composes the persistent terminal shell and responsive |
82 | | - workspace views; the package entry point registers the single page. |
83 | | - |
84 | | -State-dependent Security, Portfolio, and Risk charts use |
85 | | -`@reflex_xy.figure`. The first Markets panel is a direct, fixed-data |
86 | | -`xy.FinanceChart`, so the new finance surface is visible immediately rather |
87 | | -than only after a Security drilldown. Other fixed views exercise a direct |
88 | | -`xy.Chart` and the kernel-backed `reflex_xy.inline()` tier. The live pulse |
89 | | -starts with a figure token and receives compact points through |
90 | | -`reflex_xy.append()`. Hover and view-change events are handled as ordinary |
91 | | -Reflex events; there is no iframe or `postMessage` bridge. |
| 42 | +`uv run` resolves this directory's [`pyproject.toml`](pyproject.toml) |
| 43 | +(`xy[reflex]`) into a local environment. Open the URL Reflex prints (usually |
| 44 | +<http://localhost:3000>). Zoom into the cloud to drill density into exact |
| 45 | +points; box-select to cross-filter the histogram; press **go live** to stream. |
92 | 46 |
|
93 | | -The adapter is enabled by `reflex_xy.XYPlugin()` in |
94 | | -[`rxconfig.py`](rxconfig.py). Chart payloads travel through the app's XY |
95 | | -websocket namespace while Reflex state retains only lightweight selections |
96 | | -and token strings. |
97 | | - |
98 | | -## Paper ticket |
99 | | - |
100 | | -The Security ticket accepts side, entry, stop, target, account size, and risk |
101 | | -percentage. A valid setup updates the long/short chart overlay and displays |
102 | | -risk, quantity, and reward/risk metrics. Invalid ordering is explained inline |
103 | | -and suppresses the overlay. The button does not place or simulate an order. |
104 | | - |
105 | | -## Checks |
106 | | - |
107 | | -From the repository root, the focused test covers deterministic data, OHLC |
108 | | -invariants, portfolio/risk calculations, representative chart specs, linking |
109 | | -tiers, semantic events, and app composition: |
| 47 | +`XY_LIVE_POINTS` sets §6's point count — the same override the FastAPI app |
| 48 | +honors, so both apps build the identical dataset at any size. Unlike the |
| 49 | +FastAPI app (lazy, on first use) the columns are built at import, because |
| 50 | +`inline()` registers at module scope; the default 100M costs a few gigabytes |
| 51 | +of RAM and some startup seconds, so dial it down on small machines: |
110 | 52 |
|
111 | 53 | ```bash |
112 | | -uv run pytest tests/test_example_apps.py -q |
| 54 | +XY_LIVE_POINTS=1000000 uv run reflex run |
113 | 55 | ``` |
| 56 | + |
| 57 | +The adapter is wired in one line — `plugins=[reflex_xy.XYPlugin()]` in |
| 58 | +[`rxconfig.py`](rxconfig.py). |
| 59 | + |
| 60 | +## Interaction contract checks |
| 61 | + |
| 62 | +Section 1's badges are event counters, and its click/select handlers |
| 63 | +deliberately republish the cloud behind its stable token (the title's |
| 64 | +`handler revision`). Together they make the wrapper's restore contract |
| 65 | +manually verifiable: |
| 66 | + |
| 67 | +1. Box-select a large area. The `select` readout shows the exact total, the |
| 68 | + bounded JSON row count, and `truncated`; the §2 histogram cross-filters. |
| 69 | + The cloud must keep both its viewport and its selection highlight across |
| 70 | + the republish, and the selection counter must increment exactly once. |
| 71 | +2. Zoom until density drills into exact points, then click one. The `click` |
| 72 | + readout shows its canonical row ID, f64 data coordinates, and active |
| 73 | + keyboard modifiers; the click counter must increment exactly once. |
| 74 | +3. Focus a point and press Enter or Space. Keyboard activation must produce |
| 75 | + the same click readout contract as pointer activation. |
| 76 | +4. Clear the selection. The histogram returns to all points and the select |
| 77 | + counter increments exactly once again. |
| 78 | + |
| 79 | +A runaway counter or a viewport/selection reset after any of these reveals a |
| 80 | +republish feedback loop or a restore regression. |
0 commit comments