Spaces:
Running
Running
Commit Β·
32a996a
1
Parent(s): 624c105
fix: company info from chart API meta, multi-market sentiment, factor analysis
Browse files
backend/app/services/data_ingestion/yahoo.py
CHANGED
|
@@ -126,43 +126,62 @@ def _direct_fetch_history(ticker: str, period: str = "1y", interval: str = "1d",
|
|
| 126 |
|
| 127 |
|
| 128 |
def _direct_fetch_info(ticker: str) -> dict:
|
| 129 |
-
"""Fetch quote info
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
if not _USE_CURL or not _curl_session:
|
| 131 |
return _yfinance_fetch_info(ticker)
|
| 132 |
|
| 133 |
try:
|
| 134 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
if resp.status_code != 200:
|
| 136 |
-
logger.debug("Yahoo
|
| 137 |
return {}
|
| 138 |
|
| 139 |
data = resp.json()
|
| 140 |
-
|
| 141 |
-
if not
|
| 142 |
return {}
|
| 143 |
|
| 144 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 145 |
return {
|
| 146 |
-
"longName":
|
| 147 |
-
"shortName":
|
| 148 |
-
"currency":
|
| 149 |
-
"exchange":
|
| 150 |
-
"sector":
|
| 151 |
-
"industry":
|
| 152 |
-
"marketCap":
|
| 153 |
-
"trailingPE":
|
| 154 |
-
"forwardPE":
|
| 155 |
-
"priceToBook":
|
| 156 |
-
"dividendYield":
|
| 157 |
-
"trailingEps":
|
| 158 |
-
"fiftyTwoWeekHigh":
|
| 159 |
-
"fiftyTwoWeekLow":
|
| 160 |
-
"averageVolume":
|
| 161 |
-
"beta":
|
| 162 |
-
"country":
|
|
|
|
|
|
|
| 163 |
}
|
| 164 |
except Exception as e:
|
| 165 |
-
logger.debug("Direct
|
| 166 |
return {}
|
| 167 |
|
| 168 |
|
|
|
|
| 126 |
|
| 127 |
|
| 128 |
def _direct_fetch_info(ticker: str) -> dict:
|
| 129 |
+
"""Fetch quote info from Yahoo's chart API meta field via curl_cffi.
|
| 130 |
+
|
| 131 |
+
The v7/finance/quote endpoint is blocked on cloud IPs, but the v8/finance/chart
|
| 132 |
+
endpoint works and includes a 'meta' field with useful company data.
|
| 133 |
+
"""
|
| 134 |
if not _USE_CURL or not _curl_session:
|
| 135 |
return _yfinance_fetch_info(ticker)
|
| 136 |
|
| 137 |
try:
|
| 138 |
+
# Use the chart API which is confirmed working β extract from meta
|
| 139 |
+
url = _CHART_URL.format(ticker=ticker)
|
| 140 |
+
params = {
|
| 141 |
+
"period1": str(int(time.time()) - 86400 * 5),
|
| 142 |
+
"period2": str(int(time.time())),
|
| 143 |
+
"interval": "1d",
|
| 144 |
+
"includeAdjustedClose": "true",
|
| 145 |
+
}
|
| 146 |
+
resp = _curl_session.get(url, params=params, timeout=10)
|
| 147 |
if resp.status_code != 200:
|
| 148 |
+
logger.debug("Yahoo chart meta API returned %d for %s", resp.status_code, ticker)
|
| 149 |
return {}
|
| 150 |
|
| 151 |
data = resp.json()
|
| 152 |
+
result = data.get("chart", {}).get("result")
|
| 153 |
+
if not result:
|
| 154 |
return {}
|
| 155 |
|
| 156 |
+
meta = result[0].get("meta", {})
|
| 157 |
+
|
| 158 |
+
# Calculate basic metrics from price data if available
|
| 159 |
+
prev_close = meta.get("chartPreviousClose") or meta.get("previousClose")
|
| 160 |
+
reg_price = meta.get("regularMarketPrice")
|
| 161 |
+
|
| 162 |
return {
|
| 163 |
+
"longName": meta.get("longName"),
|
| 164 |
+
"shortName": meta.get("shortName") or meta.get("symbol"),
|
| 165 |
+
"currency": meta.get("currency", "USD"),
|
| 166 |
+
"exchange": meta.get("exchangeName") or meta.get("exchange"),
|
| 167 |
+
"sector": None, # Not available from chart meta
|
| 168 |
+
"industry": None,
|
| 169 |
+
"marketCap": None,
|
| 170 |
+
"trailingPE": None,
|
| 171 |
+
"forwardPE": None,
|
| 172 |
+
"priceToBook": None,
|
| 173 |
+
"dividendYield": None,
|
| 174 |
+
"trailingEps": None,
|
| 175 |
+
"fiftyTwoWeekHigh": meta.get("fiftyTwoWeekHigh"),
|
| 176 |
+
"fiftyTwoWeekLow": meta.get("fiftyTwoWeekLow"),
|
| 177 |
+
"averageVolume": None,
|
| 178 |
+
"beta": None,
|
| 179 |
+
"country": None,
|
| 180 |
+
"regularMarketPrice": reg_price,
|
| 181 |
+
"previousClose": prev_close,
|
| 182 |
}
|
| 183 |
except Exception as e:
|
| 184 |
+
logger.debug("Direct chart meta fetch failed for %s: %s", ticker, e)
|
| 185 |
return {}
|
| 186 |
|
| 187 |
|
backend/app/services/sentiment/engine.py
CHANGED
|
@@ -143,18 +143,24 @@ async def fetch_multi_sentiment(tickers: List[str]) -> List[Dict[str, Any]]:
|
|
| 143 |
|
| 144 |
# ββ Trending / Market Mood βββββββββββββββββββββββββββββββββββββββββββββββ
|
| 145 |
MARKET_TICKERS = [
|
|
|
|
| 146 |
"SPY", "QQQ", "AAPL", "MSFT", "GOOGL", "AMZN", "NVDA", "TSLA",
|
| 147 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 148 |
]
|
| 149 |
|
| 150 |
async def get_market_mood() -> Dict[str, Any]:
|
| 151 |
-
"""Get overall market sentiment from major tickers."""
|
| 152 |
cache_key = "sentiment:market_mood"
|
| 153 |
cached = await cache_get(cache_key)
|
| 154 |
if cached:
|
| 155 |
return cached
|
| 156 |
|
| 157 |
-
results = await fetch_multi_sentiment(MARKET_TICKERS[:
|
| 158 |
|
| 159 |
if not results:
|
| 160 |
return {"mood": "neutral", "score": 0, "tickers_analyzed": 0}
|
|
|
|
| 143 |
|
| 144 |
# ββ Trending / Market Mood βββββββββββββββββββββββββββββββββββββββββββββββ
|
| 145 |
MARKET_TICKERS = [
|
| 146 |
+
# US
|
| 147 |
"SPY", "QQQ", "AAPL", "MSFT", "GOOGL", "AMZN", "NVDA", "TSLA",
|
| 148 |
+
# India
|
| 149 |
+
"RELIANCE.NS", "TCS.NS", "HDFCBANK.NS", "INFY.NS",
|
| 150 |
+
# Europe
|
| 151 |
+
"ASML", "SAP", "SHEL", "AZN",
|
| 152 |
+
# Asia
|
| 153 |
+
"9988.HK", "7203.T",
|
| 154 |
]
|
| 155 |
|
| 156 |
async def get_market_mood() -> Dict[str, Any]:
|
| 157 |
+
"""Get overall market sentiment from major tickers across all markets."""
|
| 158 |
cache_key = "sentiment:market_mood"
|
| 159 |
cached = await cache_get(cache_key)
|
| 160 |
if cached:
|
| 161 |
return cached
|
| 162 |
|
| 163 |
+
results = await fetch_multi_sentiment(MARKET_TICKERS[:10])
|
| 164 |
|
| 165 |
if not results:
|
| 166 |
return {"mood": "neutral", "score": 0, "tickers_analyzed": 0}
|