Skip to main content

Stock / equities ticker widget for led-ticker (Finnhub).

Project description

led-ticker-stocks

A stock / equities ticker plugin for led-ticker, backed by the Finnhub API. It contributes a stocks.ticker Container widget that cycles one price view per configured symbol, trend-colored green/red and dimmed by market state (full brightness while the market is open, dimmed while pre/post-market, dimmest while closed). Three layouts are registered and auto-selected by panel geometry — see Layouts:

  • crawl (smallsign, ~160px) — a single scrolling line: SYM price ▲/▼ change change%.
  • card (bigsign, ~256px) — a held hero card: symbol + price + change, a brand chip, a sparkline, a state-chip label, and paging dots.
  • dashboard (longboi, ~512px) — a held trading dashboard: the same hero block plus a watch column showing the next symbols and a bigger sparkline.

All three layouts animate: the price line pops white on a change and settles back to amber (Bloomberg-style flash), the LIVE state chip breathes while the market is open, and the sparkline's tip pulses. Scope: US equities only. FX/forex is out of scope — see Equities only.

Prerequisites

  • A working led-ticker install.
  • Internet access on the Pi (the widget calls the Finnhub REST API).
  • A free Finnhub account and API token — or run in demo = true mode with no token at all (see Demo mode).

Install

The widget auto-registers via the led_ticker.plugins entry point — once the package is installed, no [plugins] config change is needed.

Into a containerized led-ticker (recommended): add this package to config/requirements-plugins.txt (copy it from config/requirements-plugins.example.txt), then restart:

# in your led-ticker checkout
cp config/requirements-plugins.example.txt config/requirements-plugins.txt
# add the line below to config/requirements-plugins.txt, then:
docker compose restart
led-ticker-stocks

Standalone (a venv that already has led-ticker):

pip install led-ticker-stocks

See the led-ticker Plugins docs for the constraint-based install the Docker image uses, and pin production signs to an exact version (led-ticker-stocks==0.3.0) so a restart doesn't silently pick up a new release.

Configuration

Reference the widget in a playlist section by type = "stocks.ticker":

[[playlist.section]]
mode = "ticker"

[[playlist.section.widget]]
type = "stocks.ticker"
symbols = ["AAPL", "MSFT", "NVDA", "TSLA"]

The recommended section mode depends on which layout your panel will auto-select (see Layouts below):

  • crawl (smallsign) wants mode = "ticker" (continuous side-by-side scroll) — the same shape as the built-in crypto.coingecko / rss.feed tickers. The crawl text is designed to keep moving; slideshow also works but holds each symbol still with nothing to look at until the next transition.
  • card / dashboard (bigsign / longboi) are held layouts — they paint a full static view per symbol and don't scroll. Use mode = "slideshow" with a hold_time long enough to read (the smoke configs in examples/ use 5.2).

New to led-ticker configs? The first-config tutorial walks through the overall structure — the block above shows just the stocks-specific keys.

Options

Option Type Default Description
symbols list of strings Required. Ticker symbols (e.g. ["AAPL", "MSFT"]). US equities only — a symbol containing / (e.g. "EUR/USD") fails validation with a message pointing at the FX limitation below.
layout string auto Force a specific render layout ("crawl", "card", or "dashboard"). Omit this field and let the widget auto-select by real panel width — see Layouts.
demo bool false Run against a seeded, offline random-walk feed instead of Finnhub — no token, no network call. See Demo mode.
update_interval int 60 Seconds between Finnhub polls. The widget silently raises this to len(symbols) + 1 if you set it lower — see Rate limits below.
padding int 6 Horizontal spacing (logical px) after each symbol's segment, before the next symbol. Crawl layout only — card/dashboard use a fixed layout grid instead.
green_up bool true Set false to flip the up/down colors (green-down/red-up) for non-US market conventions. Applies to all three layouts.

At least symbols must be a non-empty list — the widget fails at config validation otherwise (led-ticker validate catches this before boot).

Layouts

resolve_layout auto-selects one of three registered layouts by the REAL (physical) panel width — a layout = override always wins if set:

Layout Auto-selected at Built for Shape
crawl ≤160px wide smallsign (160×16) Single scrolling line per symbol: SYM price ▲/▼ change change%.
card ~160–400px wide bigsign (256×64) Held hero card: brand chip + symbol + price + change line, a sparkline, a state-chip label, and paging dots showing position in the symbol list.
dashboard ≥400px wide longboi (512×64) Held trading dashboard: the same hero block (chip + symbol + price + change + a "PREV" reference price) plus a bigger sparkline and a watch column listing the next 3 symbols with their percent change, and paging dots.

card and dashboard are both held — they paint a complete static frame per symbol rather than scrolling (see the mode guidance above). Every symbol renders a brand chip: an abstract two-tone diagonal block, deterministically colored from a hash of the symbol (trademark-safe — never a logo or wordmark); chip_colors on SymbolQuote lets a future data source override the pair per-symbol.

The dashboard's watch column reads the widget's own full symbols list — it shows the neighbors of the currently-focused symbol, not anything from another widget. Give the stocks widget its own section (no sibling widgets) on longboi so the watch column always reflects this widget's symbol rotation.

Both held layouts animate: the price flashes white then settles back to amber on a change, the LIVE state chip breathes while the market is open, and the sparkline's tip pulses continuously.

