06/01 Adding porfolio charts
This commit is contained in:
@@ -102,6 +102,81 @@ def _parse_v8_price(data):
|
||||
return None
|
||||
|
||||
|
||||
TIMEFRAME_MAP = {
|
||||
'1W': ('5d', '1d'),
|
||||
'1M': ('1mo', '1d'),
|
||||
'3M': ('3mo', '1d'),
|
||||
'6M': ('6mo', '1wk'),
|
||||
'1Y': ('1y', '1wk'),
|
||||
}
|
||||
|
||||
|
||||
def fetch_price_history(ticker, timeframe='1M'):
|
||||
"""
|
||||
Fetch historical closing prices for a ticker via Yahoo Finance v8 API.
|
||||
timeframe: '1W' | '1M' | '3M' | '6M' | '1Y'
|
||||
|
||||
Returns dict:
|
||||
ticker, current, prev_close, day_change, day_change_pct,
|
||||
period_change, period_change_pct, dates, closes, timeframe
|
||||
Returns None on failure.
|
||||
"""
|
||||
if not ticker:
|
||||
return None
|
||||
|
||||
ticker = ticker.upper().strip()
|
||||
yf_range, yf_interval = TIMEFRAME_MAP.get(timeframe, ('1mo', '1d'))
|
||||
|
||||
for subdomain in ('query1', 'query2'):
|
||||
url = (
|
||||
f'https://{subdomain}.finance.yahoo.com/v8/finance/chart/{ticker}'
|
||||
f'?range={yf_range}&interval={yf_interval}&includePrePost=false'
|
||||
)
|
||||
try:
|
||||
resp = requests.get(url, headers=HEADERS, timeout=15)
|
||||
if resp.status_code != 200:
|
||||
continue
|
||||
data = resp.json()
|
||||
result = data.get('chart', {}).get('result')
|
||||
if not result:
|
||||
return None
|
||||
|
||||
timestamps = result[0].get('timestamp', [])
|
||||
closes_raw = result[0]['indicators']['quote'][0].get('close', [])
|
||||
pairs = [(t, c) for t, c in zip(timestamps, closes_raw) if c is not None]
|
||||
if not pairs:
|
||||
return None
|
||||
|
||||
dates = [datetime.utcfromtimestamp(t).strftime('%Y-%m-%d') for t, _ in pairs]
|
||||
closes = [round(float(c), 4) for _, c in pairs]
|
||||
|
||||
current = closes[-1]
|
||||
prev = closes[-2] if len(closes) > 1 else current
|
||||
day_change = round(current - prev, 4)
|
||||
day_change_pct = round(day_change / prev * 100, 2) if prev != 0 else 0
|
||||
period_change = round(current - closes[0], 4)
|
||||
period_change_pct = round(period_change / closes[0] * 100, 2) if closes[0] != 0 else 0
|
||||
|
||||
log.info('[investment] %s history: %d points (%s)', ticker, len(closes), timeframe)
|
||||
return {
|
||||
'ticker': ticker,
|
||||
'current': current,
|
||||
'prev_close': prev,
|
||||
'day_change': day_change,
|
||||
'day_change_pct': day_change_pct,
|
||||
'period_change': period_change,
|
||||
'period_change_pct': period_change_pct,
|
||||
'dates': dates,
|
||||
'closes': closes,
|
||||
'timeframe': timeframe,
|
||||
}
|
||||
except Exception as exc:
|
||||
log.warning('[investment] %s history fetch failed (%s): %s', ticker, subdomain, exc)
|
||||
|
||||
log.error('[investment] %s: history fetch failed on all subdomains', ticker)
|
||||
return None
|
||||
|
||||
|
||||
def update_prices(investment_ids=None):
|
||||
"""
|
||||
Update current_price for all (or specified) investments with a ticker.
|
||||
|
||||
Reference in New Issue
Block a user