UI Intricacies & Pitfalls¶
The UI reference tells you what every button does. This page is about how the window behaves at runtime — the things that trip people up the first time they hit them. Nothing here will crash the app; the failure mode is almost always "the chart silently does something less useful than you expected."
1. Tutorial overlay¶
The first time the app runs is a lot of controls to scan. A guided tour narrates each one with a dimmed-screen + spotlight callout:
- End user (Windows installer) — Start menu → Compare My Stocks (Tutorial).
- Developer — pass
--tutorial(manual: Next/Skip buttons) or--tutorial-auto(auto-advances every ~5s). See CLI → Tutorial.
The tour is also the canonical answer to "what's the currency checkbox actually doing" — it documents the tri-state (§ 4 below) that the plain control reference glosses over.
2. Status bar (bottom edge)¶
Six pills along the bottom edge tell you at a glance which data sources the app can actually reach:
| Pill | Green ("connected" / "set") means |
|---|---|
| IB | IB sidecar is up and the Pyro5 connection is alive. |
| Flex | TransactionHandlers.IB.FlexToken is configured. |
| RAPID StockPrices | StockPricesHeaders.X_RapidAPI_Key is set (yfinance via RapidAPI). |
| RAPID SeekingAlpha | SeekingAlphaHeaders.X_RapidAPI_Key is set. |
| RAPID YFinance | Jupyter.RapidYFinanaceKey is set. |
| Polygon | Sources.PolySource.Key is set. |
If anything is red, a ⚠ Select Config/Help → Help to fix hint appears
next to the pills. The bar auto-hides ~4 s after startup to save
vertical space; move the cursor to the bottom edge to bring it back
(stays visible for ~3 s past the last hover). State refreshes every 2 s.
For the underlying config fields, see CONFIGURATION → Sources, CONFIGURATION → TransactionHandlers, and the config_help.md walkthrough.
3. Auto-update, the update queue, and the "Update is waiting" hint¶
With Auto Update on
(UI § 13), almost every control change posts
an update onto a background worker. The worker is single-threaded and
buffers up to 3 pending updates. Beyond that, additional changes
are dropped and the status line shows Update is waiting (generating
graph probably).
What this means in practice:
- Don't spam controls during a long redraw. Wait for the previous update to land, or toggle Auto Update off, make all your changes, then toggle it back on (it will recompute once).
- A change that "didn't take" is almost always dropped, not buggy. Toggle the control off and on again — that re-posts the request.
- Programmatic widget rewrites (saved-graph load, currency conversion,
setting controls from
Parameters) are protected by a reentrancy guard so they don't fire spurious updates. If you see "ignored (ignore_updates_for_now)" in the debug log, that's the guard doing its job — it's not an error.
4. Adjust Currency is tri-state, not on/off¶
adjust_currency looks like a normal checkbox but Qt renders three
states; the tutorial labels them as:
| State | Effect |
|---|---|
| Checked (✓) | Convert everything to the currency in Current Currency (e.g. ILS). |
| Partially checked (■, "square") | Show everything in Symbols.Basecur (usually USD). FX is still applied where needed to normalize to the base. |
| Unchecked | Leave each ticker in its native trading currency. No FX conversion at all. |
The control reference (UI § 11) only describes the on/off poles — click the box twice if a single click doesn't get you where you want. The "square" state is the difference between "USD everywhere" and "each stock in its own currency".
Underlying config is Symbols.Basecur and the currency-pair fetch
logic documented in CONFIGURATION → Symbols.
5. Chart interactions (pan / zoom)¶
The chart pane is a pyqtgraph canvas. Mouse interactions are:
- Scroll wheel — zoom on the cursor.
- Drag — pan.
- Right-click — pyqtgraph's built-in context menu (auto-range, axis options, export).
- Hover — tooltip with the ticker and y-value at the cursor.
Important behavior: zooming the chart updates the Start / End
date pickers visually so you can see the new range, but it does not
re-run filters, re-query data, or write back to Parameters. The chart
is a free interaction surface. If you want the zoom to stick — apply
filters at the new range, persist it to a saved graph, fetch missing
data — pick the dates explicitly (UI § 10) or
drag the Date range slider (daterangepicker). Only the date
controls trigger the data path.
6. Refresh: how it actually fetches¶
Refresh Stocks (UI § 13) is the only control that talks to the data source. Three behaviors worth knowing:
- End date is ignored during fetch. The display filter's End date does not cap the request — the app fetches up to now, then re-applies the End filter for display. You can't "stop fetching at 2023" by setting the End date.
- Click Refresh a second time to cancel. While a refresh is in
flight, the button label changes to Stop Refreshing. Clicking it
sets
cancel_requested = True; the worker checks between symbols and breaks out cleanly. The button becomes Stopping… until the current symbol completes. - Min Refresh (default on) only fetches the missing tail — fast but skips correcting silent gaps inside the existing cache. Turn it off for a "full re-pull" when you suspect cached data is wrong.
Symbol resolution is async: typing a ticker and hitting Add to / < Add to queues it for background processing. The chart updates once the queue drains.
7. Saved graphs: load behavior and persistence¶
The Saved graphs list (UI § 12) persists more than it looks like.
- Auto-load at startup — controlled by
Running.LoadLastAtBeginandRunning.LastGraphNamein config. When on (default), the app silently reloads the last graph you used on launch. To start clean, clear those fields inmyconfig.yaml. - Transient fields are stripped. Internal control flags
(
reset_ranges, …) are not written tographs.jsonso the on-disk format stays stable across app versions. - Loading a graph is reentrancy-heavy. The compare-with combo can
re-emit
currentTextChangedafter items get reseated; the load path has three layered guards to prevent that from re-triggering an update. Symptom of a bug here: loading a graph "forgets"Compare Withor flipsDo Comparisonoff. If this ever happens, save a different graph and reload — but please file an issue first.
8. Embedded notebook fallback¶
If Voila / Jupyter aren't installed (or fail to start), the notebook
pane is replaced by an explanatory frame with the failure reason,
instead of crashing the app. On a clean Windows install you can run
install\installvoila.bat to fix it. Switching to No Jupyter mode
(UI § 14) hides the area entirely.
9. High-DPI and scaling¶
The startup code sets QT_SCALE_FACTOR automatically based on screen
size. If the UI looks broken (controls clipped, fonts the wrong size),
set Running.TryToScaleDisplay: false in myconfig.yaml — see
CONFIGURATION → Running. After turning it
off you can either rely on the OS-level DPI setting or set
QT_SCALE_FACTOR yourself in the environment before launch.
10. Dev-only: module auto-reload¶
Setting Running.AutoReload: true (or running under PyCharm — the
PYCHARM_HOSTED=1 env var enables it as a convenience) polls loaded
modules every Running.CheckReloadInterval seconds and hot-swaps any
that changed on disk. Set CheckReloadInterval: 0 to disable polling
even when AutoReload is on. Don't ship with this on — it
significantly increases idle CPU usage.
11. Pitfalls — silent "did the wrong thing"¶
The control reference is permissive; the engine isn't. A few combinations are accepted by the UI but quietly degrade. None crash — they just produce a less-useful chart.
- A group name in the Reference list is not expanded. Putting
FANGthere treats it as a literal ticker — you'll get an unrelated stock or a fetch error. To use a group as a reference line, put it in the Groups list with Sum unite (UI § 9) instead. - A group name in
addstock/ Selected (with Use Groups off) — same thing, treated as a literal ticker. Compare Withset to a group works only if the same group is in your active groups list with Sum or Average unite. The app will additionally try to fetch a stock by that name; it'll fail and log a warning — harmless but noisy.- Extending the date range backwards doesn't auto-pull older transactions. Hit Refresh Stocks (UI § 13) after extending the range.
- Filter rows (UI § 6) ignore Reference lines, so loading up the Reference list defeats the "top-N" effect on the primary tickers.
- Operations that need transactions (Value, Total Profit, Unrealized Profit, Realized) silently produce empty lines when you have no transaction data imported. Set up an IB Activity Statement or My Stocks CSV — see QUICKSTART § 4.
- P/E and Price/Sells need an earnings/fundamentals data source. The status bar (§ 2 above) tells you whether RAPID StockPrices / SeekingAlpha are configured. Missing keys → empty lines.
- Symbols that share a name across exchanges are best added through Lookup (UI § 1) so the correct contract is pinned. Typing a bare ticker and pressing Add to uses the data source's default resolution, which can pick the wrong listing for non-US names.
- Refreshing while disconnected (red IB pill — § 2 above) silently leaves the cache untouched for IB-sourced symbols. Connect TWS / IB Gateway first.
- Display modes (UI § 14) affect layout only — they never change what the engine computes. If switching modes "fixes" or "breaks" the chart, the underlying issue is in another control.
See also¶
- UI reference — field-by-field guide to every control.
- Quick start — install and first-graph walkthrough.
- Configuration reference — every field in
myconfig.yaml. - CLI reference — flags and environment variables.