Spaces:
Running
Running
Update app/price_fetcher.py
Browse files- app/price_fetcher.py +59 -16
app/price_fetcher.py
CHANGED
@@ -1,27 +1,70 @@
|
|
1 |
"""
|
2 |
-
Background price cache
|
3 |
-
(If you have Gemini keys, swap endpoints & add auth)
|
4 |
"""
|
5 |
-
import httpx
|
|
|
6 |
|
|
|
7 |
COINGECKO_URL = (
|
8 |
"https://api.coingecko.com/api/v3/simple/price"
|
9 |
"?ids=bitcoin,ethereum,dogecoin&vs_currencies=usd"
|
10 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
|
12 |
-
CURRENT_PRICES = {"bitcoin": "ββ", "ethereum": "ββ", "dogecoin": "ββ"}
|
13 |
|
14 |
def fetch_prices() -> None:
|
|
|
|
|
|
|
|
|
15 |
global CURRENT_PRICES
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
"""
|
2 |
+
Background price cache with multi-API fallback and rate-limit handling
|
|
|
3 |
"""
|
4 |
+
import httpx
|
5 |
+
import logging
|
6 |
|
7 |
+
# Primary and secondary APIs for crypto prices
|
8 |
COINGECKO_URL = (
|
9 |
"https://api.coingecko.com/api/v3/simple/price"
|
10 |
"?ids=bitcoin,ethereum,dogecoin&vs_currencies=usd"
|
11 |
)
|
12 |
+
COINCAP_URL = (
|
13 |
+
"https://api.coincap.io/v2/assets?ids=bitcoin,ethereum,dogecoin"
|
14 |
+
)
|
15 |
+
|
16 |
+
# Shared price cache
|
17 |
+
CURRENT_PRICES = {"bitcoin": "--", "ethereum": "--", "dogecoin": "--"}
|
18 |
+
|
19 |
+
# How often to retry each API before falling back (seconds)
|
20 |
+
RETRY_DELAY = 5
|
21 |
|
|
|
22 |
|
23 |
def fetch_prices() -> None:
|
24 |
+
"""
|
25 |
+
Try CoinGecko first; on 429 or error, fall back to CoinCap.
|
26 |
+
Updates CURRENT_PRICES in-place.
|
27 |
+
"""
|
28 |
global CURRENT_PRICES
|
29 |
+
apis = [
|
30 |
+
("CoinGecko", COINGECKO_URL),
|
31 |
+
("CoinCap", COINCAP_URL)
|
32 |
+
]
|
33 |
+
|
34 |
+
for name, url in apis:
|
35 |
+
try:
|
36 |
+
resp = httpx.get(url, timeout=10)
|
37 |
+
# Handle CoinCap JSON structure separately
|
38 |
+
if name == "CoinGecko":
|
39 |
+
resp.raise_for_status()
|
40 |
+
data = resp.json()
|
41 |
+
prices = {
|
42 |
+
"bitcoin": data["bitcoin"]["usd"],
|
43 |
+
"ethereum": data["ethereum"]["usd"],
|
44 |
+
"dogecoin": data["dogecoin"]["usd"]
|
45 |
+
}
|
46 |
+
else:
|
47 |
+
resp.raise_for_status()
|
48 |
+
data = resp.json().get("data", [])
|
49 |
+
prices = {item["id"]: float(item["priceUsd"]) for item in data}
|
50 |
+
|
51 |
+
CURRENT_PRICES.update(prices)
|
52 |
+
logging.info("β
[%s] prices updated: %s", name, prices)
|
53 |
+
return
|
54 |
+
|
55 |
+
except httpx.HTTPStatusError as e:
|
56 |
+
status = e.response.status_code
|
57 |
+
if status == 429:
|
58 |
+
logging.warning("β οΈ [%s] rate limit (429). Retrying fallback.", name)
|
59 |
+
else:
|
60 |
+
logging.warning("β οΈ [%s] HTTP error %s: %s", name, status, e)
|
61 |
+
except Exception as e:
|
62 |
+
logging.warning("β οΈ [%s] fetch error: %s", name, e)
|
63 |
+
|
64 |
+
# If we reach here, wait a bit before trying next API
|
65 |
+
try:
|
66 |
+
import time; time.sleep(RETRY_DELAY)
|
67 |
+
except Exception:
|
68 |
+
pass
|
69 |
+
|
70 |
+
logging.error("β All price APIs failed. Keeping previous prices: %s", CURRENT_PRICES)
|