Michael ShimelesandClaude Opus 5.5 a12b5d14e4 feat(hl): built-in Turnkey wallets on testnet (#38)
* docs: design for Hyperliquid trading (crypto + HIP-3 stock perps)

Design only, no feature code. Covers custody (client-side API wallet
signing, recommended), agent approve/revoke flow, asset routing for
core/spot/HIP-3, order ticket, user streams for POS/ORD/ACTV, safety
rails, testnet-only test plan, phases, and open questions including the
Hyperliquid ToS restriction on Ontario.

* docs(hl-trading): plan the market key so HL perp rows reprice and trade

Greptile: market_key() returns None for Future and live_price/row
actions depend on it. Add Position.market and the portfolio_view work.

* docs(hl-trading): keep testnet and mainnet quotes in separate maps

Greptile: same market keys on both networks would let one overwrite the
other in the shared quote map. Plan a network-aware live_price and
mainnet-only gateway requests, with interleaved-update tests.

* feat(hl): add tr-hl, a testnet-only Hyperliquid signing and trading crate

tr-hl signs and sends Hyperliquid actions from the device. It covers
L1 phantom-agent signing and user-signed approveAgent, the order,
cancel, cancelByCloid, modify, updateLeverage, scheduleCancel and noop
actions, asset ids read from perpDexs and meta, tick and lot rounding,
info and exchange clients, a user-stream reducer and a nonce manager.

Only testnet exists in this crate. The endpoints are the testnet
hosts, the phantom agent source is always "b", hyperliquidChain is
always "Testnet", and Network has one variant. A test greps the crate
for mainnet hosts.

The caps are constants and the trader enforces them before it signs.
Orders are capped at $100 notional, leverage at 3x and market slippage
at 1%. The builder field is never sent. Signatures match the Python
SDK's published vectors, plus an approveAgent vector generated with
SDK 0.24.0. The crate builds for wasm32.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat: Hyperliquid testnet trading in the app

Hyperliquid joins LIVE next to SnapTrade, with its own opt-in in
Settings > Trading > Hyperliquid and an amber TESTNET badge on the
toolbar, the account, the ticket and every confirmation.

- Connect an address read-only. The account's positions, orders,
  fills and funding then show in PORT, POS, ORD and ACTV, from
  Hyperliquid's websocket and never through Terminal's server.
- Approve an agent on the desktop by pasting a throwaway testnet main
  key once. Terminal wipes it after signing and never stores it. In
  the browser the wallet signs instead (EIP-1193). The agent key lives
  in the macOS Keychain or the Linux Secret Service, and in browser
  memory or, by opt-in, IndexedDB under a non-extractable WebCrypto
  key. There is no file fallback.
- The ticket's Hyperliquid variant covers market (IOC, 1% cap), limit
  (GTC, IOC, post-only), stop loss and take profit, reduce-only and
  "Set 3x" leverage. HIP-3 rows read "PERP · HL xyz (perpetual, not a
  share)".
- Every order, cancel, cancel-all and leverage change asks for one
  confirmation. The worker re-reads the testnet mid and leverage and
  checks the caps again right before it signs.
- KILL and a button in ORD cancel every open order and turn
  Hyperliquid orders off. The dead man's switch is opt-in.
- Testnet rows price only from testnet mids. Position.market now
  carries the exact key, and live_price picks the map by network.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(hl): mask addresses in Hyperliquid messages, toasts and logs

Hyperliquid's errors name full addresses ("User or API Wallet 0x… does
not exist", "Must deposit… User: 0x…"). mask_addresses turns each one
into 0x1234…abcd. Exchange summaries, the worker's logs and every app
message that shows a Hyperliquid error now go through it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(hl): keep Hyperliquid out of SnapTrade's settings sections

The offscreen render showed the Hyperliquid account under SnapTrade's
Brokerage access, with SnapTrade's "reconnect with trading access" advice.
That section and the default-account picker now list SnapTrade accounts
only. Hyperliquid has its own section.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(hl-trading): record the v1 decisions and add screenshots and a wasm CI check

Section 15 of the design now records Micky's twelve decisions as
"Decided (2026-11-02)". The sections they change are updated: testnet
only, Secret Service with no file fallback on Linux, a pasted throwaway
key on the desktop for now, hard caps, an opt-in dead man's switch and
the kill switch. A new section 16 lists what v1 builds and what is still
missing.

docs/img/hl-*.png holds the before (origin/master) and after
screenshots. CI now builds and lints tr-hl for wasm32.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(hl): read the agent key from the store once per account

When the store had no key for the approved agent, the app asked again
on every frame, which meant a Keychain or Secret Service read per frame.
It now remembers the account it read and asks again only after a
connect, an approval or a revoke.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(hl): fix the kill switch, uncertain sends, approvals and cap valuation

Fixes from Greptile's first review of #37:

- Totals value a margined position (a perp) by its P&L, not its
  notional. Hyperliquid's cash is its collateral (equity less
  unrealised P&L), so a reprice moves equity only by the P&L change.
- KILL reads the open orders fresh on every dex, cancels them, and
  reads again (three rounds at most) before it reports what is left.
  Signed actions now run one at a time, and an order confirmed before
  a KILL that hasn't been signed yet is refused.
- An approval answer applies only to the account that asked for it,
  and Remove is disabled while one is in flight. The app also never
  signs with a key that isn't the approved agent's.
- An uncertain send blocks new orders until orderStatus finds its
  cloid, or until its 30 s expiresAfter window has passed without it.
- Every order needs a current mid before it is valued against the $100
  cap. Prices that round to zero are refused, and rounding zero no
  longer overflows.
- The keyring check runs in the worker, not on the UI thread.
- A cancel-all skips a market the registry doesn't know instead of
  failing, and the pasted key field's undo history is wiped once the
  key has signed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(hl): keep the dead man's switch armed while an order is unresolved

From Greptile's second review of #37: an order with no clear answer
blocked new sends through the same guard the dead man's switch reads,
so the switch cleared its scheduled cancel just when a possibly
accepted order needed it.

The guard is now split. Whether this device can sign arms the switch,
and allows cancels and leverage changes. Only new orders and modifies
also wait for an unresolved order. KILL always goes ahead.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(hl): size dollar amounts at the mid and let KILL skip stalled requests

Fixes from Greptile's fourth review of #37 (at f636b7c):

