mirror of
https://github.com/shawnkim1997/All-in-one-Financial-Analysis.git
synced 2026-08-14 19:08:06 +00:00
Add asset-type aware market/overview flows, portfolio OCR reverse-engineering with exchange overrides, and interactive index heatmap features. Update README with recent updates and wire backend/frontend APIs for FX matrix, exchange options, and improved portfolio editing flows. Made-with: Cursor
100 lines
3.6 KiB
Python
100 lines
3.6 KiB
Python
"""Commodity future analysis helpers."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from server.utils.ticker_utils import COMMODITY_FUTURES
|
|
|
|
COMMODITY_RELATED: dict[str, list[str]] = {
|
|
"GC=F": ["GLD", "SI=F", "DX-Y.NYB", "^TNX"],
|
|
"CL=F": ["USO", "BZ=F", "XLE", "^GSPC"],
|
|
"SI=F": ["SLV", "GC=F", "HG=F", "^GSPC"],
|
|
"NG=F": ["UNG", "CL=F", "XLE"],
|
|
}
|
|
|
|
|
|
def _get_related_assets(ticker: str) -> list[str]:
|
|
return COMMODITY_RELATED.get(ticker.upper(), [])
|
|
|
|
|
|
async def compute_commodity_correlations(ticker: str, period: str = "1y") -> dict:
|
|
import yfinance as yf
|
|
|
|
t = ticker.upper()
|
|
related = _get_related_assets(t)
|
|
if not related:
|
|
return {}
|
|
all_tickers = [t] + related
|
|
data = yf.download(all_tickers, period=period, auto_adjust=True, progress=False)
|
|
if data is None or data.empty:
|
|
return {}
|
|
close = data["Close"] if "Close" in data else data
|
|
returns = close.pct_change().dropna()
|
|
if returns is None or returns.empty or t not in returns.columns:
|
|
return {}
|
|
corr = returns.corr()
|
|
result = {}
|
|
for r in related:
|
|
if r in corr.columns:
|
|
result[r] = round(float(corr.loc[t, r]), 2)
|
|
return result
|
|
|
|
|
|
async def get_commodity_overview(ticker: str) -> dict:
|
|
import yfinance as yf
|
|
|
|
t = ticker.upper()
|
|
y = yf.Ticker(t)
|
|
info = y.info or {}
|
|
hist_1y = y.history(period="1y", auto_adjust=True)
|
|
hist_10y = y.history(period="10y", auto_adjust=True)
|
|
|
|
seasonal = {}
|
|
if hist_10y is not None and not hist_10y.empty:
|
|
monthly = hist_10y["Close"].resample("ME").last().pct_change().dropna()
|
|
for month in range(1, 13):
|
|
m = monthly[monthly.index.month == month]
|
|
seasonal[month] = round(float(m.mean()) * 100, 2) if len(m) > 0 else 0
|
|
|
|
related = _get_related_assets(t)
|
|
related_cards = []
|
|
if related:
|
|
data = yf.download(related, period="5d", auto_adjust=True, progress=False)
|
|
close = data["Close"] if hasattr(data, "columns") and "Close" in data.columns else data
|
|
if close is not None:
|
|
try:
|
|
if hasattr(close, "columns"):
|
|
for sym in related:
|
|
if sym not in close.columns:
|
|
continue
|
|
s = close[sym].dropna()
|
|
if len(s) < 1:
|
|
continue
|
|
cur = float(s.iloc[-1])
|
|
prev = float(s.iloc[-2]) if len(s) > 1 else cur
|
|
pct = ((cur - prev) / prev * 100) if prev else 0
|
|
related_cards.append({"symbol": sym, "price": round(cur, 2), "change_pct": round(pct, 2)})
|
|
else:
|
|
s = close.dropna()
|
|
if len(s) >= 1:
|
|
cur = float(s.iloc[-1])
|
|
prev = float(s.iloc[-2]) if len(s) > 1 else cur
|
|
pct = ((cur - prev) / prev * 100) if prev else 0
|
|
related_cards.append({"symbol": related[0], "price": round(cur, 2), "change_pct": round(pct, 2)})
|
|
except Exception:
|
|
pass
|
|
|
|
return {
|
|
"name": COMMODITY_FUTURES.get(t, info.get("shortName", t)),
|
|
"price": info.get("regularMarketPrice") or info.get("currentPrice"),
|
|
"open_interest": info.get("openInterest"),
|
|
"volume": info.get("volume"),
|
|
"high_52w": info.get("fiftyTwoWeekHigh"),
|
|
"low_52w": info.get("fiftyTwoWeekLow"),
|
|
"seasonal_pattern": seasonal,
|
|
"related_assets": related_cards,
|
|
"correlation_matrix": await compute_commodity_correlations(t),
|
|
"asset_class": "commodity_future",
|
|
}
|