Spaces:
Paused
Paused
fix: Auto-fetch prices from yfinance if DB is empty on startup
Browse files- dashboard/app.py +45 -0
dashboard/app.py
CHANGED
|
@@ -309,6 +309,50 @@ def _inject_css():
|
|
| 309 |
def _ensure_schema():
|
| 310 |
init_schema()
|
| 311 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 312 |
@st.cache_data(ttl=3600)
|
| 313 |
def _load_forecast(symbol: str) -> dict:
|
| 314 |
return predict(symbol)
|
|
@@ -1036,6 +1080,7 @@ def _render_sidebar() -> tuple[str, int]:
|
|
| 1036 |
|
| 1037 |
def main():
|
| 1038 |
_ensure_schema()
|
|
|
|
| 1039 |
_inject_css()
|
| 1040 |
_render_header()
|
| 1041 |
|
|
|
|
| 309 |
def _ensure_schema():
|
| 310 |
init_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
|
| 350 |
+
except Exception:
|
| 351 |
+
pass
|
| 352 |
+
conn2.close()
|
| 353 |
+
except Exception:
|
| 354 |
+
pass
|
| 355 |
+
|
| 356 |
@st.cache_data(ttl=3600)
|
| 357 |
def _load_forecast(symbol: str) -> dict:
|
| 358 |
return predict(symbol)
|
|
|
|
| 1080 |
|
| 1081 |
def main():
|
| 1082 |
_ensure_schema()
|
| 1083 |
+
_ensure_prices()
|
| 1084 |
_inject_css()
|
| 1085 |
_render_header()
|
| 1086 |
|