- A dollar amount on the Hyperliquid ticket (BUY xyz:INTC $10) became
  that many contracts. The ticket now sizes it at the testnet mid,
  rounded down to the market's size step. With no mid yet, or under one
  step, it stays a dollar amount and the ticket can't send it. Changing
  the market sizes the amount again.
- Every /info and /exchange request has its own 10 s deadline. The
  browser's fetch had none, so a stalled request could hold the
  worker's only turn forever.
- KILL no longer waits for that turn. It cancels at once, and an order
  confirmed before it gives up, even one still reading the market. If
  an order was already being sent, KILL cancels again once its answer
  comes, or once its expiresAfter has passed if the send got no clear
  answer. Single cancels and the dead man's switch don't wait for
  orders either. Orders, modifies and leverage changes still go one at
  a time, and an order with no clear answer still blocks new ones until
  it is found or expires.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(hl): size dollars for the market named now and keep timed cancels in order

Fixes from Greptile's review of f1ff1b3:

- The ticket reads its market at the start of a frame, before an edit
  to the market field. A dollar amount could then be sized at the old
  market's mid ($10 at a $2 mid is 5 units, worth $20 on a $4 market).
  settle_size now sizes only with the market the ticket names, so the
  next frame sizes it for the new one. The ticket's estimate also uses
  that market.
- Dead man's switch changes ran side by side, so a slow clear could
  land after a newer schedule and remove it. They now go out one at a
  time in the order the app made them, on a lock of their own that
  never waits for orders. A change that reaches the worker after a
  newer one is dropped. After a change with no clear answer, the next
  waits until it can no longer land.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(hl): give each dead man's switch refresh a fresh deadline

From Greptile's review of 2f7cab6: after a change with no clear answer,
every later change waited until that one could no longer land. Refreshes
queue every 30 s, so with lost replies they went out every 35 s or so,
each with the deadline it got when queued, and the timer could fire
while Terminal was still running.

- ScheduleCancel now carries how far ahead to cancel. The worker adds
  it to the time of signing, so a refresh that waited still gets a
  fresh deadline.
- A newer change replaces one still waiting for its turn, so a backlog
  collapses into the newest.
- Only a change of the other kind waits out a lost one: a timer after a
  clear, or a clear after a timer. A refresh that lands late only moves
  the timer back by one refresh, so refreshes don't wait for each other.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(hl): make every dead man's switch change wait out a lost one

From Greptile's review of 73b71b8: letting a refresh go out before an
earlier timer with a lost reply could no longer land was not safe. That
timer could land last (the clocks may differ by seconds) and pull the
deadline in, so the switch could fire while Terminal was running.

Every change now waits until a lost earlier change can no longer land,
whichever kind either one is. That is safe now that each change is
signed, with its deadline, when it goes out, and a newer change
replaces any still waiting, so a backlog can't build up stale
deadlines.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(hl): sign approveAgent through a built-in Turnkey wallet, testnet only

Add tr-turnkey, the client side of Terminal's built-in wallets. The device
makes a P-256 key, and every Turnkey request carries a stamp made with it.
The session token Turnkey issues for that key lasts 15 minutes, and Terminal
accepts it only if it is bound to its own key. The client reads whoami and
the wallet's Ethereum account, and signs one digest with the request
alloy-signer-turnkey sends (SIGN_RAW_PAYLOAD_V2, hex payload, no hashing).
On the desktop the browser returns the session to a listener on 127.0.0.1
that checks a 256-bit state. A mock feature adds a local stand-in for
Turnkey's API and sign-in page for tests and screenshots.

tr-hl gets sign_approve_agent_with, the only way a remote wallet's
signature enters the crate. The signer sees only the digest of a testnet
approveAgent, and the answer is used only if it recovers the wallet.
approve_agent_hash now also refuses every signatureChainId except 0x66eee.

The mock wallet holds the Python SDK's public test key and reproduces the
SDK's approveAgent vector. Mainnet and 0xa4b1 approvals never reach the
wallet, and the new code names no mainnet host.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs(hl): design built-in Turnkey wallets for testnet trading

Record Micky's decision of 2026-11-04 in §15: built-in wallets only, no
browser wallet and no pasted key. The new §4.5 covers who holds which key,
the browser sign-in that hands a session to a key made on the device, why
a loopback redirect beats a terminal:// link, what Turnkey signs and how
often, and the export through Turnkey's iframe. It explains why no
Terminal server needs a Turnkey key (the Auth Proxy holds the one that
creates sub-organizations) and sketches a scoped signup function as an
unbuilt fallback. Agents now last 180 days by default, since each approval
is a paid Turnkey signature.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(hl): replace the browser wallet and pasted key with the built-in wallet

Settings › Trading › Hyperliquid now starts with "Create Wallet / Sign In".
On the desktop it opens Terminal's sign-in page in the system browser and
waits on a 127.0.0.1 loopback for the session; the web build opens the page
in a popup and takes its postMessage. Terminal reads the wallet's address
from Turnkey with requests stamped by its own key, keeps the 15-minute
session in the OS secret store (memory on the web) and deletes it once it
expires.

"Approve Trading Key" makes the agent key on the device and has the wallet
sign approveAgent through Turnkey, signing in first if there is no session.
Revoke goes the same way. Agents now last 180 days by default, since each
approval is a paid Turnkey signature. Orders are still signed only by the
agent key.

Deposit shows the address in full with a QR code and a copy button, the
testnet perp account value and withdrawable from the stream, and spot USDC
(new spotClearinghouseState read). Back Up Wallet warns, then opens the
page's export mode.

The web's EIP-1193 Connect Wallet and the desktop's pasted testnet key are
gone. MainKey is left for tests and the testnet_check example. Watching an
address read-only stays.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* feat(site): add the wallet sign-in and backup page

/wallet/ signs the user in with an email code or a passkey through
@turnkey/core and Turnkey's Auth Proxy. A new user gets a sub-organization
with one Ethereum wallet. The page then calls stamp_login for the app's
public key, so the app gets a 15-minute session only its own key can use,
and logs itself out. Desktop answers go to the loopback port, web answers
to the opener with postMessage on the same origin.

Its parameters come from the URL fragment, which never reaches a server,
analytics or a Referer, and the page clears it on load. In export mode
the page shows the recovery phrase in Turnkey's export iframe, so the
phrase never touches the page's code or any server.

