Skip to content

0.22.0 - 2026-09-11

Choose a tag to compare

@github-actions github-actions released this 11 Sep 10:50

Release Notes

  • Portfolio valuations include premarket and after-hours prices whenever
    the source reports them, independently of the extended-candle toggle.
    The portfolio, holding columns, quote rail and JSON use the same price;
    Last marks an extended price with *, and JSON exposes it as
    position.price. Premarket Day is measured from the latest regular
    close; after hours it includes the regular session's move as well.
  • A Portfolio view, 8, for what you actually hold: quantity, average
    price, value, the day's move in money, the profit or loss since you
    bought and the share of the portfolio each ticker carries, with a total
    underneath. It was the one thing the widely used tools here are built
    around and this one had no answer to.
  • p sets the holding for the ticker under the cursor, from any view: a
    quantity and an average price on one line, prefilled with what is held,
    an empty line clearing it. Unlike every other runtime change, it is
    written to the config file straight away, because a quantity and a price
    are your data rather than a display preference.
  • Holdings can be written by hand as [[positions]] entries with
    symbol, qty and avg_price. A fractional quantity is fine, and a
    negative one is a short, which profits when the price falls.
  • A held ticker that is not on the watchlist is polled all the same, so
    every row can be valued; the startup warning about a source's request
    budget counts those tickers too. A row whose first price has not arrived
    says so instead of being summed in as zero, and the total states how
    many rows it covers.
  • The watchlist table grows Value and P&L columns, and the quote rail
    a zone with the same numbers, but only for tickers you hold. Totals that
    span several currencies are labelled mixed currencies: there is no
    conversion here, and a sum across currencies should not look exact.
  • --json carries a position object for a held ticker, with the cost,
    the value, the profit or loss and its percentage, so a status bar can
    show the money instead of the price.
  • The color presets moved from p and P to } and {, which is
    what freed p. The old keys are two lines away in the config:
    [keybindings] next_theme = "p" and prev_theme = "P".
  • The config file is now written to a temporary file and renamed into
    place. It holds API keys, and it is written on every position edit, so a
    half-finished write should never be able to replace it.
  • Prices that survive a source fallback are labelled with their real age
    instead of passing as live: a quote carries the source it came from, and
    a row inherited from the abandoned one keeps the time it was fetched.
    Adding credentials in the settings screen also makes a fallback search
    that had run out of options worth trying again.
  • The quote cache is written out when the app exits, so a session shorter
    than its flush interval still leaves its prices behind for the next
    start, and a failed write backs off like a successful one.
  • News marks respect the length of a candle: a mark lands on the bar whose
    interval actually contains its publish time, so an overnight gap or a
    missing recent bar no longer pins unrelated news to the last candle.

Also in this build

0.21.0 was never tagged on its own, so everything in it ships here too:

  • The price chart marks the ticker's news on the candles it was published
    in: ▲ or ▼ for the AI sentiment call, ◆ when it is neutral, dimmer
    or brighter with the relevance score, and the freshest of them named on
    the chart's bottom border. The scored news was the one thing here no
    other terminal stock tool has, and it lived in a view of its own, so a
    chart-first reader never saw it; now the move and its reason share a
    column. n toggles the marks, [chart] news_markers = false starts
    without them, and the action is rebindable as news_markers.
  • The marks cost no API request. They are drawn from the news bundle the
    Split and News views already keep fresh for the selected ticker, which
    also means they are absent until one of those views has loaded it.
  • Rows older than the first candle on screen are dropped rather than
    pinned to the left edge, and a candle that carried several stories keeps
    the highest-scoring one, so a busy day stays readable.
  • The Split view's footer drops its s settings hint to stay inside 110
    columns, the way the News footer already does. ? still lists it.
  • A blocked or throttled price source no longer means an empty screen. The
    last good quotes and candles are kept in the platform cache directory and
    drawn while the first poll is in flight, labelled cached 2h ago on the
    quote rail until live data lands. Entries older than a week, or taken
    with a different range and interval, are ignored.
  • Yahoo's 429 now says what it is: an IP block that lasts tens of minutes
    (measured: 19 minutes after about ten requests, over an hour for the
    second one that day), with the advice to switch source. It is also no
    longer retried, since another request only feeds the counter that holds
    the block open. Alpaca's 429, which is a genuine transient refusal, keeps
    its retries.
  • When every ticker has been failing for 45 seconds, the app switches to
    another source that has its credentials, keeps the rows already on
    screen, and says so in the footer. It never switches back on its own and
    never returns to a source that failed this session; source_fallback = false in the config turns it off.
  • --json prints the one-shot quote run as a JSON array instead of a text
    table, for status bars and cron jobs: price, the move from the previous
    close, the extended print with its own move from the regular close, the
    day and 52 week ranges, volume and the source. It implies --once.
    Absent figures are left out rather than sent as null, a symbol that
    failed gets a row carrying its error, and warnings stay on stderr so
    stdout is a valid document.