AndyKandy26 commited on
Commit
3d7d08e
Β·
verified Β·
1 Parent(s): 5cf14de

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -125
app.py DELETED
@@ -1,125 +0,0 @@
1
- """
2
- FinWise β€” FastAPI backend for HuggingFace Spaces
3
- -------------------------------------------------
4
- β€’ Serves all static HTML/CSS/JS files from the same directory
5
- β€’ /api/quotes?symbols=NVDA,AAPL,MSFT β†’ live quotes via yfinance
6
- β€’ /api/chart?symbol=NVDA&days=8 β†’ daily closes for sparkline
7
- β€’ Full CORS enabled so the browser JS can call these endpoints freely
8
- β€’ Runs on port 7860 (HuggingFace default)
9
- """
10
-
11
- import os
12
- import json
13
- from pathlib import Path
14
- from typing import List
15
-
16
- import yfinance as yf
17
- from fastapi import FastAPI, Query, HTTPException
18
- from fastapi.middleware.cors import CORSMiddleware
19
- from fastapi.staticfiles import StaticFiles
20
- from fastapi.responses import FileResponse, JSONResponse
21
- import uvicorn
22
-
23
- # ── App ───────────────────────────────────────────────────────────────────────
24
- app = FastAPI(title="FinWise API", docs_url="/api/docs")
25
-
26
- # Allow all origins (the page is served from the same Space, but be permissive)
27
- app.add_middleware(
28
- CORSMiddleware,
29
- allow_origins=["*"],
30
- allow_methods=["GET"],
31
- allow_headers=["*"],
32
- )
33
-
34
- # ── Helpers ──────────────────────────────────────────────────────────────────
35
- def safe_float(val, decimals: int = 2):
36
- """Return a rounded float or None if value is NaN / missing."""
37
- try:
38
- v = float(val)
39
- if v != v: # NaN check
40
- return None
41
- return round(v, decimals)
42
- except Exception:
43
- return None
44
-
45
-
46
- # ── Routes ───────────────────────────────────────────────────────────────────
47
-
48
- @app.get("/api/quotes")
49
- def get_quotes(symbols: str = Query(..., description="Comma-separated tickers, e.g. NVDA,AAPL")):
50
- """
51
- Returns a dict keyed by ticker symbol:
52
- { "NVDA": { "price": 875.24, "chg1d": 2.4, "vol": 45000000 }, ... }
53
- """
54
- tickers = [t.strip().upper() for t in symbols.split(",") if t.strip()]
55
- if not tickers:
56
- raise HTTPException(status_code=400, detail="No valid symbols provided")
57
- if len(tickers) > 30:
58
- raise HTTPException(status_code=400, detail="Max 30 symbols per request")
59
-
60
- try:
61
- # yfinance batch download β€” fast_info is the lightest call
62
- data = yf.Tickers(" ".join(tickers))
63
- result = {}
64
- for sym in tickers:
65
- try:
66
- t = data.tickers[sym]
67
- fi = t.fast_info # lightweight, no extra HTTP round-trip
68
- result[sym] = {
69
- "price": safe_float(fi.last_price),
70
- "chg1d": safe_float(fi.last_price / fi.previous_close * 100 - 100)
71
- if fi.previous_close else None,
72
- "vol": int(fi.three_month_average_volume or 0) or None,
73
- "prev": safe_float(fi.previous_close),
74
- "high": safe_float(fi.day_high),
75
- "low": safe_float(fi.day_low),
76
- }
77
- except Exception:
78
- result[sym] = {"price": None, "chg1d": None, "vol": None}
79
-
80
- return JSONResponse(content=result)
81
-
82
- except Exception as e:
83
- raise HTTPException(status_code=502, detail=f"yfinance error: {str(e)}")
84
-
85
-
86
- @app.get("/api/chart")
87
- def get_chart(symbol: str = Query(...), days: int = Query(default=8, ge=2, le=30)):
88
- """
89
- Returns daily closing prices for the last `days` trading days.
90
- Used for sparklines.
91
- { "symbol": "NVDA", "closes": [820.1, 835.4, ...] }
92
- """
93
- sym = symbol.strip().upper()
94
- try:
95
- hist = yf.Ticker(sym).history(period=f"{days}d", interval="1d")
96
- closes = [safe_float(v) for v in hist["Close"].tolist() if v == v]
97
- closes = [c for c in closes if c is not None]
98
- return JSONResponse(content={"symbol": sym, "closes": closes[-7:]})
99
- except Exception as e:
100
- raise HTTPException(status_code=502, detail=f"yfinance chart error: {str(e)}")
101
-
102
-
103
- @app.get("/api/health")
104
- def health():
105
- return {"status": "ok", "service": "FinWise API"}
106
-
107
-
108
- # ── Serve static files (HTML/CSS/JS) ─────────────────────────────────────────
109
- # All .html files in the same directory are served directly.
110
- # The root "/" returns index.html.
111
- BASE_DIR = Path(__file__).parent
112
-
113
- @app.get("/")
114
- def root():
115
- return FileResponse(BASE_DIR / "index.html")
116
-
117
- # Mount everything else as static β€” CSS, JS, other HTML pages
118
- # We do this AFTER the API routes so /api/* is not caught by StaticFiles
119
- app.mount("/", StaticFiles(directory=str(BASE_DIR), html=True), name="static")
120
-
121
-
122
- # ── Entry point ─────��─────────────────────────────────────────────────────────
123
- if __name__ == "__main__":
124
- port = int(os.environ.get("PORT", 7860))
125
- uvicorn.run("app:app", host="0.0.0.0", port=port, reload=False)