It needs two public IDs in the Vercel project env and says so until they
are set. @turnkey/core is pinned to 2.13.1 and @turnkey/iframe-stamper to
2.11.1.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* ci: build and lint tr-turnkey for wasm32

The browser build uses the wallet crate too, so check it the way tr-hl is
checked.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(hl): keep the trading key when the wallet doesn't sign a revoke

Revoke used to forget the agent before Turnkey signed anything, so a failed
or expired sign-in left the app without its agent while the message said
nothing changed. Orders now turn off at once, and the agent is forgotten
only when the revoke goes through. An approval the wallet didn't sign now
says so instead of reading as Hyperliquid's refusal. The mock-URL warnings
no longer print the URL.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(hl): link the sign-in page at /wallet, its canonical URL

The site serves the page at /wallet and redirects /wallet/ there. Browsers
keep the fragment across that redirect, but there is no reason to rely on
it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(site): only sign in, never sign up, when backing up a wallet

Export mode hid nothing: it offered "Create with a passkey", and the email
code path would create a new wallet for an unknown address. Backing up now
logs in to an existing wallet only.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* docs: add before and after screenshots for built-in wallets

Desktop shots are offscreen renders of the running app against a local
Turnkey stand-in. Web shots come from headless Chromium on a local build.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(hl): keep sign-in answers and wallet actions on the right wallet

From Greptile on #38:

- Removing the wallet reset the request counter, so a cancelled sign-in's
  late answer could carry the next sign-in's id and end it. Ids now keep
  counting, and removing the wallet cancels a sign-in in progress.
- A revoke or approval that needed a sign-in acted on whichever wallet the
  user signed in to. The sign-in now remembers the wallet the action is
  for, and refuses another one without changing anything.
- The browser build's sign-in never timed out. Both builds now give up
  after 10 minutes, the limit the waiting line shows.
- The sign-in page stayed on "Connecting your wallet" if Turnkey didn't
  issue the device's session. It now goes back to the sign-in controls
  with the error, so the user can retry or cancel.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-04 08:38:08 -05:00

Terminal

A fast, keyboard-driven market terminal in pure Rust: TradingView-class GPU charting with a Bloomberg Terminal look (dense tiled panes, monospace numbers, a command line like AAPL <GO>) across stocks, ETFs, indices, FX, crypto, US Treasuries, futures/commodities and options, plus congressional trade tracking ingested directly from the official STOCK Act disclosure systems and your own brokerage accounts through SnapTrade (read-only by default, with opt-in order entry). Native compact UI, themes from Bloomberg amber to Solarized, and a fully dockable workspace with saved layouts.

Terminal: AAPL chart with congress trade markers and RSI, the Congress trades feed, world markets and a multi-asset watchlist

Built from reverse-engineering research. Chart UX was studied behaviourally on tradingview.com with REA in website mode (passive captures of the public, logged-out chart page), then given a Bloomberg Terminal look. Findings, evidence IDs and their design implications are in docs/analysis/. Only behaviour was studied: no TradingView or Bloomberg code, scripts, icons, fonts or other assets are used or included here. The disclosure parsers were likewise built by examining the public Senate eFD and House Clerk sites and their filings.

Screenshots

Bloomberg theme: NVDA chart with congress markers and Nancy Pelosi's trade history with estimated returns Options chain (OMON) for AAPL with bid/ask, IV and delta per strike beside the chart
Bloomberg theme — NVDA, POL PELOSI: party-coloured B/S markers and a politician profile with estimated return, hit rate and disclosure lag Options — AAPL OMON: every expiry, calls and puts by strike, IV and greeks, spot line
Terminal Light theme with the latest congressional trades feed Nord theme, sidebar on the right, dragging a Watchlist tab onto the chart pane's dock compass
Terminal Light — the latest STOCK Act filings with party, chamber, owner and amount filters Docking (Nord theme, sidebar on the right) — drag a tab onto the compass to tab or split
Live theme editor: Monokai applied to the chart while its colours are shown in Settings Theme gallery in Settings › Appearance Congress marker hover card with disclosure lag
Theme editor — edit any colour, font or density live; save, duplicate, import/export TOML or JSON Theme gallery and the marker hover card (politician, amount, traded/filed, disclosure lag)
Portfolio summary: total value, unrealised P&L, cash, buying power, allocation bar, top holdings and accounts beside an AAPL chart Positions table with quantity, live last price, average cost, value and unrealised P&L
Portfolio — PORT: value, unrealised P&L, cash and buying power, allocation, top holdings, accounts (simulated sample shown) Positions — POS: live terminal quotes (cyan) reprice holdings; click a row to chart it

Screenshots are from the Linux build under Xvfb with live data (October 2026); on macOS the same UI gets the native title bar with traffic lights, window vibrancy behind the sidebar and SF fonts.

