06/01 Optimize porfolio page

This commit is contained in:
2026-06-01 17:34:33 -04:00
parent 7815060e89
commit b19c202786
2 changed files with 56 additions and 14 deletions
+46 -10
View File
@@ -132,11 +132,17 @@ def _extract_day_change_from_meta(meta):
def fetch_day_change(ticker): def fetch_day_change(ticker):
""" """
Lightweight call: fetch today's open price and current price only. Fetch today's open-to-current day change for a ticker.
Uses meta.regularMarketOpen / meta.regularMarketPrice from Yahoo Finance.
Strategy (in order):
1. meta.regularMarketOpen + meta.regularMarketPrice (most accurate)
2. Last bar open[] + last bar close[] from the OHLC array (fallback)
Uses range=5d so the API always returns data even on weekends / holidays
when range=1d would return an empty result set.
Returns dict: {ticker, open, current, day_change, day_change_pct} Returns dict: {ticker, open, current, day_change, day_change_pct}
or None on failure. or None on complete failure.
""" """
if not ticker: if not ticker:
return None return None
@@ -145,22 +151,50 @@ def fetch_day_change(ticker):
for subdomain in ('query1', 'query2'): for subdomain in ('query1', 'query2'):
url = ( url = (
f'https://{subdomain}.finance.yahoo.com/v8/finance/chart/{ticker}' f'https://{subdomain}.finance.yahoo.com/v8/finance/chart/{ticker}'
f'?range=1d&interval=1d&includePrePost=false' f'?range=5d&interval=1d&includePrePost=false'
) )
try: try:
resp = requests.get(url, headers=HEADERS, timeout=10) resp = requests.get(url, headers=HEADERS, timeout=10)
if resp.status_code != 200: if resp.status_code != 200:
log.warning('[investment] %s day-change: HTTP %s (%s)', ticker, resp.status_code, subdomain)
continue continue
result = resp.json().get('chart', {}).get('result')
chart_data = resp.json().get('chart', {})
if chart_data.get('error'):
log.warning('[investment] %s day-change: API error %s', ticker, chart_data['error'])
continue
result = chart_data.get('result')
if not result: if not result:
return None log.warning('[investment] %s day-change: empty result (%s)', ticker, subdomain)
continue
meta = result[0].get('meta', {}) meta = result[0].get('meta', {})
open_p, curr, chg, chg_pct = _extract_day_change_from_meta(meta)
if open_p is None:
return None
log.info('[investment] %s day change: open=%.4f current=%.4f chg=%.4f (%.2f%%)', # Strategy 1: meta fields (true intraday open → current)
open_p, curr, chg, chg_pct = _extract_day_change_from_meta(meta)
# Strategy 2: fall back to last OHLC bar open/close
if open_p is None or curr is None:
try:
quote = result[0]['indicators']['quote'][0]
valid_opens = [v for v in quote.get('open', []) if v is not None]
valid_closes = [v for v in quote.get('close', []) if v is not None]
if valid_opens and valid_closes:
open_p = float(valid_opens[-1])
curr = float(valid_closes[-1])
chg = round(curr - open_p, 4)
chg_pct = round(chg / open_p * 100, 2) if open_p != 0 else 0
log.info('[investment] %s day-change: using OHLC fallback', ticker)
except (KeyError, IndexError, TypeError) as exc:
log.warning('[investment] %s day-change: OHLC fallback failed: %s', ticker, exc)
if open_p is None or curr is None:
log.warning('[investment] %s day-change: no open/current available (meta keys: %s)',
ticker, list(meta.keys())[:10])
continue
log.info('[investment] %s day-change: open=%.4f current=%.4f chg=%.4f (%.2f%%)',
ticker, open_p, curr, chg, chg_pct) ticker, open_p, curr, chg, chg_pct)
return { return {
'ticker': ticker, 'ticker': ticker,
@@ -172,6 +206,8 @@ def fetch_day_change(ticker):
except Exception as exc: except Exception as exc:
log.warning('[investment] %s day-change fetch failed (%s): %s', ticker, subdomain, exc) log.warning('[investment] %s day-change fetch failed (%s): %s', ticker, subdomain, exc)
log.error('[investment] %s: day-change fetch failed on all subdomains', ticker)
return None return None
+10 -4
View File
@@ -331,13 +331,19 @@
if (data && data.day_change !== undefined) { if (data && data.day_change !== undefined) {
const badge = cell.querySelector('.chg-badge') || cell; const badge = cell.querySelector('.chg-badge') || cell;
badge.className = 'chg-badge ' + chgClass(data.day_change); badge.className = 'chg-badge ' + chgClass(data.day_change);
badge.title = `Open: ${fmtPrice(data.open)}Current: ${fmtPrice(data.current)}`; badge.title = `Today: open ${fmtPrice(data.open)}current ${fmtPrice(data.current)}`;
const sign = data.day_change >= 0 ? '+' : '';
badge.innerHTML = badge.innerHTML =
`<i class="bi ${chgIcon(data.day_change)}"></i>` + `<i class="bi ${chgIcon(data.day_change)}"></i>` +
(data.day_change >= 0 ? '+' : '') + `${sign}${parseFloat(data.day_change_pct).toFixed(2)}%` +
parseFloat(data.day_change_pct).toFixed(2) + '%'; `<span style="font-size:10px;opacity:.7;margin-left:3px;">${sign}${fmtPrice(data.day_change)}</span>`;
} else { } else {
cell.innerHTML = '<span class="text-muted" style="font-size:12px;">—</span>'; const badge = cell.querySelector('.chg-badge');
if (badge) {
badge.className = 'chg-badge chg-flat';
badge.title = 'Price data unavailable — check System Logs';
badge.textContent = '—';
}
} }
}); });
}); });