Demo mode (no token required)

Set demo = true (or simply omit FINNHUB_API_TOKEN from the environment — an unset token silently routes to the same demo feed, it is not an error) to drive the widget from a deterministic, seeded random-walk price generator instead of live Finnhub data. Useful for previewing the widget, render-demo GIFs, or a sign that doesn't have a Finnhub token yet. See docs/demo.toml for a runnable example.

Demo mode moves the price every step, so the price-flash animation fires continuously (a real Finnhub feed only flashes when a poll observes an actual price change) — it's a good way to preview the flash without waiting for a live tick.

Smoke-test configs (per sign)

Ready-to-run hardware smoke configs live in examples/, one per sign form factor — each with a token-free demo section plus a live section, and a "what to look for" header:

File Sign Notes
config.stocks-smoke.smallsign.toml 160×16 crawl auto-selected, mode = "ticker"
config.stocks-smoke.bigsign.toml 256×64 card auto-selected, mode = "slideshow"
config.stocks-smoke.longboi.toml 512×64 dashboard auto-selected, mode = "slideshow" (widget alone in its section — see Layouts)

Copy one to config/config.toml on the sign, led-ticker validate it, then make restart.

Rate limits & API token

Get a free API token at finnhub.io/register and supply it via the FINNHUB_API_TOKEN environment variable — never put it in config.toml; secrets are env-only in led-ticker (see the Plugins docs):

export FINNHUB_API_TOKEN="your-token-here"

Finnhub's free tier allows 60 requests per minute, per API key — not per widget. Every poll cycle costs len(symbols) + 1 requests (one market-status call plus one quote call per symbol; the widget skips the quote calls entirely while the market is closed, holding last prices instead). The widget enforces effective_interval = max(update_interval, len(symbols) + 1) automatically, so a single stocks.ticker on its own can't blow the budget — but the 60/min ceiling is shared across everything using that token: two signs pointed at the same Finnhub key, or another Finnhub-backed widget/script on the same key, split the same 60 requests. Give each sign (or each concurrent consumer) its own free Finnhub account/token if you're running more than one.

Equities only — FX requires a paid tier

Finnhub's free tier returns HTTP 403 on forex (/forex/*) endpoints. This plugin only implements the equities /quote + /stock/market-status endpoints — FX pairs are out of scope until a paid-tier client ships (not on the current roadmap). validate_config rejects any symbol containing / (the conventional FX pair separator, e.g. "EUR/USD") at config-load time with a message explaining why, rather than letting it fail opaquely at runtime.

layout override

All three layouts ("crawl", "card", "dashboard") are registered — layout = selects among them explicitly. Omit the field and resolve_layout picks by real panel width (see Layouts): ≤160px → crawl, ~160–400px → card, ≥400px → dashboard. An unrecognized layout value fails config validation naming the registered set.

Roadmap

Phase 3 (price-flash on update, sparkline/state-chip pulses) is shipping as stocks-v0.3.0 — this is now the final planned phase for the three canonical layouts (the crawl layout shipped in v0.1.0, card/dashboard in v0.2.0). Deferred / out of scope: new layouts or widgets, indices/FX, a change-field flash, and font_color/border/rainbow-gradient styling knobs on these layouts.

  • Release: docs-site page, catalog provides entry, demo GIFs, stocks-v0.3.0 release.

Development

Install dev deps and run the checks:

uv sync --extra dev      # resolves led-ticker-core from PyPI
uv run pytest -q
uv run ruff check src tests

Note: tests that need a headless canvas obtain one via HeadlessBackend(...).create_canvas() from led_ticker.plugin — the software backend shipped in led-ticker-core. No rgbmatrix test stub or PYTHONPATH plumbing is required.

The plugin imports only the public led_ticker.plugin surface — tests/test_import_purity.py enforces it.

Links

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

led_ticker_stocks-0.3.0.tar.gz (49.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

led_ticker_stocks-0.3.0-py3-none-any.whl (26.6 kB view details)

Uploaded Python 3

File details

Details for the file led_ticker_stocks-0.3.0.tar.gz.

File metadata

  • Download URL: led_ticker_stocks-0.3.0.tar.gz
  • Upload date:
  • Size: 49.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for led_ticker_stocks-0.3.0.tar.gz
Algorithm Hash digest
SHA256 2148d8133f138ce707d13cdbf8d3e3fbb58647cfde281b7d9b36c6eb80bde181
MD5 4ebf74ac976c6984a155e0c88d340db3
BLAKE2b-256 a41138e3bf1d74d559623b1bf1fe649b018804429b55194fba41d9e7ca6dd9a7

See more details on using hashes here.

Provenance

The following attestation bundles were made for led_ticker_stocks-0.3.0.tar.gz:

Publisher: publish.yml on JamesAwesome/led-ticker-plugins

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file led_ticker_stocks-0.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for led_ticker_stocks-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c8ff90a1be97e930c80351ddc99d634ded14923508ee9e3311af119c4af87476
MD5 304261da14e7facca4577a1c658bb721
BLAKE2b-256 6a14924be6e2b68086ee11734ce2984415f58aba007c055a2b62ec65743da326

See more details on using hashes here.

Provenance

The following attestation bundles were made for led_ticker_stocks-0.3.0-py3-none-any.whl:

Publisher: publish.yml on JamesAwesome/led-ticker-plugins

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page