mirror of https://github.com/ghostfolio/ghostfolio
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
125 lines
4.9 KiB
125 lines
4.9 KiB
import asyncio
|
|
import httpx
|
|
from datetime import datetime
|
|
|
|
# Tickers shown for vague "what's hot / market overview" queries
|
|
MARKET_OVERVIEW_TICKERS = ["SPY", "QQQ", "AAPL", "MSFT", "NVDA", "AMZN", "GOOGL"]
|
|
|
|
|
|
async def market_overview() -> dict:
|
|
"""
|
|
Fetches a quick snapshot of major indices and top tech stocks.
|
|
Used for queries like 'what's hot today?', 'market overview', etc.
|
|
"""
|
|
tool_result_id = f"market_overview_{int(datetime.utcnow().timestamp())}"
|
|
results = []
|
|
|
|
async def _fetch(sym: str):
|
|
try:
|
|
async with httpx.AsyncClient(timeout=8.0) as client:
|
|
resp = await client.get(
|
|
f"https://query1.finance.yahoo.com/v8/finance/chart/{sym}",
|
|
params={"interval": "1d", "range": "2d"},
|
|
headers={"User-Agent": "Mozilla/5.0"},
|
|
)
|
|
resp.raise_for_status()
|
|
data = resp.json()
|
|
meta = (data.get("chart", {}).get("result") or [{}])[0].get("meta", {})
|
|
price = meta.get("regularMarketPrice")
|
|
prev = meta.get("chartPreviousClose") or meta.get("previousClose")
|
|
chg = round((price - prev) / prev * 100, 2) if price and prev and prev != 0 else None
|
|
return {"symbol": sym, "price": price, "change_pct": chg, "currency": meta.get("currency", "USD")}
|
|
except Exception:
|
|
return {"symbol": sym, "price": None, "change_pct": None}
|
|
|
|
results = await asyncio.gather(*[_fetch(s) for s in MARKET_OVERVIEW_TICKERS])
|
|
successful = [r for r in results if r["price"] is not None]
|
|
|
|
if not successful:
|
|
return {
|
|
"tool_name": "market_data",
|
|
"success": False,
|
|
"tool_result_id": tool_result_id,
|
|
"error": "NO_DATA",
|
|
"message": "Could not fetch market overview data. Yahoo Finance may be temporarily unavailable.",
|
|
}
|
|
|
|
return {
|
|
"tool_name": "market_data",
|
|
"success": True,
|
|
"tool_result_id": tool_result_id,
|
|
"timestamp": datetime.utcnow().isoformat(),
|
|
"result": {"overview": successful},
|
|
}
|
|
|
|
|
|
async def market_data(symbol: str) -> dict:
|
|
"""
|
|
Fetches current market data from Yahoo Finance (free, no API key).
|
|
Uses the Yahoo Finance v8 chart API.
|
|
Timeout is 8.0s — Yahoo is slower than Ghostfolio.
|
|
"""
|
|
symbol = symbol.upper().strip()
|
|
tool_result_id = f"market_{symbol}_{int(datetime.utcnow().timestamp())}"
|
|
|
|
try:
|
|
async with httpx.AsyncClient(timeout=8.0) as client:
|
|
resp = await client.get(
|
|
f"https://query1.finance.yahoo.com/v8/finance/chart/{symbol}",
|
|
params={"interval": "1d", "range": "5d"},
|
|
headers={"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)"},
|
|
)
|
|
resp.raise_for_status()
|
|
data = resp.json()
|
|
|
|
chart_result = data.get("chart", {}).get("result", [])
|
|
if not chart_result:
|
|
return {
|
|
"tool_name": "market_data",
|
|
"success": False,
|
|
"tool_result_id": tool_result_id,
|
|
"error": "NO_DATA",
|
|
"message": f"No market data found for symbol '{symbol}'. Check the ticker is valid.",
|
|
}
|
|
|
|
meta = chart_result[0].get("meta", {})
|
|
current_price = meta.get("regularMarketPrice")
|
|
prev_close = meta.get("chartPreviousClose") or meta.get("previousClose")
|
|
|
|
change_pct = None
|
|
if current_price and prev_close and prev_close != 0:
|
|
change_pct = round((current_price - prev_close) / prev_close * 100, 2)
|
|
|
|
return {
|
|
"tool_name": "market_data",
|
|
"success": True,
|
|
"tool_result_id": tool_result_id,
|
|
"timestamp": datetime.utcnow().isoformat(),
|
|
"endpoint": f"https://query1.finance.yahoo.com/v8/finance/chart/{symbol}",
|
|
"result": {
|
|
"symbol": symbol,
|
|
"current_price": current_price,
|
|
"previous_close": prev_close,
|
|
"change_pct": change_pct,
|
|
"currency": meta.get("currency"),
|
|
"exchange": meta.get("exchangeName"),
|
|
"instrument_type": meta.get("instrumentType"),
|
|
},
|
|
}
|
|
|
|
except httpx.TimeoutException:
|
|
return {
|
|
"tool_name": "market_data",
|
|
"success": False,
|
|
"tool_result_id": tool_result_id,
|
|
"error": "TIMEOUT",
|
|
"message": f"Yahoo Finance timed out fetching {symbol}. Try again in a moment.",
|
|
}
|
|
except Exception as e:
|
|
return {
|
|
"tool_name": "market_data",
|
|
"success": False,
|
|
"tool_result_id": tool_result_id,
|
|
"error": "API_ERROR",
|
|
"message": f"Failed to fetch market data for {symbol}: {str(e)}",
|
|
}
|
|
|