>_TRADERVIEW — FULL DESKTOP TRADING SUITE
Replaces TraderVue ($30/mo journal) + DayTradeDash ($187/mo scanner) + StockInvest in one self-hosted binary — $2,604/yr saved. Tauri v2 desktop that downloads and runs an embedded PostgreSQL on first launch with a local auto-user, plus an axum web binary with argon2 + JWT for multi-user deployment. Both share seven library crates and the vanilla JS + uPlot frontend. 646 view modules across a 648-tile categorized launcher (Cmd-K). Live streams: Nasdaq halts (3s RSS), SEC EDGAR + 4 PR wires (catalyst radar), Finnhub WebSocket (6-panel intraday scanner), Webull broker (read-only positions). Executions are the atom; trades are FIFO-derived; the journal is markdown; expenses get OCR receipts and Schedule-C export.
Quickstart
Clone the repo, vendor uPlot, then pick a deploy target:
# 1. clone + vendor uPlot git clone https://github.com/MenkeTechnologies/traderview cd traderview ./scripts/vendor-uplot.sh # 2a. desktop — Tauri v2, embedded Postgres cargo tauri dev # first launch downloads PostgreSQL (~80 MB) # 2b. web — axum, external Postgres docker compose up -d postgres export DATABASE_URL=postgres://traderview:traderview@localhost:5432/traderview export TRADERVIEW_JWT_SECRET=$(openssl rand -hex 32) cargo run -p traderview-web --bin server # open http://localhost:8080
The desktop app is a single user, fully offline. The web binary is multi-user with registration and JWT bearer auth. Same schema, same migrations, same FIFO roll-up, same frontend, same API surface.
Architecture — One Workspace, Two Deploy Targets
The desktop and web binaries are thin shells. All domain logic, all SQL, all broker parsing lives in the three library crates. The decision "embedded vs external Postgres" and "local auto-user vs multi-user with auth" is the only thing that distinguishes them.
Desktop · traderview-desktop
Tauri v2 shell. postgresql_embedded downloads a portable PostgreSQL on first launch (cached in ~/.theseus), data lives under $APP_DATA_DIR/traderview/pg/. Auto-creates a single local user and auto-logs in. Axum router runs on a random localhost port; the WebView talks to it via fetch. Postgres shuts down cleanly on window close.
Web · server
Axum 0.7 router on 0.0.0.0:8080 (configurable). Connects to external Postgres via DATABASE_URL. argon2 password hashing on registration, JWT bearer auth on every protected route. CORS allowlist via TRADERVIEW_CORS_ORIGIN. Refuses to start without TRADERVIEW_JWT_SECRET.
Shared · traderview-{core,db,import}
Three library crates that both binaries link. core owns models + FIFO roll-up + stats. db owns sqlx pool + migrations + embedded-Postgres lifecycle. import owns broker parsers (Webull first). One-way dependency graph: desktop and web both depend on all three; import and db both depend on core; nothing depends on desktop or web.
Crate Graph
| Crate | Lines | Purpose |
|---|---|---|
traderview-core | 150,944 | Domain types (User / Account / Execution / Trade / Plan / Goal / Strategy), FIFO roll-up, statistics (win-rate / expectancy / R-multiple / SQN / Sharpe / Sortino), Kelly + correlation-aware position sizing, Monte Carlo equity forecaster, stryke-JIT backtest engine + walk-forward sweeper, sentiment scoring, custom indicator AST. |
traderview-db | 21,927 | sqlx pool factory + 46 embedded migrations, embedded PostgreSQL lifecycle (stale-PID lock cleanup, persisted password file), 50+ repo modules covering everything from trades & journal to live halts & catalysts & live_ticks stores. Background pollers for Yahoo / FINRA / EDGAR / nasdaqtrader / Finnhub WS / Reddit WSB / StockTwits / CoinGecko. In-process LRU caches with eviction. |
traderview-import | 2,477 | 12 broker CSV parsers — Webull, Lightspeed, IBKR Flex, ThinkOrSwim, TD Ameritrade, Schwab, Fidelity, ETrade, Robinhood, TradeStation, DAS Trader, TradeZero — plus a Generic ColumnMap parser for unknown sources (column-mapping wizard). Asset classes: stocks, options, futures, forex. |
traderview-web | 57,771 | axum 0.7 router — 1,952 routes split across 89 route files. argon2 + JWT auth, multipart upload, WebSocket endpoints for halts / catalysts / live ticks / Webull. Custom request/response logging middleware (snippets bodies on 4xx/5xx). client-errors sink for browser-side reports. |
traderview-expense | 447,269 | Trading-expense tracker — categorization rules engine, OCR-pipeline integration, Schedule-C report generator, multi-account reconciliation. Used by the expense view + receipt upload. |
traderview-ocr | 3,926 | Receipt OCR via the system tesseract binary + image preprocessing (binarize, deskew). Extracts merchant / amount / date for the expense matcher. |
traderview-tax | 1,948 | Tax computation crate — wage / cap-gains / Schedule-C lines, multi-state nexus, fed/state bracketing. Backs the tax wizard view. |
src-tauri · traderview-desktop | 494 | Tauri v2 shell. Worker-thread bring-up of embedded Postgres → migrations → axum on a random localhost port; main thread waits on a channel for either an ApiConfig or a String error. Native-dialog on failure. tracing-appender non-blocking file log + panic hook to ~/Library/Application Support/traderview/traderview.log. Embedded handle held across axum::serve so Postgres can't be dropped mid-request. |
| Total | 686,756 | Plus 239,580 LOC vanilla JS (646 view modules) and 3,898 LOC CSS. No bundler, no npm. |
Schema — 83 Tables, 38 Migrations
Migrations under migrations/0001_initial.sql through 0046_budgets.sql — each one adds a self-contained feature. Money is NUMERIC(20, 8) everywhere. 91 base tables, 130 indexes, 24 PostgreSQL enum types. Grouped by domain:
| Domain | Representative tables |
|---|---|
| Identity & accounts | users, accounts, api_tokens, mentorships |
| Execution & trades | executions, trades, trade_executions, trade_tags, tags, imports |
| Journal | journal_entries, note_templates, trade_reviews, chart_drawings, screenshots |
| Plans, goals, discipline | plans, trading_goals, goal_progress, discipline_violations |
| Price data & quotes | bars, quote_snapshots, news_items, earnings_events, dividends |
| Live feeds | halts, catalysts, mentions (sentiment), tick_snapshots |
| Watchlists & screening | watchlists, watchlist_symbols, filter_sets |
| Alerts & webhooks | alerts, strategy_alerts, strategy_alert_fires, hotkeys, webhooks, webhook_deliveries, disclosures_watchers |
| Backtest & strategy | backtest_runs, backtest_presets, walk_forward_runs, custom_indicators |
| Paper trading | paper_accounts, paper_orders, paper_positions |
| Portfolio / risk | rebalance_targets, rebalance_runs, tax_lots |
| Disclosures | disclosures (Form 4, 13D/G, Senate/House STOCK Act) |
| Community | shares, shared_comments, forum_categories, forum_threads, forum_posts, boards, board_items |
| Settings & AI | user_settings, ai_settings, ai_journal_cache, dashboards |
| Expenses | expense_accounts, expense_categories, expense_transactions, expense_rules, expense_receipts |
Enum types (24): sides (side_t, trade_side_t), statuses (trade_status_t, order_status_t, review_status_t), asset classes, alert triggers, sentiment sources, halt reason codes, etc. — see migrations/0001_initial.sql and feature-incremental migrations for the full set.
HTTP API — 1,865 Routes Under /api/
89 route files in crates/traderview-web/src/routes/. Bearer-auth required on everything except /health, /config, /auth/*, and /client-errors. WebSocket endpoints expose live feeds. Grouped by domain (representative endpoints — see frontend/js/api.js for the full method-bound bindings):
| Group | Sample routes |
|---|---|
| Auth + config | GET /config, GET /auth/me, POST /auth/login, POST /auth/register |
| Trades + executions | GET/POST /trades, POST /trades/rollup, POST /trades/{id}/risk, POST /trades/merge, POST /trades/bulk, POST /trades/close-expired-options, POST /trades/{id}/split, GET/POST /executions |
| Journal + AI + reviews | GET /journal/day/{day}, GET /journal/trade/{id}, POST /journal-ai/{id}/analyze, GET /trade-reviews/needed/{acct} |
| Reports (17 cuts) | /reports/overview, /by-symbol, /by-day-of-week, /by-hour, /by-hold, /r-distribution, /comparison, /exit-efficiency, /liquidity, /drawdown, /risk-adjusted, /calendar |
| Live streams (WS) | WS /ws/halts, WS /ws/catalysts, WS /ws/ticks, WS /ws/webull |
| Research per-symbol | /symbols/{sym}/quote, /signals, /news, /earnings, /dividends, /recommendations, /insiders, /fundamentals, /holders |
| Screener + scanners | GET /screener/run, GET /screener/top, GET /scans/run (24 presets) |
| Options + vol | /options/{sym}, /greeks, /vol-surface/{sym}, /iv/scan, /iv/symbols/{sym} |
| Markets + breadth | /markets/snapshot (cached), /premarket/snapshot, /breadth/snapshot, /fear-greed, /sector-rotation |
| Backtest engine | POST /backtest/run, POST /backtest/walk-forward, GET/POST /backtest-presets, POST /custom-indicators/eval/{sym} |
| Paper trading | POST /paper/accounts, GET /paper/accounts/{id}/positions, POST /paper/accounts/{id}/orders, POST /paper/accounts/{id}/reset |
| Alerts + webhooks + hotkeys | GET/POST /alerts, POST /alerts/{id}/toggle, GET/POST /strategy-alerts, GET /strategy-alerts/fires, GET/POST /hotkeys, GET/POST /webhooks, POST /webhooks/{id}/test |
| Sentiment | /sentiment/feed, /sentiment/ranked, /sentiment/symbol/{sym}, /sentiment/series/{sym} (Reddit WSB + StockTwits) |
| Crypto | /crypto/markets, /crypto/global, /crypto/btc/chain |
| Tax + reports | /tax-lots/{acct}?year=&method=FIFO|LIFO, /r-distribution/{acct}, /discipline/{acct}, /mood-analytics/{acct}, /equity-forecast, /fill-quality/{acct} |
| Webull (read-only) | POST /webull/connect (tokens in memory only), GET /webull/snapshot |
| Expenses + OCR | GET/POST /expense/transactions, POST /expense/import, POST /expense/receipts, POST /expense/receipts/{id}/attach, GET /expense/report/schedule_c?year= |
| Community | /shares, /shared/{slug}, /forum/threads, /forum/threads/{id}/posts, /mentorships, /boards |
| Custom dashboards | GET/POST /dashboards, GET /dashboards/{id} — user-defined boards built from widgets |
| Client error sink | POST /client-errors (no auth — browser-side window.onerror + unhandledrejection + console.error funnel) |
Desktop mode binds to a random localhost port and auto-logs in as the local user. Web mode requires POST /api/auth/login → JWT → Authorization: Bearer …. A custom request/response logging middleware records every request with elapsed_ms; 4xx/5xx responses get a 4KB body snippet attached to the log entry for offline debugging.
Frontend — Vanilla JS + uPlot, 641 Views, 644-Tile Launcher
Zero npm. Zero bundler. Zero JS framework. 239,580 LOC vanilla ES modules + 3,898 LOC CSS served as static files. axum's tower-http fs middleware in web mode; Tauri loads from disk via custom tauri://localhost scheme in desktop mode.
The launcher (Cmd+K) is the entry point — 646 tiles across 10 categories with a live-filter search and Enter-jumps-to-first-match. There is no fixed nav strip; the topbar carries 11 shortcuts and the rest is the launcher.
⚡ Live Markets (6 tiles)
Live Scanner (Finnhub WS, 6-panel), Halts (Nasdaq RSS + TTS), Catalysts (EDGAR + PR wires), Pre-market (futures + commodities + crypto + FX + econ events), Tape (time & sales), Heatmap (sector / S&P).
📊 Trading (16 tiles)
Webull (read-only broker), Live Positions (open P/L), Paper Trade, New Trade, Pre-trade Plans, Position Size (Kelly + correlation-aware), Hotkeys editor.
📓 Journal (9 tiles)
Journal (per-trade + daily + general), AI Journal (GPT post-mortem with cache), Trade Reviews (forced reflection on |R| ≥ 2), Trade Compare, Replay, Tape Replay, Discipline, Mood Analytics, Goals.
🔎 Charts & Research (178 tiles)
Charts (OHLC + persisted drawings), Research, Watchlists, Screener, Scanners (24 presets), Top Signals, Compare, Pairs, Correlation, Sectors, Sector Rotation, Breadth, Fear/Greed, Sentiment, Dark Pool, Short Interest, Volatility, Vol Surface, Options Chain, Earnings Cal, Earnings IV, Disclosures, Economy, News, Crypto.
📈 Reports (23 tiles)
Dashboard (overview + equity + world map), Reports (17 cuts), R-Multiple, Equity Forecast (Monte Carlo), Fill Quality (slippage vs NBBO), Risk, Rebalance, Tax Lots, Expenses (with OCR), Calendar, Accounts Overview.
🧷 Strategy & Automation (7 tiles)
Backtest (stryke-JIT), Backtest Presets, Walk-forward, Custom Indicators, Strategy Alerts (AND/OR/NOT), Alerts (price/threshold), Webhooks (Discord/Slack/generic).
💬 Community (4 tiles)
Shares (public trade links), Community (forum), Mentorship, Boards (public/private symbol boards).
⚙️ Admin & Data (18 tiles)
Import (12 brokers), CSV Wizard, Exports (CSV/JSON/Schedule D), Accounts, Tags, Search (full-text), Settings, Developer (API tokens), Tutorial (? hotkey).
Race-safe view dispatch. app.js maintains a per-dispatch token (currentViewToken()) bumped on every navigation. Every view captures the token at render start and bails after each await if the token is stale — prevents the "document.getElementById(...) returns null after navigation" crash that hits naïve SPAs when slow async resolves into a replaced DOM.
js/api.js wraps fetch with auth, error reporting, JSON parsing, and an ApiError class. js/error_reporter.js funnels window.onerror, unhandledrejection, and overridden console.error to POST /api/client-errors, queue-capped at 200 to survive a console-error loop. uPlot vendored under frontend/lib/ — pinned, reproducible, no CDN at runtime.
Importing — 12 Brokers + Column-Mapping Wizard
Built-in parsers (crates/traderview-import/src/brokers.rs):
- Webull · Lightspeed · IBKR Flex · ThinkOrSwim · TD Ameritrade · Schwab · Fidelity · ETrade · Robinhood · TradeStation · DAS Trader · TradeZero · Generic (column-mapping wizard)
Pipeline per upload:
- Raw rows + file hash recorded in
importsfor audit / re-parse. - Each row maps to an
executionand inserts under(account_id, broker_order_id, executed_at, symbol, side, qty, price)— dedupe-safe. - FIFO roll-up re-runs for affected
(account_id, symbol)pairs and rewritestrades.
Re-importing the same CSV is idempotent. Unknown brokers go through the CSV Wizard — you point at any CSV, the UI maps columns to execution fields (symbol / side / qty / price / executed_at / fees), and the mapping is saved per-broker per-user. Asset class is auto-detected from symbol patterns (options: SYMyyMMddCNNNN; futures: =F suffix; FX: =X suffix).
Live Data Streams — Polled Upstream, Pushed Downstream
Server-side pollers run on tokio tasks, push to in-process DashMap+broadcast stores, then fan out to browsers over WebSocket. All stores are bounded with oldest-first eviction (halts cap 2,000; catalysts cap 10,000) so multi-day sessions don't grow without limit.
| Stream | Source | Interval |
|---|---|---|
| Halts | nasdaqtrader.com/rss.aspx?feed=tradehalts | 3s |
| EDGAR filings | sec.gov/cgi-bin/browse-edgar?action=getcurrent&type=4&output=atom | 6s |
| PR wires | Business Wire / PR Newswire / GlobeNewswire / AccessWire RSS | 30s each |
| Finnhub ticks | wss://ws.finnhub.io (free tier: 25 syms / connection — chunked) | realtime |
| Webull broker | tradeapi.webullbroker.com (your pasted tokens; never on disk) | 5s |
| Reddit WSB | reddit.com/r/wallstreetbets/new.json | 60s |
| StockTwits | api.stocktwits.com/api/2/streams/symbol/{sym}.json | 60s per symbol |
| FINRA Reg-SHO | cdn.finra.org/equity/regsho/daily/ | on demand (back-walked by day) |
| Yahoo quote / fundamentals | query1.finance.yahoo.com v8/v10 | cached 60s per symbol in quote_snapshots + 60s in-process world-markets cache |
| CoinGecko + blockchain.com | Crypto markets + BTC chain stats | on demand |
Ticker extraction (catalyst NER) handles $SYM cashtags, parenthetical (SYM) with stop-word filter, and exchange-prefixed forms (NYSE:, NASDAQ:, AMEX:, OTC:). Halt reason codes (T1, T5, LUDP, MWC1/2/3, H10, …) are looked up in a static reason-code table for readable labels.
Resilience & Hardening
- Stale-lock recovery. Embedded Postgres checks
postmaster.pidbefore start; if the recorded PID is dead, removes the lockfile so a SIGKILL'd parent doesn't permanently block the next launch. - Persisted PG password. Random password file (mode 0600) in the data dir — initialized once, reused forever — so auth survives across launches.
- Backend held across
axum::serve. TheEmbeddedhandle is owned by the worker thread for the lifetime of the server; explicit drop after serve. Earlier, the handle dropped at function exit andpg_ctl stopkilled Postgres mid-request — every subsequent query timed out with "pool timed out". - Real concurrency for multi-symbol fan-outs.
premarket::snapshot+markets::snapshot+market_data::quotes+compare::compareall usefutures_util::future::join_all— previously serial loops blocked/api/premarket/snapshotfor 150 seconds and starved the pool. - Pool sized for cold-start fan-out.
max_connections=32,min_connections=4(kept warm),acquire_timeout=15s. - Per-view race tokens. Frontend dispatch increments a counter on every navigation; views bail post-
awaitif their captured token is stale, preventing null-DOM crashes. - Browser-side error sink.
window.onerror,unhandledrejection, and overriddenconsole.errorall POST to/api/client-errors(queue-capped). The Rust log mirrors every browser failure. - WebKit SecurityError guards. Under Tauri's
tauri://localhostcustom scheme,navigator.sendBeacon,Notification.requestPermission,new Notification,speechSynthesis.speak, andlocalStorageall throw cross-origin SecurityErrors. Each is try/caught so boot survives. - CSP allows
ws://127.0.0.1:*so WebSocket connections to the dynamic backend port aren't blocked under custom scheme.
Configuration
| Variable | Mode | Default | Purpose |
|---|---|---|---|
DATABASE_URL | web | required | Postgres connection string. |
TRADERVIEW_JWT_SECRET | web | required | HMAC secret for JWT signing. Rotate to invalidate outstanding tokens. |
TRADERVIEW_BIND | web | 0.0.0.0:8080 | axum listen address. |
TRADERVIEW_CORS_ORIGIN | web | * | CORS allowlist. |
TRADERVIEW_LOG | both | info | tracing-subscriber env-filter directive. |
$APP_DATA_DIR/traderview/pg/ | desktop | OS app-data dir | Embedded Postgres cluster location. |
The desktop app stores its Postgres cluster under the OS-appropriate app-data directory: ~/Library/Application Support/com.menketechnologies.traderview/pg/ on macOS, ~/.local/share/com.menketechnologies.traderview/pg/ on Linux, %APPDATA%\com.menketechnologies.traderview\pg\ on Windows.
What's Built
| Area | Item | Status |
|---|---|---|
| Foundation | Workspace + 7 crates + 46 migrations | Done |
| Foundation | FIFO trade roll-up + stats (expectancy, R-multiple, SQN, Sharpe, Sortino) | Done |
| Foundation | Tauri v2 desktop + embedded Postgres bootstrap, axum web binary w/ argon2 + JWT | Done |
| Import | 12 broker parsers + Generic column-mapping wizard | Done |
| Live | Halts (Nasdaq RSS, 3s) + Catalysts (EDGAR + 4 PR wires) + Finnhub WS scanner | Done |
| Live | Webull read-only broker integration (paste session tokens) | Done |
| Journal | Per-trade + daily + general notes, templates, trade reviews, AI post-mortem cache | Done |
| Analytics | 17 reports + R-distribution + Monte Carlo forecast + fill-quality + tax-lot tracker + Schedule D export | Done |
| Strategy | stryke-JIT backtest + walk-forward sweeper + custom-indicator AST + strategy alerts (AND/OR/NOT) | Done |
| Research | Options chain + Greeks + IV surface + earnings-IV crush + short interest + dark pool | Done |
| Community | Public trade shares + forum + mentorship + boards | Done |
| Expenses | Multi-account expense tracker + receipt OCR + Schedule-C report | Done |
| UX | 648-tile launcher (Cmd-K) + in-app tutorial (?) + 11-shortcut topbar | Done |
| Hardening | Per-view race tokens + browser-side error sink + WebKit SecurityError guards + bounded in-memory caches | Done |
| Roadmap | Lightspeed live broker (currently CSV-only) | Planned |
| Roadmap | Cloud sync — encrypted snapshot to S3/R2 | Future |
What This Replaces
| Tool | Cost | Replaced by |
|---|---|---|
| TraderVue (trade journal) | $30/mo | Trades + Journal + Reports + AI Journal + Trade Reviews |
| DayTradeDash (Warrior Trading scanner) | $187/mo | Live Scanner + Halts + Catalysts + Pre-market + Scanners (24 presets) |
| StockInvest.us (signal site) | varies | Top Signals + Screener + Research (per-symbol dossier) |
| Zendoo Live Scanners | free w/ TTS | Live Scanner with built-in TTS voice alerts |
| Total saved | $2,604/yr (self-hosted, your data stays local) | |
Built by MenkeTechnologies, part of the MenkeTechnologiesMeta stack alongside strykelang, zshrs, zpwr, and the broader Rust + CLI family.