Features

  • Multi-asset with Bloomberg-style market keys — AAPL <EQUITY>, SPY <EQUITY>, SPX <INDEX>, BTCUSD <CRNCY>, EURUSD <CRNCY>, US10Y <GOVT>, CL1 <CMDTY> (keys display as AAPL US Equity, BTCUSD Curncy, US10Y Govt, CL1 Comdty). The yellow key is optional for known tickers. A built-in catalog covers ~120 instruments (regional indices, equity index futures, energy/metals/grains, the full Treasury curve, majors and crosses, top crypto, popular ETFs and large caps); anything else resolves by convention.

  • Command line and search — <SECURITY> [<FUNCTION>] <GO> (Enter is <GO>); typing anywhere starts a command, with a live interpretation underneath (5 → "Interval 5 minutes") and symbol matches across all asset classes (catalog instantly, plus Yahoo search after a short pause); ↑/↓, Tab, Enter; history with ↑/↓. SECF gold cmdty opens a full search panel.

  • Charts — GPU-rendered candles, OHLC bars, line and area; 1m, 5m, 15m, 30m, 1H, 4H, 1D, 1W, 1M plus quick ranges (1D … ALL); volume; 151 indicators; CMP <SYM> compare; log scale; crosshair with OHLC legend; smooth pan and zoom over a million bars. Live crypto bars from the Binance websocket (Coinbase fallback).

  • Indicators — 151 built-in studies: 23 moving averages (SMA, EMA, WMA, Hull, DEMA/TEMA, KAMA, ALMA, T3, McGinley, VIDYA, FRAMA, MAMA/FAMA, Ehlers filters, GMMA…), 8 bands and channels (Bollinger, Keltner, Donchian, STARC, standard-error and VWAP bands…), 43 momentum oscillators (RSI, MACD, Stochastic, StochRSI, CCI, Williams %R, TSI, KST, Fisher, Schaff, QQE, WaveTrend, Connors RSI…), 22 trend tools (ADX/DMI, Aroon, Vortex, Parabolic SAR, SuperTrend, Ichimoku, Alligator, ZigZag, fractals, Chandelier and Chande Kroll stops…), 16 volatility measures (ATR, close-to-close and Parkinson, Garman-Klass, Rogers-Satchell and Yang-Zhang estimators, Ulcer, Mass index, squeeze momentum…), 23 volume studies (OBV, A/D, CMF, MFI, Klinger, NVI/PVI, VZO…), 11 rolling statistics and five pivot-point families. Type a code with optional parameters (RSI, SMA 200, BB 20 2.5), browse and search with IND, or right-click the chart. Oscillators get their own panes (up to six) with reference levels; moving averages can be added several times; indicators are saved with the layout.

  • Drawings — trendline (Alt+T), horizontal (Alt+H), vertical (Alt+V) and fib retracement (Alt+F), stored in (time, price) so they survive zoom and timeframe changes; magnet (Alt+M) snaps to OHLC; click to select, Del to remove.

  • Options (AAPL OMON, ⌘⇧O) — full chains from Yahoo for US equities, ETFs and indices (SPX via ^SPX): every expiration, calls and puts by strike with bid/ask/last, change, volume, open interest and IV, plus Black-Scholes greeks (Δ, Γ, Θ/day, vega) from each contract's IV, the dividend yield and the 3-month T-bill rate; in-the-money shading, spot line, ±10/±20/all strikes; offline mode shows a flagged simulated chain.

  • Markets — WEI world markets (Americas/EMEA/Asia-Pacific indices, futures, Treasuries, FX, crypto and commodities with sparklines), GC US Treasury yield curve (now vs 1M and 1Y ago, 2s10s / 3m10y / 5s30s), news (AAPL CN; Yahoo headlines, CoinDesk for crypto) and order book depth (BTCUSD CRNCY OB; live Binance book, Coinbase fallback; markets without a free public book show a clearly marked simulation).

  • Market data behind a trait (tr_data::MarketData), routed per asset class to free sources:

    Asset class History Quotes Live
    Crypto Binance public REST (data-api.binance.vision, then api.binance.com), Coinbase Exchange fallback Binance 24h ticker Binance kline websocket, Coinbase ticker fallback
    US Treasuries Treasury.gov daily par yield curve CSV (FRED fallback); Yahoo ^IRX/^FVX/^TNX/^TYX intraday Treasury.gov —
    FX, indices, futures Yahoo Finance (EURUSD=X, ^GSPC, CL=F) Yahoo spark (batched) —
    Stocks, ETFs Alpaca when APCA_API_KEY_ID/APCA_API_SECRET_KEY are set, else Yahoo Yahoo spark —
    Hyperliquid HIP-3 perps (xyz:AAPL, xyz:GOLD, xyz:EUR) The Terminal data server only (TR_SERVER_URL) Server mids Server candles, book and trades

    A HIP-3 perp is its own instrument, labelled PERP · HL xyz everywhere. AAPL US Equity stays the listed stock. AAPL PERP opens the perp linked to it, and PERP or ⇧P switches back and forth. A perp has no other source, so it never gets sample data; without the server it shows why.

    No keys are required. TR_PROVIDER=sample runs fully offline on deterministic, class-aware sample data (BTCUSD near 65,000, EURUSD near 1.09, US10Y near 4.2%) with simulated live ticks, and the live router falls back to sample data when a source is unreachable. Bars are cached in SQLite so charts paint from disk on start. Older history pages in as you scroll left.

  • Congress trades (key feature)

    • Ingests Periodic Transaction Reports from the Senate eFD (efdsearch.senate.gov: agreement
      • CSRF session, DataTables search API, HTML transaction tables) and the House Clerk (disclosures-clerk.house.gov: yearly {year}FD.zip XML index, PTR PDFs parsed with pure-Rust text extraction). Scanned paper filings are detected and recorded as such.
    • Normalises politician, party, chamber, state, ticker, buy/sell, amount range, transaction date and filed date into SQLite. Party comes from the official rosters (clerk.house.gov MemberData.xml, senate.gov senators_cfm.xml); politicians are keyed by bioguide ID, which also repairs typos in the source index.
    • Refreshed on a schedule (every 30 minutes by default, TR_CONGRESS_REFRESH_MIN, 0 disables; REFRESH/F6 forces one), incrementally — only unseen filings are fetched.
    • Panels: latest trades feed with party/chamber/side filters (CONG); per-ticker "who in Congress traded it" (NVDA CONG, or right-click any ticker); politician profile with trade history and estimated performance (amount-weighted directional return, hit rate, average disclosure lag) (POL PELOSI); directory (POL).
    • Chart overlay: B/S markers at transaction dates coloured by party (blue D, red R, purple I); hover for a card with politician, amount, owner, traded/filed dates and the disclosure lag.
    • Alerts: TRACK <name> (or ☆ in the directory/profile); when a tracked politician files a new PTR you get a toast, an ALERTS n badge and an entry in ALERTS.
  • Dockable workspace and saved layouts

    • Any panel (chart, watchlist, news, order book, world markets, yield curve, options, Congress feed, politicians, alerts, search, settings, help) docks anywhere: drag a tab (or a pane's header) onto another pane and drop on the compass (centre adds a tab, arrows split), with a ghost preview and a labelled landing highlight, or drag to a window edge. Resize with dividers (double-click to equalise), tabs in every pane, grid presets (LAYOUT 1|2|2H|3|4|6 or the toolbar), and pop any pane out into its own native window (close it to dock back). Add panels from each pane's + menu, NEW <PANEL> / SPLIT [DOWN] <PANEL> or ⌘1…⌘0.
    • Link groups: give panes a colour (LINK BLUE, or the tab menu) and one security change drives every linked chart, order book, news, options and per-ticker Congress pane; clicking a row in a linked watchlist or world-markets pane loads it into the group.
    • Saved layouts: save, name, rename, duplicate, delete and switch (LAYOUT <name>, the sidebar list, ⌥1…⌥9); the active layout autosaves and is restored on launch. Each layout is a JSON file in ~/Library/Application Support/terminal/layouts ($XDG_DATA_HOME on Linux) and can be imported or exported (LAYOUT EXPORT/IMPORT, or Settings › Layouts). Per-pane settings (chart type, indicators, drawings, filters, depth, link, tab title) and, optionally, the theme (LAYOUT THEME ON) save with the layout.
  • Themes — built-in Terminal Dark, Terminal Light, Bloomberg (amber on black, monospace, compact), High Contrast, Solarized Dark/Light, Nord and Monokai. A theme sets every colour (backgrounds, panels, text, accent, up/down, candles, grid, series), the interface and number fonts and size, and density (compact, regular, comfortable). Pick one from the gallery in Settings › Appearance, the View menu, THEME <name> (prefix match), THEME NEXT|PREV|LIST or ⌘⌥T; THEME SYSTEM follows the OS with a chosen dark and light theme. Settings › Themes is a live editor: editing a built-in makes a copy (built-ins stay read-only); duplicate, rename, delete, import or export as TOML or JSON. Custom themes are TOML files in the themes folder next to layouts; a partial file inherits everything else from Terminal Dark or Light.

  • Native, compact UI — a macOS unified compact toolbar with the traffic lights inset, a translucent source-list sidebar with window vibrancy (NSVisualEffectView via objc2, in crates/macos) that can sit on either side (SIDEBAR LEFT|RIGHT, ⌘⌥S; the toolbar toggle moves and mirrors with it, right-click it for the View menu), SF Pro for chrome and SF Mono for numbers, segmented controls, spring animations, hover states and context menus. The System theme follows the OS appearance and accent colour; charts render at native retina scale with 4x MSAA.

  • Brokerage accounts (SnapTrade) — connect your brokers once through SnapTrade's Connection Portal (opened in your browser) and the terminal shows real accounts and balances (ACCT), positions with unrealised P&L (POS), orders from the last 30 days (ORD), activity such as trades, dividends, interest and transfers (ACTV) and a portfolio summary with allocation (PORT). Holdings are repriced with the terminal's own live quotes; clicking a position, order or activity row charts it, and right-click offers the option chain, news, Congress trades and watchlist. AAPL POS / AAPL ORD filter to one security, the account picker to one account. Stocks, ETFs, funds, crypto and options (OCC symbols shown as AAPL 350C) are supported. Read-only unless you turn on trading (below); Connect Brokerage requests read-only access. Without credentials the panes show a setup screen; offline (TR_PROVIDER=sample or TR_BROKER=sample) they show a clearly labelled simulated portfolio. See Brokerage setup.

  • Demo account and easy trading — a DEMO paper-trading account ($100,000 virtual cash) is selected by default; the toolbar badge shows DEMO or a red LIVE and switches between them (switching to live asks first and is remembered). Buy/Sell buttons sit on charts, watchlist rows and positions (plus Close), ⇧B / ⇧S trade the security in focus, and BUY AAPL 10, SELL ALL TSLA, BUY NVDA $500, SELL MSFT 5 LMT 410 work from the command line or ⌘K. One compact ticket: side, shares or dollars, market/limit (stops under More), live estimated cost against buying power. Demo orders fill in one click at the live quote (limits and stops when the quote crosses) and persist in the app data folder; Settings › Trading resets the account. Live orders stay opt-in and go through SnapTrade's preview and a single confirmation. See Trading.

  • Command palette — ⌘K (Ctrl+K on Linux and Windows) opens a centred, theme-matched palette that fuzzy-searches every function code (PORT, POS, ORD, OMON, THEME, …), tickers (load the quote and chart), new panels, layout presets and saved layouts, themes, Settings tabs and workspace actions (move the sidebar, open Brokerage settings). Each row shows its shortcut; recent picks come first. ↑↓ select, ↵ open, Esc close.

  • Hotkeys and preferences — every hotkey is rebindable from a native shortcut table in Settings › Hotkeys and Help (click a binding, press the new keys); defaults for new charts and order books; everything persists in prefs.json.

Running

cargo run --release                  # Yahoo data, congress ingest in the background
TR_PROVIDER=sample cargo run --release   # offline
APCA_API_KEY_ID=… APCA_API_SECRET_KEY=… cargo run --release   # Alpaca

Data lives in ~/Library/Application Support/terminal (macOS) or $XDG_DATA_HOME/terminal (Linux), or TR_DATA_DIR. Versions from before the project was renamed used a terminal-rs folder in the same place; the first launch moves it to terminal and leaves a link at the old path, so builds that still use the old name find the same files. The first congress ingest covers the current and previous year (a few minutes; it runs in the background while you work). cargo run -p tr-congress --example ingest -- congress.sqlite3 runs one ingest from the CLI.

Appearance: TR_THEME=light|dark|<theme name> overrides the saved theme, TR_MSAA=1|2|4|8 sets chart anti-aliasing (default 4 on macOS), TR_VIBRANCY=0 disables the translucent sidebar, and ⌃⌘S (Ctrl+Shift+S elsewhere) toggles the sidebar. scripts/bundle-macos.sh builds a retina-capable Terminal.app (icon from scripts/build-icon.sh); scripts/release-macos.sh builds the universal (Apple Silicon + Intel) app, signs it with a Developer ID certificate, notarizes it and packages Terminal.dmg, plus the zip and signed appcast.xml that installed copies update from (one-time credential setup is at the top of the script). The app checks that feed once a day through Sparkle and offers to install a newer release; Terminal › Check for Updates… or UPDATE checks at once. scripts/check-macos-cross.sh compiles and lints the whole workspace for aarch64-apple-darwin from Linux (zig as the C cross-compiler; linking needs the macOS SDK).

In the browser

The same code also builds for WebAssembly and runs in a browser tab on WebGPU:

rustup target add wasm32-unknown-unknown
cargo install wasm-bindgen-cli --version 0.2.129 --locked   # must match Cargo.lock
brew install binaryen                                       # wasm-opt
scripts/build-web.sh                                        # writes site/static/app
cd site && npm run dev                                      # then open http://localhost:5173/app/index.html

scripts/build-web.sh <dir> writes somewhere else instead; any static file server works. Add ?sample to the URL to run offline on sample data, or ?fps for the frame-time readout. Every push to master builds it and deploys it with the site to terminal.rasmic.xyz/app. Pull requests get a preview; site/README.md covers the workflow and the secrets it needs.

The browser build in Chrome: a live BTCUSD chart from Binance beside a demo-account position

What the browser build can and can't do yet:

  • Crypto is live, because Binance and Coinbase allow cross-origin requests. History, quotes, order books and websocket bars all come from them. Yahoo, Treasury.gov, Alpaca and CoinDesk don't allow them, so every other market shows sample data and the toolbar says "Crypto live". A same-origin proxy can fix that later.
  • HIP-3 perps (xyz:AAPL) are live through the Terminal data server the page is built with. They never fall back to sample data: while the server is unreachable they say so.
  • Congress disclosures, SnapTrade accounts and live order entry need the desktop app. The paper trading demo account works.
  • Layouts, settings, themes and the demo account are saved in localStorage under terminal/. The first load copies anything saved under the old terminal-rs/ prefix, without overwriting newer entries.
  • Charts need WebGPU, since they read bars from storage buffers in the vertex shader. Browsers without a WebGPU adapter get a second, larger build on WebGL2 where everything except the chart series works.

Measured with Playwright driving Chrome 155 on an Apple M4 Pro:

WebGPU build WebGL2 build
.wasm 5.60 MB (2.26 MB gzip, 1.69 MB brotli) 7.48 MB (2.99 MB gzip, 2.22 MB brotli)
JS glue 115 KB 169 KB
  • A cold load from localhost draws its first frame 91 to 97 ms after navigation, the medians of three runs of 9 loads, two headless and one in a Retina window. On a 50 Mbit/s link with 40 ms of latency it takes 446 ms, and 1.64 s on 10 Mbit/s with 80 ms.
  • Panning and zooming a chart costs 1.3 to 1.5 ms of CPU in the UI code and 2.4 to 3.3 ms for the whole frame (medians across runs), and holds the display's 60 Hz.
  • egui's two emoji fonts, 740 KB together, load after the first frame instead of inside the .wasm.

Brokerage setup (SnapTrade)

Everything happens in Settings › Brokerage, with no commands needed. Open it from the sidebar (Portfolio › Brokerage Settings), the toolbar's View menu (Brokerage Accounts…), the gear button on any portfolio pane, or the setup screen; PORT SETUP still works too.

  1. Create API keys (a client ID and consumer key) in the SnapTrade dashboard. Personal keys work too.
  2. Enter the Client ID and Consumer Key (masked; Show secrets reveals them) and click Save.
  3. Click Register User. This creates a SnapTrade user for this installation and saves its user secret, which SnapTrade only returns once. With personal keys, leave the user fields empty.
  4. Click Connect Brokerage…. SnapTrade's Connection Portal opens in your browser with read-only access; sign in to your broker there. New accounts appear within a minute.

The status card at the top always says where you are and what to do next ("Not set up", "Connected · 3 accounts at 2 brokerages · synced 14:32", or the API's error). Test Connection reloads accounts and connections on demand. Connected brokerages lists every connection with its accounts; a connection the broker has expired shows Reconnect…, which re-authenticates it in the portal, and Disconnect removes a connection and its accounts from SnapTrade after an explicit confirmation.

Where credentials live. On macOS the client ID, consumer key, user ID and user secret are stored together in the Keychain (generic password, service terminal, account snaptrade); credentials saved under the old service, terminal-rs, move there the first time the app reads them. Elsewhere they go in <data dir>/secrets/snaptrade.json with owner-only permissions (0600 in a 0700 directory). They are never written to the repository, layouts, prefs or logs; request errors are scrubbed of the URL (which carries the user secret), and the settings screen masks secrets. Forget Credentials removes them. SNAPTRADE_CLIENT_ID, SNAPTRADE_CONSUMER_KEY, SNAPTRADE_USER_ID and SNAPTRADE_USER_SECRET override the saved values field by field (useful for CI or a shell profile), and TR_SECRET_STORE=file uses the file store on macOS.

How it talks to SnapTrade. Requests go to https://api.snaptrade.com and are signed as the SnapTrade docs specify: clientId and a Unix timestamp in the query, and a Signature header holding a base64 HMAC-SHA256 (keyed with the consumer key) of the canonical JSON {"content","path","query"}. Portfolios refresh every two minutes while a portfolio pane is visible (every 20 s for five minutes after opening the portal); PORT REFRESH reloads on demand. Brokerage data can be delayed or cached by SnapTrade depending on your plan, which is why holdings are repriced with the terminal's live quotes where a market key exists.

Trading (opt-in)

The demo account needs no setup: it is a local paper-trading account (demo-account.json in the app data folder) with no commissions, no short selling and Day orders expiring at the 4 p.m. New York close. Switch to your brokerage accounts with the toolbar badge, MODE LIVE, or Settings › Trading (DEMO switches back).

Live trading is off until you enable it in Settings › Trading, which asks you to acknowledge that confirmed orders are real. Then:

  1. Make sure the connection allows trading. Connections made with Connect Brokerage are read-only; Reconnect with Trading Access… in Settings › Trading opens the Connection Portal asking for trading access where the broker supports it (trade-if-available). Settings › Trading lists, per brokerage, whether trading is available, from SnapTrade's allows_trading flag and each connection's access type.
  2. Open a ticket (BUY AAPL 10, a Buy/Sell button, ⇧B / ⇧S), pick the account and order type, and click Review. The broker checks the order through SnapTrade's order impact endpoint; nothing is placed.
  3. The broker's estimate opens one confirmation dialog. Send places exactly the previewed trade (POST /trade/{tradeId}). Editing the ticket after previewing, or waiting past the preview's validity (SnapTrade expires it after five minutes), requires a new preview. A preview is used up when it is sent. If the answer is lost (network error, timeout, 5xx), the ticket reports the status as unknown and opens ORD, because the broker may have accepted the order; only a 4xx is reported as a rejection.

Which order types a broker accepts varies and SnapTrade does not publish it per broker, so the preview is where unsupported combinations are rejected. Dollar amounts work only for market/day orders at brokers that support notional orders; fractional share quantities are refused up front at brokers SnapTrade reports as not supporting them. Settings › Trading also has an optional maximum order value and a default account.

Trading access is read once per brokerage connection (/authorizations), cached for an hour (on disk too) with a Refresh button. SnapTrade requests are limited to four at a time; a rate-limited read waits for Retry-After and retries, and a rate limit is shown as a quiet "retrying" note rather than an error. Orders are never retried automatically.

Tests never place real orders: the trading client is exercised only against a local mock server, and the app's flows against the simulated portfolio and the demo account.

Keyboard shortcuts and commands

Press F1 (or type HELP) for the full, searchable list; every hotkey can be rebound in Settings › Hotkeys. On Linux and Windows ⌘ is Ctrl and ⌥ is Alt.

Keys Action
⌘K (Ctrl+K) command palette: fuzzy search over commands, tickers, panels, layouts, themes, settings and actions
F1 · F2 · F3 · F4 · F5 · F6 · F12 help · Congress feed · politicians · alerts · watchlist · refresh filings · frame timing
F7 · F8 · F9 brokerage portfolio · positions · orders
Tab · ⇧Tab next · previous pane
⌘1 … ⌘0 · ⌘⇧O new chart, watchlist, news, order book, world markets, yield curve, Congress, politicians, alerts, search pane · options chain
⌘D · ⌘⇧D · ⌘T · ⌘W · ⌘⇧P split right · split down · new tab · close pane · pop out
⌘S · ⌥1 … ⌥9 save layout · switch to saved layout 1–9
⌃⌘S (Ctrl+Shift+S) · ⌘⌥S toggle sidebar · move sidebar to the other side
⌘⌥T · ⌘, cycle theme · settings
⌥T · ⌥H · ⌥V · ⌥F · ⌥M draw trendline · horizontal · vertical · fib · magnet
⌥R · ⌥L · Esc · Del reset chart view · log scale · cancel · delete selected drawing
wheel · drag · drag axes · double-click zoom · pan · stretch · reset
Command Action
AAPL · AAPL US EQUITY <GO> load a security in the focused chart
BTCUSD <CRNCY> · EURUSD <CRNCY> · US10Y <GOVT> · CL1 <CMDTY> · SPX <INDEX> other asset classes
5 1H D 1W 1M · YTD 5Y ALL interval · quick range
RSI MACD SMA 200 BB 20 2.5 · IND IND VOLUME · CLEAR toggle any of the 151 indicators (optional parameters) · indicator picker · remove all
CMP MSFT · LOG · CANDLE BAR LINE AREA compare · log scale · chart type
AAPL OMON · AAPL CN · BTCUSD CRNCY OB · WEI · GC · SECF <query> options · news · order book · world markets · yield curve · search
CONG · NVDA CONG · POL PELOSI · TRACK PELOSI · ALERTS · REFRESH Congress feed · who traded it · profile · track · alerts · refresh
NEW BOOK · SPLIT DOWN NEWS · POP · LINK GREEN · TITLE Desk · CLOSE add, split, pop out, link, rename, close panes
LAYOUT 4 · LAYOUT SAVE Macro · LAYOUT Macro · LAYOUT RENAME Rates · LAYOUT DUP · LAYOUT DEL · LAYOUT EXPORT grid preset · saved layouts
THEME BLOOMBERG · THEME NEXT · THEME SYSTEM · THEME EDIT · THEME COMPACT · LAYOUT THEME ON themes
PORT · ACCT · POS · ORD · ACTV · AAPL POS · AAPL ORD brokerage portfolio · accounts and balances · positions · orders · activity · one security
PORT REFRESH · PORT CONNECT · PORT SETUP (BROKER) reload accounts · connect a broker in the browser · SnapTrade keys
BUY AAPL 10 · SELL ALL TSLA · SELL MSFT 5 LMT 410 · SELL TSLA 3 STP 250 LMT 245 · BUY NVDA $500 · TRADE order ticket, prefilled (demo: one click; live: review and confirm)
DEMO · MODE LIVE · ⇧B · ⇧S switch account mode · buy / sell the security in focus
SIDEBAR RIGHT · SIDEBAR · PREFS · HELP sidebar side or visibility · settings · help
right-click chart / watchlist / feed row / sidebar toggle context menu (chart type, indicators, draw, add to watchlist, who traded it, View menu)

Architecture

app/            eframe shell: command line, docked panes, chart widget, panels
  dock.rs       docking tree (splits + tab groups): insert/move/remove, layout, resize, presets
  layouts.rs    saved layout documents (JSON) and the layouts folder store
  prefs.rs      appearance, keymap and panel defaults (prefs.json)
  themes.rs     theme model, built-in themes, TOML/JSON theme files and the themes folder
  app/          workspace (drag and drop, pop-out windows, link groups), panels, settings, chrome
  worker.rs     tokio runtime: all network, SQLite, pyramid and indicator work (UI never blocks)
  storage.rs    saved files: the data folder on desktop, localStorage in the browser
  web.rs        browser entry point (eframe web runner), console logging, frame timings
  web/          the page that hosts the browser build
crates/core     Bar/Series, timeframes, market keys + security catalog, indicators and their
                registry (codes, parameters, panes, levels), sample data
crates/data     MarketData trait; per-class router (Binance/Coinbase, Treasury.gov/FRED, Yahoo,
                Alpaca), websocket live feeds, sample provider with simulated ticks; bar cache;
                the browser router (crypto live, the rest sampled) and a tokio/browser task shim
crates/chart    wgpu renderer, LOD pyramids, viewport math, axes, drawings, egui paint callback
crates/congress Senate eFD + House Clerk parsers, rosters, SQLite store, refresh, alerts, performance
crates/broker   SnapTrade client (request signing, accounts, balances, positions, orders, activity),
                order entry (impact preview, place, cancel, capabilities), portfolio model and P&L,
                Keychain / owner-only credential store, simulated sample and order simulator
docs/analysis   REA research notes on TradingView chart UX

Rendering. Bars are uploaded once per data version into a storage buffer as a level-of-detail pyramid (level k merges 2^k bars), and geometry is generated in the vertex shader (one instance per bar or line segment). Each frame picks the level that keeps instances at ≥ 2 px, so the work per frame is bounded by the plot width, not the series length: a 1M-bar chart fully zoomed out draws about 800 instances. Pan and zoom only rewrite a few hundred bytes of uniforms; bar indices are split into integer and fraction for f32 precision at millions of bars. Axes, legend, crosshair, drawings and markers are cheap egui shapes.

Threading. The UI thread sends requests to the worker and drains responses each frame; history fetches, pyramid building (≈ 200 ms for 1M bars), indicators, SQLite queries and the congress ingest all run on tokio tasks. The worker owns each chart's bar series, so initial history, paging and live websocket ticks merge into one copy; live updates are coalesced to at most four GPU re-uploads a second.

Performance

Measured on Linux with Mesa llvmpipe (a CPU software rasterizer) — a real GPU is much faster:

  • Cold start: window and GPU ready in 143 ms; charts paint from the SQLite cache as soon as the worker answers.

  • 1M-bar pan/zoom (cargo run --release -p tr-chart --example framebench, 1600×900, candles + volume + 2 overlays, 300 frames per phase):

    phase CPU per frame p50 / p99 until GPU done p50 / p99
    zoom out to all 1M bars 0.07 / 0.20 ms 3.09 / 5.27 ms
    pan across history 0.06 / 0.18 ms 1.40 / 3.93 ms
    zoom in to 8 px/bar 0.05 / 0.19 ms 1.34 / 2.63 ms

    All within the 8.33 ms budget for 120 fps even on the software rasterizer. Criterion versions: cargo bench -p tr-chart --bench frame.

  • In the app (F12 readout, interactive wheel-zoom + drag): UI CPU ≈ 0.2 ms per frame, 118 fps. To try a million bars interactively: TR_PROVIDER=sample TR_SAMPLE_BARS=1000000 TR_HISTORY_LIMIT=1000000 TR_FPS=1 cargo run --release, then 1 <GO>. With all 1M one-minute bars visible on llvmpipe under Xvfb the app measured UI CPU 0.36 ms and 76 fps for the whole window (software rasterization and presentation bound; the chart pass itself is the benchmark above).

  • Indicators over 1M bars (cargo bench -p tr-core): SMA 1.8 ms, EMA 2.2 ms, VWAP 7.0 ms, Bollinger 29 ms; RSI and MACD are single-pass (≈ 12 ms and 26 ms including first-touch of their output memory). The whole 151-indicator catalogue at default parameters over 100k one-minute bars takes ≈ 134 ms on macOS (Ichimoku, the slowest, 6.6 ms).

Tests

cargo test --workspace                       # unit + fixture tests (parsers, indicators, chart, app)
cargo test -p tr-data -- --ignored           # live tests: every asset class, quotes, curve, search, websocket
cargo bench -p tr-core && cargo bench -p tr-chart
  • Parsers are tested against real filings captured from the official sources (crates/congress/tests/fixtures): multi-page House PDFs with partial sales and page-break rows, spouse/joint owners, wrapped and open-ended amounts, CUSIP vs ticker, exchanges, private holdings, a scanned paper filing, Senate search JSON (electronic, paper and amended reports) and PTR tables. An end-to-end refresh test covers discovery, roster joins, idempotency and alerts.
  • Data-source parsers are tested against captured responses: Binance klines/24h ticker/websocket kline frames, Coinbase candles/stats/websocket ticker, Treasury.gov curve CSV, FRED CSV, Yahoo chart/spark/search. Market-key parsing, catalog search and symbol mapping per source are unit tested; opt-in live tests hit the real sources for every asset class.
  • Indicators are checked against reference values (Wilder's RSI example, hand-computed EMA, MACD, population-SD Bollinger, session VWAP) and hand-computed windows (stochastic, %R, CCI, WMA, true range, Donchian, envelopes, %B, OBV and A/D, CMF, Aroon, pivots). Every catalogue entry runs on intraday and daily random walks and on degenerate input (no bars, one to three bars, flat zero-volume bars) at its default, minimum and maximum parameters, checking alignment, declared ranges and no panics; layouts saved in the old indicator format still load.
  • Layouts: the dock tree (insert, split, tab, move, remove/normalise, resize clamping, presets, JSON round trip and repair of hand-edited files), layout documents (every panel kind and its settings, links, popped windows, older formats), the store (save, list, rename, duplicate, delete, import, export, safe file names), migration from pre-docking settings, prefs and keymap parsing/rebinding.
  • Themes: every built-in round-trips through TOML and JSON, partial theme files inherit from the dark/light base, the themes folder store (save, rename, delete, name collisions), copy-on-write names and migration of older appearance preferences.
  • Brokerage: request signatures match values computed independently with Python's hmac over the documented canonical JSON; an end-to-end test runs the client against a local mock SnapTrade server that verifies every request's signature, timestamp and auth parameters and serves fixtures shaped like the API's responses (stocks, ETFs, crypto, options, cash equivalents, string and numeric decimals, a failing account); P&L for longs, shorts and option multipliers; market-key mapping; OCC symbols; credential storage permissions and redaction; errors never echo the user secret; every portfolio view renders.
  • Trading: order validation (prices per order type, stop-limit direction, notional rules, path-safe ids); a mock SnapTrade trading server checks the signed symbol lookup, order impact, placement of the previewed trade, cancel and portal requests; expired or simulated previews are never sent; per-account capabilities from brokerage and connection; BUY/SELL/TRADE parsing; edited tickets invalidate their preview; the simulator fills, rests and cancels orders.
  • The chart renders real frames offscreen on whatever adapter is present and checks pixels.

Platform notes and limitations

  • macOS is the primary target (wgpu Metal backend); Linux works with Vulkan or GL. This repository was developed and run on Linux (X11, software Vulkan). For macOS, the GPU and UI stack (tr-core, tr-chart, tr-macos, eframe/winit) is cross-checked for aarch64-apple-darwin, and CI (.github/workflows/ci.yml) builds, tests, lints and launches the app with screenshots on macOS arm64 and Intel runners.
  • Scanned paper filings (some House PTRs and Senate /paper/ reports) are recorded and alerted but not OCR'd, so their trades are not itemised.
  • Estimated performance uses disclosed value bands (midpoints), so it is an estimate of trade-level skill, not a reconstruction of anyone's portfolio.
  • Yahoo's chart endpoint is unofficial and rate-limited; use Alpaca for a supported free source. Senate eFD terms prohibit using the data for commercial purposes or credit decisions; the terminal accepts the eFD agreement on your behalf for personal research use.
S
Description
Bloomberg-style trading terminal in Rust with GPU charts and congressional trades
Readme
17 MiB
0 Stars 1 Watchers 0 Forks
2026-11-07 05:52:19 -10:00
Languages
Rust 93.5%
Svelte 3.5%
TypeScript 1.2%
Shell 1.1%
WGSL 0.4%
Other 0.3%