06/01 Optimize porfolio page
This commit is contained in:
@@ -132,11 +132,17 @@ def _extract_day_change_from_meta(meta):
|
||||
|
||||
def fetch_day_change(ticker):
|
||||
"""
|
||||
Lightweight call: fetch today's open price and current price only.
|
||||
Uses meta.regularMarketOpen / meta.regularMarketPrice from Yahoo Finance.
|
||||
Fetch today's open-to-current day change for a ticker.
|
||||
|
||||
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}
|
||||
or None on failure.
|
||||
or None on complete failure.
|
||||
"""
|
||||
if not ticker:
|
||||
return None
|
||||
@@ -145,22 +151,50 @@ def fetch_day_change(ticker):
|
||||
for subdomain in ('query1', 'query2'):
|
||||
url = (
|
||||
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:
|
||||
resp = requests.get(url, headers=HEADERS, timeout=10)
|
||||
if resp.status_code != 200:
|
||||
log.warning('[investment] %s day-change: HTTP %s (%s)', ticker, resp.status_code, subdomain)
|
||||
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:
|
||||
return None
|
||||
log.warning('[investment] %s day-change: empty result (%s)', ticker, subdomain)
|
||||
continue
|
||||
|
||||
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)
|
||||
return {
|
||||
'ticker': ticker,
|
||||
@@ -172,6 +206,8 @@ def fetch_day_change(ticker):
|
||||
except Exception as 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
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user