Bitfinex Bitcoin Ticker
A live Bitcoin price in the Omarchy bar, streamed over a WebSocket from the public Bitfinex v2 API — no polling, no API key, no account.

The pill is the price alone, in the theme foreground, so it reads as part of the bar rather than as a coloured badge. Clicking it opens a panel with the 24h change, a sparkline, and the rest of the ticker.
Defaults to Bitcoin, and quotes it in your locale's currency.
Requirements
Omarchy with the Quickshell-based omarchy-shell. Nothing else — no extra
packages, no API key, no account.
Install
omarchy plugin add https://github.com/ismyhc/omarchy-bitfinex-ticker.git
omarchy plugin enable io.github.ismyhc.bitfinex-ticker
Plugins land disabled so you can read the code before it runs; enable puts
the widget in the bar. To move it:
omarchy bar move io.github.ismyhc.bitfinex-ticker --section right
That is the whole install. The price should appear within a second or two.
Optional: live streaming
Out of the box the price is polled from Bitfinex's REST ticker every 10 seconds. Install Qt's WebSocket QML module and the widget switches itself to the live websocket stream instead, updating as trades happen:
omarchy pkg add qt6-websockets
omarchy restart shell
Nothing else changes, and nothing needs configuring — the widget detects the
module at load. It is 441 KiB from Arch's extra repo and depends on nothing
Omarchy does not already have. The panel's header says which source is in use
(Bitfinex · live or Bitfinex · 10s poll), and polling resumes on its own
whenever the stream drops, so a reconnect or a suspend never leaves the price
frozen.
Remove
omarchy plugin remove io.github.ismyhc.bitfinex-ticker
That deletes ~/.config/omarchy/plugins/io.github.ismyhc.bitfinex-ticker/ and
drops the widget from your bar layout. Nothing else is touched: the plugin
writes only its own entry in ~/.config/omarchy/shell.json, keeps no state
elsewhere on disk, and installs no services, timers, or system packages.
If you installed qt6-websockets for this, it stays — remove it with
omarchy pkg drop qt6-websockets if nothing else needs it.
Currency
currency defaults to auto, which reads your desktop locale: an en_US
session streams tBTCUSD and shows $68,292, a de_DE one streams tBTCEUR
and shows 58.659 €. Grouping, decimal separator, and symbol placement all
follow the locale.
Bitfinex only quotes BTC against USD, EUR, GBP and JPY. A locale on any
other currency falls back to USD rather than subscribing to a pair that does
not exist. Set currency explicitly to override, or symbol to name a
Bitfinex pair directly.
Other assets
base takes any asset Bitfinex lists, so "base": "ETH" gives you
tETHUSD. Change icon to match — the default ₿ is Bitcoin's. For a pair
the base/currency mapping does not cover, set symbol to the exact
Bitfinex pair (e.g. "symbol": "tSOL:USD"), which overrides both.
allowMultiple is on, so several entries with different assets can sit in the
bar at once.
Interactions
| Gesture | Action |
|---|---|
| left click | open the detail panel |
| right click | bring the 24h change into the bar beside the price, tinted by direction (persisted) |
| middle click | force a reconnect and refetch the history |
The panel
- the price at display size, with the 24h move in percent and absolute terms
- a 24h sparkline — 96 fifteen-minute closes over REST, right edge tracking the live ticker — with the day's open drawn as a dotted reference
- bid, ask, 24h high/low, spread, and volume
History is fetched when the panel opens and refreshed once a minute while it stays open; the ticker websocket channel carries no history of its own.
Settings
Set inline on the widget's entry in ~/.config/omarchy/shell.json:
{
"id": "io.github.ismyhc.bitfinex-ticker",
"currency": "auto",
"compact": false
}
| Key | Default | Meaning |
|---|---|---|
currency |
auto |
auto (locale), or USD / EUR / GBP / JPY |
base |
BTC |
base asset, e.g. ETH |
symbol |
— | explicit Bitfinex pair; overrides currency and base |
icon |
₿ |
glyph before the price (nerd font) |
decimals |
-1 |
price decimals; -1 picks by magnitude |
showChange |
false |
also show the 24h change in the bar |
showCurrency |
true |
show the currency symbol |
compact |
false |
68.3k instead of 68,292 |
pollSeconds |
10 |
seconds between REST reads while not streaming (min 5) |
upColor |
#2ea043 |
tint for a positive 24h change |
downColor |
theme urgent | tint for a negative 24h change |
Themes carry no green role, so the up colour is a plain default; the down colour rides the theme's urgent (its red). Both accept any QML colour string.
Vertical bars always use the compact form — a grouped six-figure price does not fit 28px.
IPC
omarchy-shell io.github.ismyhc.bitfinex-ticker price # last price on stdout
omarchy-shell io.github.ismyhc.bitfinex-ticker toggle # open/close the panel
omarchy-shell io.github.ismyhc.bitfinex-ticker refresh # reconnect + refetch history
omarchy-shell io.github.ismyhc.bitfinex-ticker toggleChange # show/hide the 24h change
Network access
The plugin talks to two Bitfinex endpoints and nothing else:
| Endpoint | When |
|---|---|
https://api-pub.bitfinex.com/v2/ticker/<pair> |
every pollSeconds while not streaming |
wss://api-pub.bitfinex.com/ws/2 |
continuously, when qt6-websockets is installed |
https://api-pub.bitfinex.com/v2/candles/... |
on panel open, then once a minute while it is open |
All three are public and unauthenticated. Nothing is sent but the pair being watched, no credentials are stored or read, and no other host is contacted.
Files
| File | Role |
|---|---|
manifest.json |
plugin declaration, defaults, settings schema |
BarWidget.qml |
the pill: currency resolution, formatting, gestures, IPC |
Panel.qml |
detail popup |
Sparkline.qml |
the 24h canvas |
Feed.qml |
ticker state, REST polling, and the candle history |
Stream.qml |
the optional WebSocket |
Format.js |
locale-aware number, currency, and pair formatting |
Stream.qml is deliberately separate: it is the only file that imports
QtWebSockets, so a missing qt6-websockets fails one Loader instead of the
widget's own import. Feed.qml imports nothing but QtQuick — the fallback
cannot depend on the thing it is a fallback for.
Both sources produce the same value. /v2/ticker returns the identical
10-field array the stream pushes, so one applyTicker handles both and the
widget never has to know where a price came from.
Notes for anyone hacking on this
manifest.jsondefaults win.barWidget.defaultsare merged into the widget'ssettingsbefore it sees them, so those values — not the QML fallbacks inBarWidget.qml— are what an entry without the key resolves to. Keep the two in step.- Candle order.
/v2/candles/...?limit=96returns the newest 96, newest-first. Addingsort=1does not sort that window ascending — it makeslimitselect the oldest 96 candles Bitfinex holds, which fortBTCUSDis March 2013 at $93. Fetch newest-first and reverse client-side. - The fallback is not just for missing packages. Polling runs whenever the stream is not carrying the price — including the first connect and every reconnect gap — so the two paths are exercised together on every machine, not only on ones without the module.
- Half-dead sockets. Bitfinex sends
[CHAN_ID, "hb"]roughly every 15s when the book is quiet. Wi-Fi handoff and resume-from-suspend can kill the connection without a close frame, so a 45s silence forces a reconnect; otherwise the price silently freezes. Reconnects back off 1s → 30s. baselineis taken. It is a final property onItem, so a custom one will not bind —Sparkline.qmlcalls its day-open linereference.- Developing against a symlink. Symlinking a working copy into
~/.config/omarchy/plugins/works for discovery, butinotifywait -rdoes not follow it, so edits will not hot-reload — runomarchy-shell shell rescanPlugins, oromarchy restart shellfor changes the reload does not pick up. Pointomarchy plugin validateat the real directory; it rejects symlinks.
Licence
MIT — see LICENSE.
Not affiliated with Bitfinex. Market data is provided as-is by their public API; do not trade off a status bar.