This document collects every detail needed to stand up the dashboard locally, customize it for a household, and ship a production build.
client/- React + Vite single-page app (SPA) rendered in the browser.server/- Node/Express proxy that calls the Questrade APIs, refreshes OAuth tokens, and exposes a single/api/summaryendpoint to the UI.shared/- Utility modules reused across the client and server (formatters, deployment helpers, date math, etc.).docs/- Guides, screenshots, and operational notes.vendor/- Optional checkouts for external helpers like the TQQQ investment-model bridge.
| Requirement | Notes |
|---|---|
| Node.js 20.19+ | The UI still builds on 20.11, but Vite prints a warning. |
| npm 10+ | Installed with Node 20. |
| Python 3.9+ (optional) | Needed only when running the investment model helper / QQQ temperature overlays. |
| Git | For cloning this repo and any optional helpers. |
| Questrade API refresh tokens | One per login you want to mirror. Generate them through Questrade's official portal. |
-
Copy
server/.env.exampletoserver/.env. AdjustPORT,CLIENT_ORIGIN, or logging flags as required. -
Seed refresh tokens for each login:
cd server npm run seed-token -- <refreshTokenFromQuestrade> [--id=<loginId>] [--label="Display name"] [--email=<email>]
- Run the command once per login.
--iddefaults toprimaryif omitted. Use friendly IDs such asdaniel,meredith, orresp. - The seed script exchanges the refresh token, stores the resulting access + refresh pair in
server/token-store.json, and preserves existing logins. - Re-run the command any time you rotate a refresh token.
- Run the command once per login.
-
(Optional) Copy
server/account-beneficiaries.example.jsontoserver/account-beneficiaries.jsonand replace the placeholder account numbers. The proxy uses the file to surface per-account beneficiary metadata (for example "Eli" or "Philanthropy"). -
(Optional) Copy
server/accounts.example.jsontoserver/accounts.jsonto define richer metadata:label,chatURL,uuid, andemailfor deep links and quick support actions.showQQQDetailsto display the QQQ temperature card for specific accounts.investmentModelsarrays describing each strategy (model,symbol,leveragedSymbol,reserveSymbol,lastRebalance).netDepositAdjustment,cagrStartDate,default, and nestedaccountsarrays to control ordering and calculations.- Override the path to this file with
ACCOUNTS_FILE/ACCOUNT_NAMES_FILEif you keep metadata elsewhere.
-
(Optional) Copy
client/.env.exampletoclient/.envif the frontend should target a non-default proxy URL or needs extra keys such asVITE_LOGO_DEV_PUBLISHABLE_KEY. -
(Optional) Provide
OPENAI_API_KEY(andOPENAI_NEWS_MODEL) inserver/.envto enable the News tab powered by OpenAI. -
Enable verbose API logging by setting
DEBUG_QUESTRADE_API=trueinserver/.envwhen diagnosing proxy calls. -
Token lifecycle debug logging is enabled by default (
DEBUG_QUESTRADE_TOKEN_FLOW=truewhen unset).- Logs are written as JSON lines to
<DATA_DIR>/.cache/questrade-token-debug.jsonl(server/.cache/questrade-token-debug.jsonlby default). - Per-login refresh single-flight protection is also enabled by default (
ENABLE_QUESTRADE_REFRESH_SINGLE_FLIGHT=true) to prevent concurrent refresh races. - Set
DEBUG_QUESTRADE_TOKEN_FLOW_VERBOSE=trueto include token-cache hit events. - Set
DEBUG_QUESTRADE_TOKEN_FLOW_STDOUT=trueto mirror these events to server stdout. - Use
QUESTRADE_TOKEN_DEBUG_LOG_MAX_BYTESto control rollover size (default 10 MB).
- Logs are written as JSON lines to
cd server
npm install
cd ../client
npm installmkdir -p vendor
cd vendor
git clone https://github.com/dbigham/TQQQ.git TQQQFollow the helper repository's README to install its Python dependencies (virtualenv recommended). The server honours INVESTMENT_MODEL_REPO if you keep the checkout somewhere else. You only need this helper when accounts opt in to investmentModels or showQQQDetails.
cd server
npm run dev- Default port:
4000. - The proxy stores refreshed tokens in memory and persists the latest refresh token per login to
server/token-store.jsonso restarts do not require reseeding. - The proxy only authorizes the frontend origin defined in
CLIENT_ORIGIN. When driving automated browsers, match that origin exactly (for examplehttp://localhost:5173/instead ofhttp://127.0.0.1:5173/). Seedocs/ui-screenshot-guide.mdfor an end-to-end walkthrough.
cd client
npm run devVite serves the SPA at http://localhost:5173. Open the URL in a browser window that is allowed by CLIENT_ORIGIN.
cd client
npm run buildThe compiled assets are written to client/dist/. Serve that folder with any static host (NGINX, S3 + CloudFront, etc.) and point it at the running proxy.
Set VITE_LOGO_DEV_PUBLISHABLE_KEY=pk_... inside client/.env to enable high-quality company logos in the Positions table. The UI loads logos from https://img.logo.dev/ticker/<TICKER>?token=.... Never embed a secret (sk_...) key in the client bundle.
Provide OPENAI_API_KEY and optionally OPENAI_NEWS_MODEL (defaults to a GPT-4.1/4o series model) inside server/.env. When set, the News tab summarizes recent public articles related to the holdings shown on screen.
Use server/accounts.json to set cashOverrides, investmentModels, and projection presets per account. The proxy watches the file for changes and hot-reloads metadata without restarting the process.
Whenever you generate a fresh refresh token inside Questrade, run:
cd server
npm run seed-token -- <refreshToken> --id=<loginId> [--label="Display name"] [--email=<email>]The script replaces only the matching login entry in token-store.json and keeps other logins untouched.
- API coverage: the proxy calls
/v1/accounts,/v1/accounts/{id}/positions,/v1/accounts/{id}/balances,/v1/accounts/{id}/activities, and/v1/symbolsfor enrichment. Additional widgets from the official portal (watchlists, events, etc.) are intentionally omitted. - Currency translation: combined P&L values reflect the native currency of each position. Cross-currency translation is on the enhancement list.
- Read-only by design: no trade placement, journaling, or fund transfers are exposed.
- Dividend coverage depends on
activitiesresponses. If the API does not return history for an account, the Dividends tab may show partial data. - Investment model evaluation requires the optional Python helper. When it fails, the UI gracefully falls back to the standard QQQ card.
- Pull-request automation: when preparing a PR with the OpenAI
make_prhelper, avoid Git submodules. The helper snapshots files but does not understand gitlink entries, and the request fails silently.
- Use
npm run lint/npm run testinsideclientif you add automated checks. - The
docs/stop-servers.mdguide lists commands for cleaning up lingering dev servers on Windows. - For deterministic screenshots (useful when updating documentation), follow
docs/ui-screenshot-guide.md.