Spaces:
Sleeping
Sleeping
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)
|