repos

Fix futures symbol pages and backfill ETF profiles on boot

2499055e by Isaac Bythewood · 1 month ago

Fix futures symbol pages and backfill ETF profiles on boot

A future's symbol page showed the generic "no price history, run
make seed" empty state. That advice is wrong: futures have no daily
history by design (Stooq carries no =F data). The page now hides the
chart and key-stats sections for any symbol with no daily history and
shows an honest message instead — a "live quotes only" note for a
future, a plain "no history available" for a historyless index.

Also bring the sec job forward to the first scheduler tick on boot,
the way the history seed is, so a deploy that adds SEC-backed data
(the Phase 18 ETF profiles) backfills within a tick instead of waiting
out the 24h interval. The job is a cheap no-op when nothing is stale.
modified PLAN.md
@@ -1176,6 +1176,19 @@ finance/  nothing is polled unless a browser is viewing it. No futures-hours calendar  is modelled (a closed futures market just returns a flat quote), consistent  with the no-holiday-calendar decision in `market.rs`.- **2026-05-22: futures pages no longer prompt to seed; SEC job runs on boot.**  A future's symbol page showed the generic "no price history, run `make  seed`" empty state, which is wrong: a future has no daily history by design  (Stooq carries no `=F` data) and seeding cannot change that. The symbol page  now hides the daily chart and key-stats sections for any symbol without  daily history and shows an honest message: a short "followed with live  quotes only" note for a future, a plain "no daily history available" for a  historyless index (^RUT, ^VIX). The live quote in the header is unaffected.  Separately, the `sec` scheduler job is now brought forward to the first tick  on boot, the way the history seed is, so a deploy that introduces new  SEC-backed data backfills within a tick instead of waiting out the ~24h  interval. That is what populates the Phase 18 ETF profiles on the production  box right after a deploy; the job is cheap when nothing is stale.---
modified src/scheduler.rs
@@ -110,6 +110,13 @@ pub fn spawn(pool: SqlitePool, config: Arc<Config>, hub: Arc<Hub>) -> JoinHandle            tracing::warn!(                "[scheduler] SEC_CONTACT_EMAIL unset: SEC fundamentals & filings job disabled"            );        } else if let Err(e) = schedule_next(&pool, "sec", now_ms()).await {            // Bring the SEC job forward to the first tick. The sweep is            // resumable and cheap when nothing is stale, so running it on            // each boot is harmless — and it means a deploy that introduces            // new SEC-backed data (e.g. the Phase 18 ETF profiles) backfills            // within a tick instead of waiting out the ~24h interval.            tracing::warn!("[scheduler] bring sec job forward: {e}");        }        // Prune's last-run time is loop-local: a restart simply re-prunes once,
modified templates/pages/symbol.html
@@ -56,6 +56,10 @@    {% endif %}  </header>  {# Chart and key stats need daily history. A future has none by design     (live quotes only), and so do the historyless indexes; those fall to the     explanatory empty state below instead of an empty chart. #}  {% if stats %}  <section class="panel chart-panel">    <div class="chart-bar">      <div class="range-bar">
@@ -86,7 +90,6 @@    <div id="chart" data-ticker="{{ symbol.ticker }}"></div>  </section>  {% if stats %}  <h2 class="section-title">Key stats</h2>  <section class="keystats">
@@ -184,7 +187,13 @@  </section>  {% else %}  <section class="empty">    <p>No price history for {{ symbol.ticker }} yet. Run <code>make seed</code> or wait for the next history sync.</p>    {% if symbol.kind == 'future' %}    <p><strong>{{ symbol.ticker }}</strong> is a futures contract. Finance follows    futures with live quotes only, so there is no historical daily chart; the    price above updates live while the market is trading.</p>    {% else %}    <p>No daily price history is available for {{ symbol.ticker }}.</p>    {% endif %}  </section>  {% endif %}