aaxaxax commited on
Commit
f9048aa
·
1 Parent(s): ffa98be

fix: auto top-up stale prices from yfinance on startup — chart always shows latest data

Browse files
Files changed (1) hide show
  1. dashboard/app.py +42 -21
dashboard/app.py CHANGED
@@ -311,39 +311,60 @@ def _ensure_schema():
311
 
312
  @st.cache_resource
313
  def _ensure_prices():
314
- """Fetch 2 years of price history from yfinance if the DB is empty.
315
- Runs once per process — makes the app self-healing on fresh containers."""
316
- conn = get_conn()
317
- count = conn.execute("SELECT COUNT(*) FROM prices").fetchone()[0]
318
- conn.close()
319
- if count > 100:
320
- return
321
  try:
322
  import yfinance as yf
323
- from data.db import get_conn as _gc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
324
  symbols = list(SYMBOL_NAMES.keys())
325
- ticker_data = yf.download(symbols, period="5y", auto_adjust=True, progress=False)
 
 
 
 
 
 
326
  if ticker_data.empty:
327
  return
328
- conn2 = _gc()
 
329
  for sym in symbols:
330
  try:
331
- if len(symbols) == 1:
332
- df = ticker_data.copy()
333
- else:
334
- df = ticker_data.xs(sym, axis=1, level=1) if sym in ticker_data.columns.get_level_values(1) else None
335
- if df is None or (hasattr(df, 'empty') and df.empty):
336
  continue
337
  df = df.reset_index()
338
  df.columns = [c.lower() for c in df.columns]
339
- for _, row in df.iterrows():
340
  try:
341
  conn2.execute(
342
- "INSERT OR REPLACE INTO prices (date,symbol,open,high,low,close,volume,adj_close) VALUES (?,?,?,?,?,?,?,?)",
343
- [str(row["date"])[:10], sym,
344
- float(row.get("open", 0) or 0), float(row.get("high", 0) or 0),
345
- float(row.get("low", 0) or 0), float(row.get("close", 0) or 0),
346
- float(row.get("volume", 0) or 0), float(row.get("close", 0) or 0)]
 
 
347
  )
348
  except Exception:
349
  pass
 
311
 
312
  @st.cache_resource
313
  def _ensure_prices():
314
+ """Keep prices up to date on every container start.
315
+ - If DB is empty: full 5-year backfill from yfinance.
316
+ - If DB has data but is stale (latest < yesterday): fetch missing days only.
317
+ Runs once per process lifetime."""
 
 
 
318
  try:
319
  import yfinance as yf
320
+ from datetime import date as _date, timedelta
321
+
322
+ conn = get_conn()
323
+ row = conn.execute(
324
+ "SELECT COUNT(*), MAX(date) FROM prices"
325
+ ).fetchone()
326
+ conn.close()
327
+ count, latest_date = row[0], row[1]
328
+
329
+ yesterday = (_date.today() - timedelta(days=1)).isoformat()
330
+
331
+ # Nothing to do if already current
332
+ if count > 100 and latest_date and str(latest_date) >= yesterday:
333
+ return
334
+
335
+ # Full backfill for empty DB, incremental top-up otherwise
336
+ period = "5y" if count < 100 else None
337
+ start = None if period else (str(latest_date) if latest_date else "2020-01-01")
338
+
339
  symbols = list(SYMBOL_NAMES.keys())
340
+ kwargs = dict(auto_adjust=True, progress=False)
341
+ if period:
342
+ kwargs["period"] = period
343
+ else:
344
+ kwargs["start"] = start
345
+
346
+ ticker_data = yf.download(symbols, **kwargs)
347
  if ticker_data.empty:
348
  return
349
+
350
+ conn2 = get_conn()
351
  for sym in symbols:
352
  try:
353
+ df = ticker_data.xs(sym, axis=1, level=1) if len(symbols) > 1 else ticker_data.copy()
354
+ if df is None or df.empty:
 
 
 
355
  continue
356
  df = df.reset_index()
357
  df.columns = [c.lower() for c in df.columns]
358
+ for _, r in df.iterrows():
359
  try:
360
  conn2.execute(
361
+ "INSERT OR REPLACE INTO prices "
362
+ "(date,symbol,open,high,low,close,volume,adj_close) "
363
+ "VALUES (?,?,?,?,?,?,?,?)",
364
+ [str(r["date"])[:10], sym,
365
+ float(r.get("open") or 0), float(r.get("high") or 0),
366
+ float(r.get("low") or 0), float(r.get("close") or 0),
367
+ float(r.get("volume") or 0), float(r.get("close") or 0)]
368
  )
369
  except Exception:
370
  pass