File size: 9,967 Bytes
32f16c2 7aea037 32f16c2 7aea037 32f16c2 7aea037 32f16c2 7aea037 32f16c2 7aea037 32f16c2 7aea037 32f16c2 7aea037 32f16c2 7aea037 32f16c2 7aea037 32f16c2 7aea037 32f16c2 7aea037 32f16c2 7aea037 32f16c2 7aea037 32f16c2 7aea037 32f16c2 7aea037 3452823 7aea037 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 | """
API-ready versions of stock data functions
Streamlit bağımlılıkları olmayan versiyonlar
"""
import pandas as pd
from datetime import datetime, timedelta
import numpy as np
from typing import Any, Dict, Optional, Tuple
import json
import urllib.request
import urllib.error
def _get_yfinance():
try:
import yfinance as yf # type: ignore
return yf
except Exception as exc:
raise RuntimeError(
"yfinance is not available or is incompatible with this Python runtime"
) from exc
def _normalize_yahoo_symbol(symbol: str) -> str:
sym = str(symbol or "").strip().upper()
if not sym:
return ""
if sym.endswith(".IS"):
return sym
return f"{sym}.IS"
def _probe_yahoo_chart_status(
yahoo_symbol: str,
period: str,
interval: str,
timeout_seconds: int = 10,
) -> Dict[str, Any]:
url = (
f"https://query1.finance.yahoo.com/v8/finance/chart/{yahoo_symbol}"
f"?range={period}&interval={interval}"
)
req = urllib.request.Request(
url,
headers={
"User-Agent": "Mozilla/5.0",
"Accept": "application/json,text/plain;q=0.9,*/*;q=0.8",
},
method="GET",
)
try:
with urllib.request.urlopen(req, timeout=timeout_seconds) as resp:
status = int(getattr(resp, "status", 200))
ct = str(resp.headers.get("content-type") or "")
body = resp.read(512) or b""
body_preview = body.decode("utf-8", errors="replace")
parsed_ok = False
if "json" in ct.lower():
try:
json.loads(body_preview)
parsed_ok = True
except Exception:
parsed_ok = False
return {
"ok": status == 200,
"http_status": status,
"content_type": ct,
"parsed_json_preview": parsed_ok,
"body_preview": body_preview,
"url": url,
}
except urllib.error.HTTPError as e:
body = (e.read(512) or b"") if hasattr(e, "read") else b""
body_preview = body.decode("utf-8", errors="replace")
return {
"ok": False,
"http_status": int(getattr(e, "code", 0) or 0),
"content_type": str(getattr(e, "headers", {}).get("content-type") if getattr(e, "headers", None) else ""),
"body_preview": body_preview,
"url": url,
}
except Exception as e:
return {
"ok": False,
"http_status": None,
"error": f"{type(e).__name__}: {e}",
"url": url,
}
def _fetch_yahoo_chart_df(
yahoo_symbol: str,
period: str,
interval: str,
timeout_seconds: int = 20,
) -> Optional[pd.DataFrame]:
url = (
f"https://query1.finance.yahoo.com/v8/finance/chart/{yahoo_symbol}"
f"?range={period}&interval={interval}"
)
req = urllib.request.Request(
url,
headers={
"User-Agent": "Mozilla/5.0",
"Accept": "application/json,text/plain;q=0.9,*/*;q=0.8",
},
method="GET",
)
with urllib.request.urlopen(req, timeout=timeout_seconds) as resp:
raw = resp.read() or b""
payload = json.loads(raw.decode("utf-8", errors="strict"))
chart = (payload or {}).get("chart") or {}
results = chart.get("result") or []
if not results:
return None
r0 = results[0] or {}
timestamps = r0.get("timestamp") or []
indicators = r0.get("indicators") or {}
quotes = indicators.get("quote") or []
if not timestamps or not quotes:
return None
q0 = quotes[0] or {}
open_ = q0.get("open") or []
high = q0.get("high") or []
low = q0.get("low") or []
close = q0.get("close") or []
volume = q0.get("volume") or []
df = pd.DataFrame(
{
"Open": open_,
"High": high,
"Low": low,
"Close": close,
"Volume": volume,
}
)
df.index = pd.to_datetime(pd.Series(timestamps, dtype="int64"), unit="s", utc=True)
for col in ["Open", "High", "Low", "Close", "Volume"]:
df[col] = pd.to_numeric(df[col], errors="coerce")
df = df.dropna(subset=["Close"])
df["Volume"] = df["Volume"].fillna(0.0)
if df.empty:
return None
return df
def get_stock_data_with_status_for_api(
symbol: str,
period: str = "1y",
interval: str = "1d",
) -> Tuple[Optional[pd.DataFrame], Dict[str, Any]]:
yahoo_symbol = _normalize_yahoo_symbol(symbol)
if not yahoo_symbol:
return None, {"ok": False, "error_type": "invalid_symbol"}
status: Dict[str, Any] = {
"ok": False,
"symbol": str(symbol or ""),
"yahoo_symbol": yahoo_symbol,
"period": period,
"interval": interval,
"source": "yahoo_finance",
}
try:
yf = _get_yfinance()
stock = yf.Ticker(yahoo_symbol)
data = stock.history(period=period, interval=interval)
if data is not None and len(data) > 0:
try:
fast_info = stock.fast_info
if hasattr(fast_info, "last_price") and fast_info.last_price:
last_idx = data.index[-1]
data.loc[last_idx, "Close"] = fast_info.last_price
except Exception:
try:
info = stock.info
last_idx = data.index[-1]
for key in ["regularMarketPrice", "currentPrice", "previousClose"]:
if key in info and info[key]:
data.loc[last_idx, "Close"] = info[key]
break
except Exception:
pass
status["ok"] = True
status["error_type"] = None
return data, status
probe = _probe_yahoo_chart_status(yahoo_symbol, period=period, interval=interval)
status["probe"] = {
k: probe.get(k)
for k in ["ok", "http_status", "content_type", "parsed_json_preview", "body_preview", "url", "error"]
if k in probe
}
http_status = probe.get("http_status")
if http_status == 429:
status["error_type"] = "rate_limited"
return None, status
try:
chart_df = _fetch_yahoo_chart_df(yahoo_symbol, period=period, interval=interval)
if chart_df is not None and not chart_df.empty:
status["ok"] = True
status["error_type"] = None
status["source"] = "yahoo_chart"
return chart_df, status
except Exception as e:
status["chart_fallback_error"] = f"{type(e).__name__}: {e}"
for backup_period in ["6mo", "3mo", "1mo"]:
try:
data2 = stock.history(period=backup_period, interval=interval)
if data2 is not None and not data2.empty:
status["ok"] = True
status["error_type"] = None
status["period"] = backup_period
return data2, status
except Exception:
continue
try:
chart_df2 = _fetch_yahoo_chart_df(yahoo_symbol, period=backup_period, interval=interval)
if chart_df2 is not None and not chart_df2.empty:
status["ok"] = True
status["error_type"] = None
status["source"] = "yahoo_chart"
status["period"] = backup_period
return chart_df2, status
except Exception:
continue
status["error_type"] = status.get("error_type") or "no_data"
return None, status
except Exception as e:
status["error_type"] = "exception"
status["error"] = f"{type(e).__name__}: {e}"
return None, status
def get_stock_data_for_api(symbol, period="1y", interval="1d"):
"""
API için cache'siz veri çekme fonksiyonu
"""
try:
data, _status = get_stock_data_with_status_for_api(symbol, period=period, interval=interval)
return data
except Exception as e:
print(f"API veri hatası ({symbol}): {e}")
return None
def get_market_summary_for_api():
"""
API için BIST 100 özet bilgisi
"""
try:
yf = _get_yfinance()
xu100 = yf.Ticker("XU100.IS")
data = xu100.history(period="5d")
if data.empty:
return None
latest = data.iloc[-1]
previous = data.iloc[-2] if len(data) > 1 else latest
change = latest['Close'] - previous['Close']
change_pct = (change / previous['Close']) * 100
# Safely format date
try:
date_str = data.index[-1].strftime('%Y-%m-%d') # type: ignore
except (AttributeError, TypeError):
date_str = str(data.index[-1])[:10]
return {
'index': 'XU100',
'value': float(latest['Close']),
'change': float(change),
'change_percent': float(change_pct),
'high': float(data['High'].max()),
'low': float(data['Low'].min()),
'volume': int(latest['Volume']),
'date': date_str
}
except Exception as e:
print(f"Market summary hatası: {e}")
return None
def get_popular_stocks():
"""
Popüler/likit hisseleri döndürür.
NOTE: Hardcoded/toy list is intentionally avoided.
We derive a bounded universe from official BIST index constituents.
"""
try:
from data.index_constituents import get_index_constituents
res = get_index_constituents("bist30")
return res.symbols or []
except Exception:
return []
|