Spaces:
Running
Running
Update app/price_fetcher.py
Browse files- app/price_fetcher.py +32 -23
app/price_fetcher.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
"""
|
2 |
-
A high-
|
3 |
-
|
4 |
"""
|
5 |
import asyncio
|
6 |
import logging
|
@@ -10,42 +10,51 @@ import httpx
|
|
10 |
logger = logging.getLogger(__name__)
|
11 |
|
12 |
class PriceFetcher:
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
|
|
18 |
|
19 |
def __init__(self, client: httpx.AsyncClient):
|
20 |
self.client = client
|
21 |
-
self._prices: Dict[str, Optional[float]] = {
|
22 |
self._lock = asyncio.Lock()
|
23 |
|
24 |
-
async def
|
25 |
try:
|
26 |
-
resp = await self.client.get(
|
27 |
resp.raise_for_status()
|
28 |
data = resp.json()
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
|
|
36 |
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
except Exception as e:
|
38 |
-
logger.error(f"β Failed to fetch from
|
39 |
return None
|
40 |
|
41 |
async def update_prices_async(self):
|
42 |
-
|
43 |
-
|
|
|
|
|
44 |
|
45 |
async with self._lock:
|
46 |
-
|
47 |
-
|
48 |
-
self._prices[name] = price
|
49 |
|
50 |
logger.info(f"β
Prices updated: {self._prices}")
|
51 |
|
|
|
1 |
"""
|
2 |
+
A high-availability price fetcher using a decentralized oracle (Pyth)
|
3 |
+
and a reliable data aggregator to bypass geoblocking.
|
4 |
"""
|
5 |
import asyncio
|
6 |
import logging
|
|
|
10 |
logger = logging.getLogger(__name__)
|
11 |
|
12 |
class PriceFetcher:
|
13 |
+
# Pyth provides real-time, on-chain prices. We will use their public API.
|
14 |
+
# We will fetch BTC/USD.
|
15 |
+
PYTH_URL = "https://hermes.pyth.network/v2/updates/price/latest?ids[]=e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415B43"
|
16 |
+
|
17 |
+
# A reliable aggregator that is not typically geoblocked.
|
18 |
+
AGGREGATOR_URL = "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd"
|
19 |
|
20 |
def __init__(self, client: httpx.AsyncClient):
|
21 |
self.client = client
|
22 |
+
self._prices: Dict[str, Optional[float]] = {"on_chain_pyth": None, "off_chain_agg": None}
|
23 |
self._lock = asyncio.Lock()
|
24 |
|
25 |
+
async def _fetch_pyth(self) -> Optional[float]:
|
26 |
try:
|
27 |
+
resp = await self.client.get(self.PYTH_URL, timeout=5)
|
28 |
resp.raise_for_status()
|
29 |
data = resp.json()
|
30 |
+
# The price is in the 'parsed' part of the response
|
31 |
+
price_data = data['parsed'][0]['price']
|
32 |
+
# Price is given with an exponent, e.g., price=119123, expo=-2 -> 1191.23
|
33 |
+
# But for BTC/USD, the expo is -8, and price is in sats. We need to adjust.
|
34 |
+
price = int(price_data['price']) / (10 ** abs(int(price_data['expo'])))
|
35 |
+
return price
|
36 |
+
except Exception as e:
|
37 |
+
logger.error(f"β Failed to fetch from Pyth: {e}")
|
38 |
return None
|
39 |
+
|
40 |
+
async def _fetch_aggregator(self) -> Optional[float]:
|
41 |
+
try:
|
42 |
+
resp = await self.client.get(self.AGGREGATOR_URL, timeout=5)
|
43 |
+
resp.raise_for_status()
|
44 |
+
return float(resp.json()['bitcoin']['usd'])
|
45 |
except Exception as e:
|
46 |
+
logger.error(f"β Failed to fetch from Aggregator: {e}")
|
47 |
return None
|
48 |
|
49 |
async def update_prices_async(self):
|
50 |
+
pyth_task = self._fetch_pyth()
|
51 |
+
agg_task = self._fetch_aggregator()
|
52 |
+
|
53 |
+
pyth_price, agg_price = await asyncio.gather(pyth_task, agg_task)
|
54 |
|
55 |
async with self._lock:
|
56 |
+
self._prices["on_chain_pyth"] = pyth_price
|
57 |
+
self._prices["off_chain_agg"] = agg_price
|
|
|
58 |
|
59 |
logger.info(f"β
Prices updated: {self._prices}")
|
60 |
|