makeev/alphai-tui

Terminal stock dashboard: live quotes and charts plus AI-scored news and SEC Form 4 insider activity from AlphaAI. Rust + ratatui.

30

stars

79

commits

Rust

primary language

Sep 10, 2026

updated

alphai.io
finance
financial-news
insider-trading
market-data
ratatui
rust
stock-market
stocks
terminal
tui
Browse cluster: Go terminal UI and CLI libraries

README

alphai-tui

CI crates.io license

Terminal dashboard for watching stocks: live quotes and charts next to AI-scored financial news, sentiment and SEC Form 4 insider activity. Built in Rust with ratatui.

alphai-tui demo: news list with the AI analysis card, market and trending scopes, SEC Form 4 insider stream, candlestick chart with SMA and RSI, split dashboard

What you get

  • The quote rail: one line under the tabs, present in every view, with the selected ticker's price, its change on the day, where that price sits between the session low and high, what the US market is doing right now (pre, live, post or closed, and how long until the next bell) and the rest of the watchlist as percentages. The News, Insider and Earnings views are no longer a ticker name with no price attached, and moving between tickers with the arrow keys is not a blind jump. Parts drop one at a time as the terminal narrows, the symbol and the price surviving to the last; [ui] quote_rail = false turns the line off, and a terminal under 12 rows gives the row back to the view. A delayed source (Yahoo, or ALPACA_FEED=delayed_sip) says so on the rail instead of passing a 15 minute old price off as live. After the closing bell the rail also carries the extended-hours print (AH, or PRE before the open) with its move measured against the close, so news breaking outside the session is not read next to a price frozen at 16:00. E puts the pre and post market candles on the chart itself, and the day range on the rail keeps meaning the regular session either way, because the source states that range rather than it being folded out of whatever candles were fetched. Where the source reports them, the year's range and the day's volume follow at the end of the line, first to go as the terminal narrows.

    alphai-tui split view an hour before the US open: the quote rail with the pre-market print measured against the close, the time to the bell and the day range, the watchlist with an Ext Δ% column, the candlestick chart and the scored news feed

  • The watchlist, editable while it runs: a opens a one-line prompt and the new ticker is polled from the next tick, d drops the selected one. Both are session-only, like every other runtime change here; Save in the settings screen writes the watchlist to the config. The last ticker stays, because every view is scoped to a selected one.

  • Split (the default view): watchlist and chart side by side in the top half, the news feed in the bottom half (hidden on very small terminals).

  • News: enriched articles for the selected ticker, the whole market or the 48-hour trending top 10 (f cycles the three scopes), shown as a list next to a full article card with the complete AI analysis: price impact prediction with confidence, relevance and novelty scores, actionability, background context, key entities and a contrarian view. The feed shows articles with a relevance score of 7 and up by default; + and - move that bar live (1 to 10, filtered server-side, the block title shows the active value) and [ui] news_min_score sets the startup default. Articles fresher than 15 minutes light up their age in the accent color, and rows that appeared since you last looked at the feed carry a marker that goes out once the cursor rests on them (insider rows get the same treatment). Arrivals are placed at the top of the list even when the rows below them are newer: an article reaches the feed a while after it was published (a Form 4 days after its trade), so at its publish position it would land below the fold and never be seen. x flips the layout between side-by-side and list-over-card, v expands the card to full screen, PgUp/PgDn scroll it. On terminals narrower than 90 columns the side layout gives the whole width to the list and v remains the way to read the card. Pressing down on the last row loads the next page of the feed; the page size adapts to your plan automatically (20 per page, 50 on Pro keys). Market and trending scopes collapse syndicated reprints to one row per story and show how many outlets carry it (×7). Enter opens the article page on alphai.io; a settings toggle switches that to the original source site. A 7-day bullish/bearish rollup tops the ticker scope.

    alphai-tui news view: article list next to the full AI analysis card with price impact, trading value, context and a contrarian view

  • Table: watchlist with price, change, day range and unicode sparklines.

  • Chart: candlestick chart of the selected ticker at half-block resolution, with a previous-close reference line, 20/100 moving average overlays, a volume panel and an RSI(14) panel. The overlays thread through the candles as thin braille lines and average simple or exponential (e, or [chart] ma_type); the volume bars sit in their candles' own columns and take their color, so a move and the volume behind it read together. c switches to the classic Braille line chart, m, i and b toggle the indicators and panels, t cycles interval presets on the fly. A short terminal drops the panels rather than squeezing the price chart, and sources without volume (finnhub) simply have no volume panel. The client quietly fetches extra history beyond the visible window, so the average and RSI lines are fully drawn from the first candle on screen instead of waiting a hundred candles to warm up. Like a trading terminal, the chart keeps a margin right of the newest candle (20% of the plot; [chart] right_margin_pct resizes it, 0 turns it off) with a last-price marker line and tag in it. Every time a poll changes the price, the marker and the title price pulse in the tick's color for a moment, and the newest candle folds the live price into its close, so the candle and the marker never disagree and a live market is visible at a glance; the poll interval is --every / the Poll every settings row.

  • Insider: SEC Form 4 activity for the selected ticker. A 12-month rollup (buys vs sells, dollar volumes, share of pre-arranged 10b5-1 plan trades, most active insiders with their event counts) sits above a trades chart and the stream of filing events. The chart mirrors the insider trades page on alphai.io: every event in the window is a triangle placed by date on a log dollar scale ( buy, sale, a hollow for shares sold back to the issuer, dimmed when the trade ran under a 10b5-1 plan), with weekly buy/sell dollar bars underneath and month marks along the axis. The mark of the filing selected in the list renders inverted, so the list and the chart always point at each other. g cycles the chart window: 3 months, 12 months, off ([ui] insider_chart sets the startup value); on low terminals the bars drop first and the whole panel yields before the list would starve. The chart plots every event the API knows in the window, unaffected by the score filter below. Each filing row shows the trade side straight from the filing (a buy/sell glyph; a sale back to the issuer stays neutral instead of reading as a market sale), a D/I marker for direct or indirect ownership, a p flag on pre-arranged 10b5-1 plan trades and the total trade value. The article card breaks the event down further: shares, the value-weighted average price, the SEC transaction code, who traded and their role, and the transaction date; the detail pane below the list adds what the chart data knows about the selected filing: the share of the insider's stake the event moved, how many tranches the filing folded into it, and a late-filing flag. Insider rows are scored from the trade size, so + and - filter the stream by dollar value ([ui] insider_min_score sets the startup default; 7 keeps roughly the $10M+ trades). The stream pages like the news feed: down on the last row loads more.

    alphai-tui insider view: the Form 4 trades chart, a log-scale scatter of sales over weekly dollar bars, above the filing stream with the 12-month rollup on top

  • Summary: the whole watchlist as small charts at once, laid out in a grid sized to the terminal. The table's one-row sparkline says up or down; this spends real rows on each ticker, so one glance covers the shape of the session across every name you follow. move between cards and page the grid when the watchlist outgrows the screen.

    alphai-tui summary view: nine watchlist tickers as small charts in a three by three grid, each card with the price and the day's change

  • Earnings: AlphaAI's structured read of the selected ticker's own earnings filing, the way a company reports it: the verdict and why, the metric table with prior quarter, prior year and both changes, segments, the outlook, concerns, what to watch and several paragraphs of analysis. Every figure in it was checked against the filing text before it was published, and it prints exactly as the filing wrote it, units shortened and nothing rounded into a new number. Both American and foreign filings are covered (an 8-K item 2.02, or a foreign private issuer's 6-K, which reports half years and its own currency). The metric table is banded row by row and led by dots from each name across to its figures, and it stays as wide as its own content however wide the terminal is. walk the watchlist, scroll, and older quarters continue below the newest read.

    alphai-tui earnings view: NVIDIA's second quarter fiscal 2027 read with the verdict, the summary paragraph and the metric table with prior quarter, prior year and both changes

    When a company has not reported since AlphaAI began reading filings, the view says so and gives the date of its next report when the company has confirmed one. The bottom line carries the next couple of US macro releases (CPI, the jobs report, an FOMC decision and the rest), which is the other half of what moves a price you are about to read about. Filings show up in the News feed too: the row for the filing itself is marked 8-K or 6-K rather than earnings, which tells it apart from the coverage around it, and its article card carries a short form of the read once the Earnings view has loaded it.

  • In-app settings (s): pick the price source, paste API keys once, set the poll interval (applies immediately) and choose where Enter opens news articles. Everything is saved to a config file, so after the first run a bare alphai-tui is enough.

Prices work with no key at all (Yahoo). News, sentiment and insider views use the AlphaAI API and need a free key.

How it compares

There are good terminal stock tools already. The two you are most likely to be choosing between are tickrs, which is the closest in shape (Rust, ratatui, charts per ticker), and ticker, which is the most widely used and is built around tracking what you own.

The short version: they answer what the price is doing, this one also tries to answer why. Filings and scored news sit next to the chart instead of in a browser tab.

alphai-tuitickrsticker
Price chartscandles, line, SMA/EMA, RSI, volumeline, candle, kagi, volumenone
Whole watchlist at oncesummary grid, table with sparklinessummary panequote table
News with per-article analysisyesnono
SEC Form 4 insider activitychart and filing streamnono
Earnings filing readsyesnono
Extended hoursprice and candlescandlesprice
Options chainnoyesno
Positions and P&Lnoquantity and average pricecost-basis lots, groups, currencies
Export for scripts--once textnoCSV and JSON
Price sourcesYahoo, Finnhub, AlpacaYahooYahoo, Coinbase
Add or remove a ticker in the appyesyesno
Rebindable keysany action, in the configvim keysno

What they do better. tickrs has an options chain with calls and puts by expiry, which this has nothing to answer with, and a kagi chart if that is how you read price. ticker has the most complete position tracking of the three: several cost-basis lots per holding, named groups, currency conversion, and ticker print for piping into something else. If what you want is a portfolio view, ticker is the one to reach for.

One thing worth knowing. Yahoo rate-limits by IP, and every tool here depends on it, this one included. Three price sources is the hedge: if Yahoo starts refusing, s switches to Finnhub or Alpaca without leaving the app.

Install

Homebrew (macOS and Linux):

brew install makeev/tap/alphai-tui

Arch Linux, from the AUR:

paru -S alphai-tui-bin

Debian and Ubuntu, from the apt repository (amd64 and arm64, Ubuntu 22.04 and Debian 12 or newer):

sudo install -d -m 0755 /etc/apt/keyrings
sudo curl -fsSL https://makeev.github.io/alphai-tui-apt/alphai-tui.gpg \
  -o /etc/apt/keyrings/alphai-tui.gpg
echo "deb [signed-by=/etc/apt/keyrings/alphai-tui.gpg] https://makeev.github.io/alphai-tui-apt stable main" \
  | sudo tee /etc/apt/sources.list.d/alphai-tui.list
sudo apt update && sudo apt install alphai-tui

Single .deb files, for an install without the repository, are linked from the repository landing page.

Prebuilt binaries for macOS, Linux and Windows, no Rust needed:

curl -LsSf https://github.com/makeev/alphai-tui/releases/latest/download/alphai-tui-installer.sh | sh
# Windows
powershell -ExecutionPolicy Bypass -c "irm https://github.com/makeev/alphai-tui/releases/latest/download/alphai-tui-installer.ps1 | iex"

Archives for every platform, with checksums, live on the releases page.

With a Rust toolchain (1.85+):

cargo install alphai-tui

Or straight from the repository:

cargo install --git https://github.com/makeev/alphai-tui

Or from a clone:

git clone https://github.com/makeev/alphai-tui
cd alphai-tui
cargo run --release -- AAPL MSFT NVDA BTC-USD

Quick start

alphai-tui AAPL MSFT NVDA BTC-USD

The first run opens the settings screen: pick a price source and paste your AlphaAI key (get one free at alphai.io, Account > API keys). Leave it empty if you only want quotes and charts. Your watchlist and options persist in the config file, so next time plain alphai-tui works.

alphai-tui --once AAPL      # print quotes to stdout and exit (for scripts)
alphai-tui -s finnhub NVDA  # explicit source for one run

Options

FlagDefaultMeaning
-s, --sourceyahooPrice source: yahoo, finnhub or alpaca
-e, --every15Poll interval, seconds (also a settings row, applied live)
-r, --range1dHistory window: 1d 5d 1mo 3mo 6mo 1y 2y
-i, --interval5mCandle size: 1m 2m 5m 15m 30m 60m 1d
--themedefaultColor preset, e.g. catppuccin-mocha (also a key and a settings row)
--bareoffStart without the header and footer, for a tmux pane (z toggles it live)
--oncePrint quotes to stdout and exit
--earnings TICKERPrint the latest earnings read to stdout and exit (needs an AlphaAI key; one request)
--configUse an alternate config file (Save writes back to it)

-r and -i set the startup window; the t key cycles the preset combinations (configurable as [chart] presets) for the session without persisting them.

CLI arguments win over the config file; the config file wins over built-in defaults. API keys can also come from env vars, which win over the config: ALPHAI_API_KEY, FINNHUB_API_KEY, APCA_API_KEY_ID, APCA_API_SECRET_KEY.

Keys

KeyWhereAction
Tab / 1..7everywhereswitch view
/ j ktable, chart, splitselect ticker
aeverywhereadd a ticker: type the symbol, Enter adds it, Esc cancels
deverywhereremove the selected ticker (the last one stays)
/ j knews, insiderscroll articles
/ j kearningsscroll the read
/ h lnews, insider, earningsswitch ticker
Enter / onews, insideropen article in browser
Enter / oearningsopen the read on alphai.io
vnews, insiderfullscreen article card; scroll with , Esc closes
Eeverywheredraw pre and post market candles too (yahoo only)
xnewsflip the list/card layout: side-by-side or stacked
PgUp PgDnnewsscroll the article card pane
PgUp PgDnearningspage through the read
/ j on the last rownews, insiderload the next page of the feed
fnews, splitcycle news scope: selected ticker, whole market, trending
+ / -news, insider, splitraise / lower the visible feed's score filter (news: relevance, starts at 7; insider: trade size, starts at 4)
ginsidercycle the trades chart window: 3 months, 12 months, off
cchart, splittoggle candlestick / line chart
mchart, splittoggle the two moving average overlays
echart, splitaverage them simple (SMA) or exponential (EMA)
ichart, splittoggle the RSI(14) panel
bchart, splittoggle the volume panel
t / Teverywherecycle candle interval presets forward / back (each interval with a matching history window; the list is configurable as [chart] presets)
reverywhererefresh prices and the visible news view
zeverywherebare mode: hide the header and footer, giving both rows to the view
p / Peverywherenext / previous color preset (session-only until Save)
severywheresettings
?everywherehelp overlay: every action with its current keys
q / Esc / Ctrl-Ceverywherequit

A tmux workspace

alphai-tui is a single self-contained process, so a terminal multiplexer (tmux, zellij, screen, or your terminal's own splits) turns it into a custom trading workspace: run one instance per pane and switch each pane to the view you want with 1..5.

tmux new-session -d -s market 'alphai-tui --bare CRWV'
tmux split-window  -h 'alphai-tui --bare AAPL'      # news pane on the right
tmux split-window -v -t market:0.0 'alphai-tui --bare NVDA'
tmux split-window -v -t market:0.1 'alphai-tui --bare NBIS'
tmux attach -t market

--bare drops the header and the key hints, which a pane with tmux's own status bar has little use for, and hands both rows to the view; z toggles it in a running instance and [ui] bare = true makes it the default. The quote rail stays, so a bare pane still names its ticker and its price.

Press 4 in the chart panes and 2 in the news pane, and you get a wall of charts next to a live scored feed:

four alphai-tui instances in tmux panes: three candlestick charts with SMA and RSI next to a full-height AI-scored news view

Two things the instances share. The config file is one: the last pane to save settings wins, so set things up once and let the other panes just read it. Your AlphaAI key's rate budget is the other: every pane showing news or insider data spends requests from the same per-key allowance, so on a free key keep an eye on how many such panes you open.

The same trick turns the terminal into a full trading desk with an AI analyst on staff. Run an agent such as Claude Code in the pane next to alphai-tui and connect it to the AlphaAI MCP server, which serves the same news, sentiment and insider data as the dashboard. You watch the tape on one side while the agent digs through whatever the tape surfaces: ask it for the last insider sells and the news that moved the stock this week, and get a sourced brief without leaving the terminal.

Claude Code next to alphai-tui in tmux: the agent summarizes CRWV insider selling and the week's dominant story while the dashboard shows the candlestick chart and the scored news feed

Data sources

Prices

  • yahoo: no API key, quote and candle history in one request, roughly 15 minutes delayed. Crypto and FX tickers work as BTC-USD, EURUSD=X. The only source here that reports extended-hours prices and candles, the 52 week range and full market volume, all in the same request as the price. E and [chart] extended_hours apply to this source alone.

  • finnhub: needs a key (free at finnhub.io). Real-time-ish quotes; historical candles are premium-only there, so charts build up from quotes collected during the session and reset on restart. Range/interval switching with t does not apply to that synthetic history, and candles degrade to flat marks. Free tier is 60 req/min, one request per ticker per poll; the app warns on startup and in the settings screen when the watchlist and the poll interval together go over that. Crypto needs exchange-prefixed symbols (BINANCE:BTCUSDT). Its quote endpoint covers the regular session only, so no extended-hours price, 52 week range or volume.

  • alpaca: needs a key id and secret (free at alpaca.markets). Realtime quotes from the IEX feed plus real historical bars, so charts are complete right after start instead of growing over the session. Crypto works in the usual BTC-USD form. IEX is one exchange rather than the whole tape, so on the free feed there are no pre or post market prints and no volume figure: a few percent of the day's shares would read as the day's volume. Both appear on ALPACA_FEED=sip or delayed_sip. Getting free keys:

    1. Sign up at alpaca.markets. Email is enough; market data and paper trading need no KYC.
    2. The free Basic data plan is enabled by default.
    3. In the dashboard switch the environment to Paper (fine for data), then Home > API Keys > Generate. Copy the Key ID and the Secret; the secret is shown only once.
    4. Paste both in the settings screen (s) or export APCA_API_KEY_ID and APCA_API_SECRET_KEY.

    Free plan notes: the IEX feed is realtime but thin (roughly 2 to 3 percent of market volume, so charts of illiquid names can be sparse), and the API allows 200 requests/min. The app makes 2 requests per ticker per poll and warns, on startup and under the interval in the settings screen, when the watchlist and the interval together go over the ceiling, naming an interval that fits (going over turns tickers into error rows). ALPACA_FEED=sip needs a paid data plan; ALPACA_FEED=delayed_sip gives the full market with a 15 minute delay.

News, sentiment, insider

  • AlphaAI: AI-enriched financial news feed. Every article carries validated tickers, a category, a deterministic 1 to 10 relevance score and a full per-ticker AI analysis (sentiment, price impact, confidence, novelty, actionability); insider rows are generated from SEC EDGAR Form 4 filings, one row per economic event. The free tier (no card) allows 20 requests/min and 100/day. The app is careful with that budget: it fetches only what the visible view needs (the trending scope is one extra request), caches each response for 5 minutes ([ui] alphai_ttl_secs in the config changes that), loads further pages only when you ask for them, and the article card reuses data already fetched with the list. The refresh at the end of that cache window asks the server what has arrived since the previous check rather than re-reading the newest page, which costs the same single request, keeps the pages you loaded and the row you are on, and is the only way to see an article that entered the feed behind its own publish time. The relevance filter is applied by the server, so filtered-out articles never occupy page slots; moving it with +/- refetches the visible feed, one request per press at most. The Insider view's rollup and trades chart arrive as one bundle alongside the feed's first page and live in the same cache, so the chart costs no extra requests and the g window switch is free. The Earnings view costs one request per ticker, made only while that view is on screen and cached for an hour, because a read is published once a quarter and never changes afterwards. That single response carries the whole history of reads for the ticker and its next confirmed report date, which is also what fills the read shown in the News card, so opening the card still costs nothing. The macro calendar behind the bottom line of that view is one request for the whole market, cached for six hours; if it fails there is simply no line. Feeds page 20 articles at a time, the most every plan allows (50 on Pro keys, detected automatically). Paging back past your plan's archive horizon (30 days on Free, 90 on Basic) shows an upgrade hint instead of older articles. Full API reference: alphai.io/developers.

Ticker forms follow the US/Yahoo convention (AAPL, BTC-USD, VOD.L), which is also what AlphaAI uses. Finnhub-specific symbols like BINANCE:BTCUSDT will not have news attached.

Configuration

~/.config/alphai-tui/config.toml on Linux and macOS (%APPDATA% on Windows), created by the settings screen with mode 0600 since it can hold keys; --config PATH points at a different file. Saving the settings also persists the watchlist on screen. Every key is optional. A misspelled value in the [ui], [chart], [theme] or [keybindings] sections prints a warning on startup and keeps that entry's default; only a TOML syntax error makes the whole file fall back to defaults. The [ui] and [chart] sections set startup defaults; the session keys (x, f, g, +, -, c, m, i, b, e, t) still change everything live without persisting it:

source = "yahoo"
watchlist = ["AAPL", "MSFT", "NVDA", "BTC-USD"]
every = 15
range = "1d"
interval = "5m"
news_open = "alphai"  # where enter opens news: "alphai" or "original"

[keys]
alphai = "ak_live_..."
finnhub = ""
alpaca_key_id = ""
alpaca_secret = ""

[ui]
default_view = "split"    # split | news | table | chart | insider | earnings
quote_rail = true         # the price line under the tabs
bare = false              # start with no header and no footer (--bare, z)
news_layout = "side"      # side | stacked
news_scope = "ticker"     # ticker | market | trending
borders = "rounded"       # panel frames: rounded | plain
news_min_score = 7        # minimum relevance score in news feeds, 1 to 10
insider_min_score = 4     # insider feed filter; the score tracks trade size
insider_chart = "3m"      # insider trades chart window at start: 3m | 12m | off
alphai_ttl_secs = 300     # news/sentiment/insider cache lifetime, 30 to 86400

[chart]
style = "candles"         # candles | line
sma = true                # moving average overlays visible at start
ma_type = "sma"           # sma | ema, both using the periods below
rsi = true                # RSI panel visible at start
volume = true             # volume panel visible at start
extended_hours = false    # draw pre and post market candles (yahoo only)
sma_fast = 20             # 2 to 250
sma_slow = 100            # 2 to 250; also sizes the history warm-up
rsi_period = 14           # 2 to 100
right_margin_pct = 20     # free space right of the newest candle, 0 to 50
presets = [               # the combos the t and T keys cycle
  ["1d", "5m"],
  ["5d", "15m"],
  ["1mo", "60m"],
  ["6mo", "1d"],
  ["1y", "1d"],
]

Colors

[theme] preset swaps in a ready-made palette:

[theme]
preset = "catppuccin-mocha"

alphai-tui cycling through its color presets with the p key: catppuccin mocha, macchiato and frappe, dracula, gruvbox and nord

p and P walk the presets live, --theme catppuccin-mocha picks one for a single run, and the Theme row in the settings screen (s) does both: cycle it with a live preview, Save writes it here.

Available: default, catppuccin-mocha, catppuccin-macchiato, catppuccin-frappe, catppuccin-latte, dracula, gruvbox-dark, gruvbox-light, nord. Presets are written in hex, so they want a terminal with 24-bit color; default uses ANSI names and follows whatever palette the terminal itself is set to. They only set foreground colors, which leaves a transparent or blurred terminal background alone. The two light ones (catppuccin-latte, gruvbox-light) expect a light terminal background.

Every color the views draw comes from a named slot, and the optional [theme] table recolors any of them, over the preset when there is one. Values are ANSI color names (case-insensitive, light-blue, grey), #RRGGBB hex, or an ANSI-256 index written as a string like "245". A bad color, a misspelled slot or an unknown preset prints a warning on startup and keeps the default; it never breaks the config file. The defaults are the values shown:

[theme]
accent = "cyan"          # header title, active tab, overlay borders, headings
accent_text = "black"    # text on the active tab
up = "green"             # price up: candles, deltas, sparklines
down = "red"             # price down
flat = "gray"            # unchanged / no data
pos = "green"            # bullish sentiment, insider buys
neg = "red"              # bearish sentiment, insider sells
error = "red"            # error messages
warn = "yellow"          # notices and the editing highlight
score_high = "yellow"    # relevance score 8 to 10
sma_fast = "yellow"      # fast moving average overlay
sma_slow = "magenta"     # slow moving average overlay
rsi_line = "cyan"        # RSI line
ref_line = "darkgray"    # previous close and RSI 30/70 reference lines
border = "reset"         # panel frames; reset keeps the terminal's foreground

Custom keybindings

The optional [keybindings] table rebinds any action. An action you list replaces its default keys entirely; actions you leave out keep theirs. The value is one key or a list of keys:

[keybindings]
quit = "ctrl-q"
open = ["enter", "w"]
next_preset = "]"
prev_preset = "["

A key is written as [ctrl-][alt-][shift-]<base>, where base is a single character or one of the named keys: esc, enter, tab, backtab, space, up, down, left, right, home, end, pgup, pgdn, backspace, delete, insert, f1 to f12. shift- plus a letter means the uppercase letter (shift-t equals T), and shift-tab equals backtab.

The actions: quit, next_view, prev_view, settings, help, refresh, up, down, left, right, page_up, page_down, open, card, cycle_scope, cycle_layout, score_up, score_down, insider_chart, chart_style, toggle_sma, toggle_rsi, toggle_volume, ma_type, next_preset, prev_preset, next_theme, prev_theme. The ? help overlay shows this list with the current keys next to it.

Reserved and never remappable: ctrl-c (force quit), esc, the digits 1 to 9 (view hotkeys), and the keys inside the settings form. A bad or reserved key, an unknown action, or a key claimed by two actions prints a warning on startup and falls back safely; the footer always shows the keys that are actually bound.

Not configurable on purpose

The AlphaAI response cache (5 minutes), the feed page sizes, the 2 second poll floor and the chart warm-up factors are fixed. They keep the app a fair citizen of the free API tiers, and a config knob for them would turn an innocent-looking file into an abuse vector. ALPACA_FEED, ALPHAI_API_URL and ALPACA_DATA_URL stay env-only debug overrides for the same reason.

Architecture

src/
  domain.rs      Quote, Candle, TickerData, Range/Interval
  config.rs      config file load/save (CLI > env > file > defaults)
  source/        DataSource trait + implementations
    registry.rs  the one place a new source registers; CLI, settings and keys derive from it
    http.rs      shared client builder, JSON fetching and error helpers
    yahoo.rs     Yahoo v8 chart endpoint (quote + history in one call)
    finnhub.rs   Finnhub /quote with synthetic session history
    alpaca.rs    snapshot + real historical bars (IEX/SIP feeds, crypto)
  alphai.rs      AlphaAI API client + demand-driven fetch task (TTL cache)
  keymap.rs      semantic actions + the key table (footer hints derive from it)
  theme.rs       semantic color palette ([theme] overrides)
  indicators.rs  SMA, EMA and RSI (Wilder smoothing)
  poller.rs      fetches all symbols concurrently on a timer -> mpsc channel
  app.rs         App state, event loop, key handling
    app/feeds.rs     feed cache and every AlphaAI request-budget guard
    app/settings.rs  settings overlay state, rows derived from the registry
  ui/            View trait + implementations
    table.rs     watchlist table
    chart.rs     candlestick + line chart, SMA overlays, volume and RSI panels
    split.rs     table + chart
    news.rs      article list + sentiment rollup + detail pane
    insider.rs   Form 4 rollup + filing list
    insider_chart.rs  log-scale trades scatter + weekly dollar bars
    earnings.rs  structured earnings read + the schedule line
    article.rs   modal full-article card (AI analysis, context)
    settings.rs  modal settings overlay

Data flows one way: background tasks (price poller, AlphaAI fetcher) push events over an mpsc channel into App::apply; views are stateless renderers over &mut App. The UI never blocks on the network, and every AlphaAI request-budget guard lives in one file (app/feeds.rs).

Adding a price source

  1. Implement source::DataSource (one async fetch returning quote plus candles) in a new module under src/source/. The helpers in source/http.rs cover the client, JSON fetching and error plumbing.
  2. Append one SourceInfo entry to source/registry.rs: id, aliases, a settings hint, the key fields it needs and a constructor. The --source help and error list, the settings screen rows and picker cycle, config [keys] persistence and env-var overrides all derive from that entry, and the registry tests check it.
  3. Describe the source in this README.

Adding a view

  1. Implement ui::View as a unit struct in a new module under src/ui/: a stateless render over &mut App, a new ViewId variant, a footer hint line, and the capability methods (feed_shown, navigates_articles, has_chart_panel) that opt into the shared key handling and the demand-driven AlphaAI fetch. Views never fetch anything themselves.
  2. Add it to ui::VIEWS. Order in that array defines the tab cycle and the 1..9 hotkeys; the header pills and the footer hints derive from it.

Development

cargo test          # unit + TestBackend rendering tests
cargo clippy --all-targets
cargo fmt --all -- --check
cargo run -- --once AAPL             # network smoke test without a TTY
ALPHAI_API_KEY=ak_live_... cargo test live_api -- --ignored   # live API smoke

CI runs the first three on every push and pull request, on Linux, macOS and Windows. What changed in each release is in CHANGELOG.md.

Issues and PRs are welcome.

License

MIT. Not investment advice; data comes from third-party sources and can be delayed or wrong. Respect the terms of the data providers you enable.

Contributors

makeev

79 commits

makeev/alphai-tui

Terminal stock dashboard: live quotes and charts plus AI-scored news and SEC Form 4 insider activity from AlphaAI. Rust + ratatui.

30

stars

79

commits

Rust

primary language

Sep 10, 2026

updated

alphai.io
finance
financial-news
insider-trading
market-data
ratatui
rust
stock-market
stocks
terminal
tui
Browse cluster: Go terminal UI and CLI libraries

README

alphai-tui

CI crates.io license

Terminal dashboard for watching stocks: live quotes and charts next to AI-scored financial news, sentiment and SEC Form 4 insider activity. Built in Rust with ratatui.

alphai-tui demo: news list with the AI analysis card, market and trending scopes, SEC Form 4 insider stream, candlestick chart with SMA and RSI, split dashboard

What you get

  • The quote rail: one line under the tabs, present in every view, with the selected ticker's price, its change on the day, where that price sits between the session low and high, what the US market is doing right now (pre, live, post or closed, and how long until the next bell) and the rest of the watchlist as percentages. The News, Insider and Earnings views are no longer a ticker name with no price attached, and moving between tickers with the arrow keys is not a blind jump. Parts drop one at a time as the terminal narrows, the symbol and the price surviving to the last; [ui] quote_rail = false turns the line off, and a terminal under 12 rows gives the row back to the view. A delayed source (Yahoo, or ALPACA_FEED=delayed_sip) says so on the rail instead of passing a 15 minute old price off as live. After the closing bell the rail also carries the extended-hours print (AH, or PRE before the open) with its move measured against the close, so news breaking outside the session is not read next to a price frozen at 16:00. E puts the pre and post market candles on the chart itself, and the day range on the rail keeps meaning the regular session either way, because the source states that range rather than it being folded out of whatever candles were fetched. Where the source reports them, the year's range and the day's volume follow at the end of the line, first to go as the terminal narrows.

    alphai-tui split view an hour before the US open: the quote rail with the pre-market print measured against the close, the time to the bell and the day range, the watchlist with an Ext Δ% column, the candlestick chart and the scored news feed

  • The watchlist, editable while it runs: a opens a one-line prompt and the new ticker is polled from the next tick, d drops the selected one. Both are session-only, like every other runtime change here; Save in the settings screen writes the watchlist to the config. The last ticker stays, because every view is scoped to a selected one.

  • Split (the default view): watchlist and chart side by side in the top half, the news feed in the bottom half (hidden on very small terminals).

  • News: enriched articles for the selected ticker, the whole market or the 48-hour trending top 10 (f cycles the three scopes), shown as a list next to a full article card with the complete AI analysis: price impact prediction with confidence, relevance and novelty scores, actionability, background context, key entities and a contrarian view. The feed shows articles with a relevance score of 7 and up by default; + and - move that bar live (1 to 10, filtered server-side, the block title shows the active value) and [ui] news_min_score sets the startup default. Articles fresher than 15 minutes light up their age in the accent color, and rows that appeared since you last looked at the feed carry a marker that goes out once the cursor rests on them (insider rows get the same treatment). Arrivals are placed at the top of the list even when the rows below them are newer: an article reaches the feed a while after it was published (a Form 4 days after its trade), so at its publish position it would land below the fold and never be seen. x flips the layout between side-by-side and list-over-card, v expands the card to full screen, PgUp/PgDn scroll it. On terminals narrower than 90 columns the side layout gives the whole width to the list and v remains the way to read the card. Pressing down on the last row loads the next page of the feed; the page size adapts to your plan automatically (20 per page, 50 on Pro keys). Market and trending scopes collapse syndicated reprints to one row per story and show how many outlets carry it (×7). Enter opens the article page on alphai.io; a settings toggle switches that to the original source site. A 7-day bullish/bearish rollup tops the ticker scope.

    alphai-tui news view: article list next to the full AI analysis card with price impact, trading value, context and a contrarian view

  • Table: watchlist with price, change, day range and unicode sparklines.

  • Chart: candlestick chart of the selected ticker at half-block resolution, with a previous-close reference line, 20/100 moving average overlays, a volume panel and an RSI(14) panel. The overlays thread through the candles as thin braille lines and average simple or exponential (e, or [chart] ma_type); the volume bars sit in their candles' own columns and take their color, so a move and the volume behind it read together. c switches to the classic Braille line chart, m, i and b toggle the indicators and panels, t cycles interval presets on the fly. A short terminal drops the panels rather than squeezing the price chart, and sources without volume (finnhub) simply have no volume panel. The client quietly fetches extra history beyond the visible window, so the average and RSI lines are fully drawn from the first candle on screen instead of waiting a hundred candles to warm up. Like a trading terminal, the chart keeps a margin right of the newest candle (20% of the plot; [chart] right_margin_pct resizes it, 0 turns it off) with a last-price marker line and tag in it. Every time a poll changes the price, the marker and the title price pulse in the tick's color for a moment, and the newest candle folds the live price into its close, so the candle and the marker never disagree and a live market is visible at a glance; the poll interval is --every / the Poll every settings row.

  • Insider: SEC Form 4 activity for the selected ticker. A 12-month rollup (buys vs sells, dollar volumes, share of pre-arranged 10b5-1 plan trades, most active insiders with their event counts) sits above a trades chart and the stream of filing events. The chart mirrors the insider trades page on alphai.io: every event in the window is a triangle placed by date on a log dollar scale ( buy, sale, a hollow for shares sold back to the issuer, dimmed when the trade ran under a 10b5-1 plan), with weekly buy/sell dollar bars underneath and month marks along the axis. The mark of the filing selected in the list renders inverted, so the list and the chart always point at each other. g cycles the chart window: 3 months, 12 months, off ([ui] insider_chart sets the startup value); on low terminals the bars drop first and the whole panel yields before the list would starve. The chart plots every event the API knows in the window, unaffected by the score filter below. Each filing row shows the trade side straight from the filing (a buy/sell glyph; a sale back to the issuer stays neutral instead of reading as a market sale), a D/I marker for direct or indirect ownership, a p flag on pre-arranged 10b5-1 plan trades and the total trade value. The article card breaks the event down further: shares, the value-weighted average price, the SEC transaction code, who traded and their role, and the transaction date; the detail pane below the list adds what the chart data knows about the selected filing: the share of the insider's stake the event moved, how many tranches the filing folded into it, and a late-filing flag. Insider rows are scored from the trade size, so + and - filter the stream by dollar value ([ui] insider_min_score sets the startup default; 7 keeps roughly the $10M+ trades). The stream pages like the news feed: down on the last row loads more.

    alphai-tui insider view: the Form 4 trades chart, a log-scale scatter of sales over weekly dollar bars, above the filing stream with the 12-month rollup on top

  • Summary: the whole watchlist as small charts at once, laid out in a grid sized to the terminal. The table's one-row sparkline says up or down; this spends real rows on each ticker, so one glance covers the shape of the session across every name you follow. move between cards and page the grid when the watchlist outgrows the screen.

    alphai-tui summary view: nine watchlist tickers as small charts in a three by three grid, each card with the price and the day's change

  • Earnings: AlphaAI's structured read of the selected ticker's own earnings filing, the way a company reports it: the verdict and why, the metric table with prior quarter, prior year and both changes, segments, the outlook, concerns, what to watch and several paragraphs of analysis. Every figure in it was checked against the filing text before it was published, and it prints exactly as the filing wrote it, units shortened and nothing rounded into a new number. Both American and foreign filings are covered (an 8-K item 2.02, or a foreign private issuer's 6-K, which reports half years and its own currency). The metric table is banded row by row and led by dots from each name across to its figures, and it stays as wide as its own content however wide the terminal is. walk the watchlist, scroll, and older quarters continue below the newest read.

    alphai-tui earnings view: NVIDIA's second quarter fiscal 2027 read with the verdict, the summary paragraph and the metric table with prior quarter, prior year and both changes

    When a company has not reported since AlphaAI began reading filings, the view says so and gives the date of its next report when the company has confirmed one. The bottom line carries the next couple of US macro releases (CPI, the jobs report, an FOMC decision and the rest), which is the other half of what moves a price you are about to read about. Filings show up in the News feed too: the row for the filing itself is marked 8-K or 6-K rather than earnings, which tells it apart from the coverage around it, and its article card carries a short form of the read once the Earnings view has loaded it.

  • In-app settings (s): pick the price source, paste API keys once, set the poll interval (applies immediately) and choose where Enter opens news articles. Everything is saved to a config file, so after the first run a bare alphai-tui is enough.

Prices work with no key at all (Yahoo). News, sentiment and insider views use the AlphaAI API and need a free key.

How it compares

There are good terminal stock tools already. The two you are most likely to be choosing between are tickrs, which is the closest in shape (Rust, ratatui, charts per ticker), and ticker, which is the most widely used and is built around tracking what you own.

The short version: they answer what the price is doing, this one also tries to answer why. Filings and scored news sit next to the chart instead of in a browser tab.

alphai-tuitickrsticker
Price chartscandles, line, SMA/EMA, RSI, volumeline, candle, kagi, volumenone
Whole watchlist at oncesummary grid, table with sparklinessummary panequote table
News with per-article analysisyesnono
SEC Form 4 insider activitychart and filing streamnono
Earnings filing readsyesnono
Extended hoursprice and candlescandlesprice
Options chainnoyesno
Positions and P&Lnoquantity and average pricecost-basis lots, groups, currencies
Export for scripts--once textnoCSV and JSON
Price sourcesYahoo, Finnhub, AlpacaYahooYahoo, Coinbase
Add or remove a ticker in the appyesyesno
Rebindable keysany action, in the configvim keysno

What they do better. tickrs has an options chain with calls and puts by expiry, which this has nothing to answer with, and a kagi chart if that is how you read price. ticker has the most complete position tracking of the three: several cost-basis lots per holding, named groups, currency conversion, and ticker print for piping into something else. If what you want is a portfolio view, ticker is the one to reach for.

One thing worth knowing. Yahoo rate-limits by IP, and every tool here depends on it, this one included. Three price sources is the hedge: if Yahoo starts refusing, s switches to Finnhub or Alpaca without leaving the app.

Install

Homebrew (macOS and Linux):

brew install makeev/tap/alphai-tui

Arch Linux, from the AUR:

paru -S alphai-tui-bin

Debian and Ubuntu, from the apt repository (amd64 and arm64, Ubuntu 22.04 and Debian 12 or newer):

sudo install -d -m 0755 /etc/apt/keyrings
sudo curl -fsSL https://makeev.github.io/alphai-tui-apt/alphai-tui.gpg \
  -o /etc/apt/keyrings/alphai-tui.gpg
echo "deb [signed-by=/etc/apt/keyrings/alphai-tui.gpg] https://makeev.github.io/alphai-tui-apt stable main" \
  | sudo tee /etc/apt/sources.list.d/alphai-tui.list
sudo apt update && sudo apt install alphai-tui

Single .deb files, for an install without the repository, are linked from the repository landing page.

Prebuilt binaries for macOS, Linux and Windows, no Rust needed:

curl -LsSf https://github.com/makeev/alphai-tui/releases/latest/download/alphai-tui-installer.sh | sh
# Windows
powershell -ExecutionPolicy Bypass -c "irm https://github.com/makeev/alphai-tui/releases/latest/download/alphai-tui-installer.ps1 | iex"

Archives for every platform, with checksums, live on the releases page.

With a Rust toolchain (1.85+):

cargo install alphai-tui

Or straight from the repository:

cargo install --git https://github.com/makeev/alphai-tui

Or from a clone:

git clone https://github.com/makeev/alphai-tui
cd alphai-tui
cargo run --release -- AAPL MSFT NVDA BTC-USD

Quick start

alphai-tui AAPL MSFT NVDA BTC-USD

The first run opens the settings screen: pick a price source and paste your AlphaAI key (get one free at alphai.io, Account > API keys). Leave it empty if you only want quotes and charts. Your watchlist and options persist in the config file, so next time plain alphai-tui works.

alphai-tui --once AAPL      # print quotes to stdout and exit (for scripts)
alphai-tui -s finnhub NVDA  # explicit source for one run

Options

FlagDefaultMeaning
-s, --sourceyahooPrice source: yahoo, finnhub or alpaca
-e, --every15Poll interval, seconds (also a settings row, applied live)
-r, --range1dHistory window: 1d 5d 1mo 3mo 6mo 1y 2y
-i, --interval5mCandle size: 1m 2m 5m 15m 30m 60m 1d
--themedefaultColor preset, e.g. catppuccin-mocha (also a key and a settings row)
--bareoffStart without the header and footer, for a tmux pane (z toggles it live)
--oncePrint quotes to stdout and exit
--earnings TICKERPrint the latest earnings read to stdout and exit (needs an AlphaAI key; one request)
--configUse an alternate config file (Save writes back to it)

-r and -i set the startup window; the t key cycles the preset combinations (configurable as [chart] presets) for the session without persisting them.

CLI arguments win over the config file; the config file wins over built-in defaults. API keys can also come from env vars, which win over the config: ALPHAI_API_KEY, FINNHUB_API_KEY, APCA_API_KEY_ID, APCA_API_SECRET_KEY.

Keys

KeyWhereAction
Tab / 1..7everywhereswitch view
/ j ktable, chart, splitselect ticker
aeverywhereadd a ticker: type the symbol, Enter adds it, Esc cancels
deverywhereremove the selected ticker (the last one stays)
/ j knews, insiderscroll articles
/ j kearningsscroll the read
/ h lnews, insider, earningsswitch ticker
Enter / onews, insideropen article in browser
Enter / oearningsopen the read on alphai.io
vnews, insiderfullscreen article card; scroll with , Esc closes
Eeverywheredraw pre and post market candles too (yahoo only)
xnewsflip the list/card layout: side-by-side or stacked
PgUp PgDnnewsscroll the article card pane
PgUp PgDnearningspage through the read
/ j on the last rownews, insiderload the next page of the feed
fnews, splitcycle news scope: selected ticker, whole market, trending
+ / -news, insider, splitraise / lower the visible feed's score filter (news: relevance, starts at 7; insider: trade size, starts at 4)
ginsidercycle the trades chart window: 3 months, 12 months, off
cchart, splittoggle candlestick / line chart
mchart, splittoggle the two moving average overlays
echart, splitaverage them simple (SMA) or exponential (EMA)
ichart, splittoggle the RSI(14) panel
bchart, splittoggle the volume panel
t / Teverywherecycle candle interval presets forward / back (each interval with a matching history window; the list is configurable as [chart] presets)
reverywhererefresh prices and the visible news view
zeverywherebare mode: hide the header and footer, giving both rows to the view
p / Peverywherenext / previous color preset (session-only until Save)
severywheresettings
?everywherehelp overlay: every action with its current keys
q / Esc / Ctrl-Ceverywherequit

A tmux workspace

alphai-tui is a single self-contained process, so a terminal multiplexer (tmux, zellij, screen, or your terminal's own splits) turns it into a custom trading workspace: run one instance per pane and switch each pane to the view you want with 1..5.

tmux new-session -d -s market 'alphai-tui --bare CRWV'
tmux split-window  -h 'alphai-tui --bare AAPL'      # news pane on the right
tmux split-window -v -t market:0.0 'alphai-tui --bare NVDA'
tmux split-window -v -t market:0.1 'alphai-tui --bare NBIS'
tmux attach -t market

--bare drops the header and the key hints, which a pane with tmux's own status bar has little use for, and hands both rows to the view; z toggles it in a running instance and [ui] bare = true makes it the default. The quote rail stays, so a bare pane still names its ticker and its price.

Press 4 in the chart panes and 2 in the news pane, and you get a wall of charts next to a live scored feed:

four alphai-tui instances in tmux panes: three candlestick charts with SMA and RSI next to a full-height AI-scored news view

Two things the instances share. The config file is one: the last pane to save settings wins, so set things up once and let the other panes just read it. Your AlphaAI key's rate budget is the other: every pane showing news or insider data spends requests from the same per-key allowance, so on a free key keep an eye on how many such panes you open.

The same trick turns the terminal into a full trading desk with an AI analyst on staff. Run an agent such as Claude Code in the pane next to alphai-tui and connect it to the AlphaAI MCP server, which serves the same news, sentiment and insider data as the dashboard. You watch the tape on one side while the agent digs through whatever the tape surfaces: ask it for the last insider sells and the news that moved the stock this week, and get a sourced brief without leaving the terminal.

Claude Code next to alphai-tui in tmux: the agent summarizes CRWV insider selling and the week's dominant story while the dashboard shows the candlestick chart and the scored news feed

Data sources

Prices

  • yahoo: no API key, quote and candle history in one request, roughly 15 minutes delayed. Crypto and FX tickers work as BTC-USD, EURUSD=X. The only source here that reports extended-hours prices and candles, the 52 week range and full market volume, all in the same request as the price. E and [chart] extended_hours apply to this source alone.

  • finnhub: needs a key (free at finnhub.io). Real-time-ish quotes; historical candles are premium-only there, so charts build up from quotes collected during the session and reset on restart. Range/interval switching with t does not apply to that synthetic history, and candles degrade to flat marks. Free tier is 60 req/min, one request per ticker per poll; the app warns on startup and in the settings screen when the watchlist and the poll interval together go over that. Crypto needs exchange-prefixed symbols (BINANCE:BTCUSDT). Its quote endpoint covers the regular session only, so no extended-hours price, 52 week range or volume.

  • alpaca: needs a key id and secret (free at alpaca.markets). Realtime quotes from the IEX feed plus real historical bars, so charts are complete right after start instead of growing over the session. Crypto works in the usual BTC-USD form. IEX is one exchange rather than the whole tape, so on the free feed there are no pre or post market prints and no volume figure: a few percent of the day's shares would read as the day's volume. Both appear on ALPACA_FEED=sip or delayed_sip. Getting free keys:

    1. Sign up at alpaca.markets. Email is enough; market data and paper trading need no KYC.
    2. The free Basic data plan is enabled by default.
    3. In the dashboard switch the environment to Paper (fine for data), then Home > API Keys > Generate. Copy the Key ID and the Secret; the secret is shown only once.
    4. Paste both in the settings screen (s) or export APCA_API_KEY_ID and APCA_API_SECRET_KEY.

    Free plan notes: the IEX feed is realtime but thin (roughly 2 to 3 percent of market volume, so charts of illiquid names can be sparse), and the API allows 200 requests/min. The app makes 2 requests per ticker per poll and warns, on startup and under the interval in the settings screen, when the watchlist and the interval together go over the ceiling, naming an interval that fits (going over turns tickers into error rows). ALPACA_FEED=sip needs a paid data plan; ALPACA_FEED=delayed_sip gives the full market with a 15 minute delay.

News, sentiment, insider

  • AlphaAI: AI-enriched financial news feed. Every article carries validated tickers, a category, a deterministic 1 to 10 relevance score and a full per-ticker AI analysis (sentiment, price impact, confidence, novelty, actionability); insider rows are generated from SEC EDGAR Form 4 filings, one row per economic event. The free tier (no card) allows 20 requests/min and 100/day. The app is careful with that budget: it fetches only what the visible view needs (the trending scope is one extra request), caches each response for 5 minutes ([ui] alphai_ttl_secs in the config changes that), loads further pages only when you ask for them, and the article card reuses data already fetched with the list. The refresh at the end of that cache window asks the server what has arrived since the previous check rather than re-reading the newest page, which costs the same single request, keeps the pages you loaded and the row you are on, and is the only way to see an article that entered the feed behind its own publish time. The relevance filter is applied by the server, so filtered-out articles never occupy page slots; moving it with +/- refetches the visible feed, one request per press at most. The Insider view's rollup and trades chart arrive as one bundle alongside the feed's first page and live in the same cache, so the chart costs no extra requests and the g window switch is free. The Earnings view costs one request per ticker, made only while that view is on screen and cached for an hour, because a read is published once a quarter and never changes afterwards. That single response carries the whole history of reads for the ticker and its next confirmed report date, which is also what fills the read shown in the News card, so opening the card still costs nothing. The macro calendar behind the bottom line of that view is one request for the whole market, cached for six hours; if it fails there is simply no line. Feeds page 20 articles at a time, the most every plan allows (50 on Pro keys, detected automatically). Paging back past your plan's archive horizon (30 days on Free, 90 on Basic) shows an upgrade hint instead of older articles. Full API reference: alphai.io/developers.

Ticker forms follow the US/Yahoo convention (AAPL, BTC-USD, VOD.L), which is also what AlphaAI uses. Finnhub-specific symbols like BINANCE:BTCUSDT will not have news attached.

Configuration

~/.config/alphai-tui/config.toml on Linux and macOS (%APPDATA% on Windows), created by the settings screen with mode 0600 since it can hold keys; --config PATH points at a different file. Saving the settings also persists the watchlist on screen. Every key is optional. A misspelled value in the [ui], [chart], [theme] or [keybindings] sections prints a warning on startup and keeps that entry's default; only a TOML syntax error makes the whole file fall back to defaults. The [ui] and [chart] sections set startup defaults; the session keys (x, f, g, +, -, c, m, i, b, e, t) still change everything live without persisting it:

source = "yahoo"
watchlist = ["AAPL", "MSFT", "NVDA", "BTC-USD"]
every = 15
range = "1d"
interval = "5m"
news_open = "alphai"  # where enter opens news: "alphai" or "original"

[keys]
alphai = "ak_live_..."
finnhub = ""
alpaca_key_id = ""
alpaca_secret = ""

[ui]
default_view = "split"    # split | news | table | chart | insider | earnings
quote_rail = true         # the price line under the tabs
bare = false              # start with no header and no footer (--bare, z)
news_layout = "side"      # side | stacked
news_scope = "ticker"     # ticker | market | trending
borders = "rounded"       # panel frames: rounded | plain
news_min_score = 7        # minimum relevance score in news feeds, 1 to 10
insider_min_score = 4     # insider feed filter; the score tracks trade size
insider_chart = "3m"      # insider trades chart window at start: 3m | 12m | off
alphai_ttl_secs = 300     # news/sentiment/insider cache lifetime, 30 to 86400

[chart]
style = "candles"         # candles | line
sma = true                # moving average overlays visible at start
ma_type = "sma"           # sma | ema, both using the periods below
rsi = true                # RSI panel visible at start
volume = true             # volume panel visible at start
extended_hours = false    # draw pre and post market candles (yahoo only)
sma_fast = 20             # 2 to 250
sma_slow = 100            # 2 to 250; also sizes the history warm-up
rsi_period = 14           # 2 to 100
right_margin_pct = 20     # free space right of the newest candle, 0 to 50
presets = [               # the combos the t and T keys cycle
  ["1d", "5m"],
  ["5d", "15m"],
  ["1mo", "60m"],
  ["6mo", "1d"],
  ["1y", "1d"],
]

Colors

[theme] preset swaps in a ready-made palette:

[theme]
preset = "catppuccin-mocha"

alphai-tui cycling through its color presets with the p key: catppuccin mocha, macchiato and frappe, dracula, gruvbox and nord

p and P walk the presets live, --theme catppuccin-mocha picks one for a single run, and the Theme row in the settings screen (s) does both: cycle it with a live preview, Save writes it here.

Available: default, catppuccin-mocha, catppuccin-macchiato, catppuccin-frappe, catppuccin-latte, dracula, gruvbox-dark, gruvbox-light, nord. Presets are written in hex, so they want a terminal with 24-bit color; default uses ANSI names and follows whatever palette the terminal itself is set to. They only set foreground colors, which leaves a transparent or blurred terminal background alone. The two light ones (catppuccin-latte, gruvbox-light) expect a light terminal background.

Every color the views draw comes from a named slot, and the optional [theme] table recolors any of them, over the preset when there is one. Values are ANSI color names (case-insensitive, light-blue, grey), #RRGGBB hex, or an ANSI-256 index written as a string like "245". A bad color, a misspelled slot or an unknown preset prints a warning on startup and keeps the default; it never breaks the config file. The defaults are the values shown:

[theme]
accent = "cyan"          # header title, active tab, overlay borders, headings
accent_text = "black"    # text on the active tab
up = "green"             # price up: candles, deltas, sparklines
down = "red"             # price down
flat = "gray"            # unchanged / no data
pos = "green"            # bullish sentiment, insider buys
neg = "red"              # bearish sentiment, insider sells
error = "red"            # error messages
warn = "yellow"          # notices and the editing highlight
score_high = "yellow"    # relevance score 8 to 10
sma_fast = "yellow"      # fast moving average overlay
sma_slow = "magenta"     # slow moving average overlay
rsi_line = "cyan"        # RSI line
ref_line = "darkgray"    # previous close and RSI 30/70 reference lines
border = "reset"         # panel frames; reset keeps the terminal's foreground

Custom keybindings

The optional [keybindings] table rebinds any action. An action you list replaces its default keys entirely; actions you leave out keep theirs. The value is one key or a list of keys:

[keybindings]
quit = "ctrl-q"
open = ["enter", "w"]
next_preset = "]"
prev_preset = "["

A key is written as [ctrl-][alt-][shift-]<base>, where base is a single character or one of the named keys: esc, enter, tab, backtab, space, up, down, left, right, home, end, pgup, pgdn, backspace, delete, insert, f1 to f12. shift- plus a letter means the uppercase letter (shift-t equals T), and shift-tab equals backtab.

The actions: quit, next_view, prev_view, settings, help, refresh, up, down, left, right, page_up, page_down, open, card, cycle_scope, cycle_layout, score_up, score_down, insider_chart, chart_style, toggle_sma, toggle_rsi, toggle_volume, ma_type, next_preset, prev_preset, next_theme, prev_theme. The ? help overlay shows this list with the current keys next to it.

Reserved and never remappable: ctrl-c (force quit), esc, the digits 1 to 9 (view hotkeys), and the keys inside the settings form. A bad or reserved key, an unknown action, or a key claimed by two actions prints a warning on startup and falls back safely; the footer always shows the keys that are actually bound.

Not configurable on purpose

The AlphaAI response cache (5 minutes), the feed page sizes, the 2 second poll floor and the chart warm-up factors are fixed. They keep the app a fair citizen of the free API tiers, and a config knob for them would turn an innocent-looking file into an abuse vector. ALPACA_FEED, ALPHAI_API_URL and ALPACA_DATA_URL stay env-only debug overrides for the same reason.

Architecture

src/
  domain.rs      Quote, Candle, TickerData, Range/Interval
  config.rs      config file load/save (CLI > env > file > defaults)
  source/        DataSource trait + implementations
    registry.rs  the one place a new source registers; CLI, settings and keys derive from it
    http.rs      shared client builder, JSON fetching and error helpers
    yahoo.rs     Yahoo v8 chart endpoint (quote + history in one call)
    finnhub.rs   Finnhub /quote with synthetic session history
    alpaca.rs    snapshot + real historical bars (IEX/SIP feeds, crypto)
  alphai.rs      AlphaAI API client + demand-driven fetch task (TTL cache)
  keymap.rs      semantic actions + the key table (footer hints derive from it)
  theme.rs       semantic color palette ([theme] overrides)
  indicators.rs  SMA, EMA and RSI (Wilder smoothing)
  poller.rs      fetches all symbols concurrently on a timer -> mpsc channel
  app.rs         App state, event loop, key handling
    app/feeds.rs     feed cache and every AlphaAI request-budget guard
    app/settings.rs  settings overlay state, rows derived from the registry
  ui/            View trait + implementations
    table.rs     watchlist table
    chart.rs     candlestick + line chart, SMA overlays, volume and RSI panels
    split.rs     table + chart
    news.rs      article list + sentiment rollup + detail pane
    insider.rs   Form 4 rollup + filing list
    insider_chart.rs  log-scale trades scatter + weekly dollar bars
    earnings.rs  structured earnings read + the schedule line
    article.rs   modal full-article card (AI analysis, context)
    settings.rs  modal settings overlay

Data flows one way: background tasks (price poller, AlphaAI fetcher) push events over an mpsc channel into App::apply; views are stateless renderers over &mut App. The UI never blocks on the network, and every AlphaAI request-budget guard lives in one file (app/feeds.rs).

Adding a price source

  1. Implement source::DataSource (one async fetch returning quote plus candles) in a new module under src/source/. The helpers in source/http.rs cover the client, JSON fetching and error plumbing.
  2. Append one SourceInfo entry to source/registry.rs: id, aliases, a settings hint, the key fields it needs and a constructor. The --source help and error list, the settings screen rows and picker cycle, config [keys] persistence and env-var overrides all derive from that entry, and the registry tests check it.
  3. Describe the source in this README.

Adding a view

  1. Implement ui::View as a unit struct in a new module under src/ui/: a stateless render over &mut App, a new ViewId variant, a footer hint line, and the capability methods (feed_shown, navigates_articles, has_chart_panel) that opt into the shared key handling and the demand-driven AlphaAI fetch. Views never fetch anything themselves.
  2. Add it to ui::VIEWS. Order in that array defines the tab cycle and the 1..9 hotkeys; the header pills and the footer hints derive from it.

Development

cargo test          # unit + TestBackend rendering tests
cargo clippy --all-targets
cargo fmt --all -- --check
cargo run -- --once AAPL             # network smoke test without a TTY
ALPHAI_API_KEY=ak_live_... cargo test live_api -- --ignored   # live API smoke

CI runs the first three on every push and pull request, on Linux, macOS and Windows. What changed in each release is in CHANGELOG.md.

Issues and PRs are welcome.

License

MIT. Not investment advice; data comes from third-party sources and can be delayed or wrong. Respect the terms of the data providers you enable.

Contributors

makeev

79 commits

Languages

Rust

96.4%

Shell

2.9%