Spaces:
Running
Running
File size: 4,820 Bytes
97873ec 76f2683 7731384 97873ec fd31489 476cee0 2ac36a0 fd31489 19f368e fd31489 7731384 76f2683 c95cace 76f2683 fd31489 7731384 76f2683 431e338 19f368e fd31489 76f2683 19f368e 476cee0 76f2683 7731384 476cee0 c95cace 19f368e 7731384 76f2683 7731384 76f2683 7731384 76f2683 19f368e 431e338 76f2683 431e338 76f2683 431e338 c95cace 431e338 76f2683 431e338 76f2683 19f368e 431e338 76f2683 431e338 76f2683 c95cace 431e338 76f2683 431e338 |
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 |
"""
A professional-grade, multi-asset, multi-oracle price engine.
This engine dynamically fetches prices for a configured list of assets
from decentralized oracles to detect market dislocations.
v11.1: Corrected Pyth Network API endpoint.
"""
import asyncio
import logging
from typing import Dict, Optional
import httpx
logger = logging.getLogger(__name__)
# --- CONFIGURATION ---
ASSET_CONFIG = {
"BTC": {
"pyth_id": "e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415B43",
"coingecko_id": "bitcoin",
},
"ETH": {
"pyth_id": "ff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace",
"coingecko_id": "ethereum",
},
"SOL": {
"pyth_id": "ef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d",
"coingecko_id": "solana",
},
"XRP": {
"pyth_id": "02a01e69981d314fd8a723be08253181e53b4945b4bf376d15a51980a37330c3",
"coingecko_id": "ripple",
},
"DOGE": {
"pyth_id": "042f02faf4229babc62635593855b6a383d6a4a2a1b9b9a7c385a4a50b86a345",
"coingecko_id": "dogecoin",
},
"ADA": {
"pyth_id": "34f544985c7943c093b5934963505a767f4749445244a852654c6017b28091ea",
"coingecko_id": "cardano",
},
"AVAX": {
"pyth_id": "0x141f2a3c34c8035443a01d64380b52207991b16c14c5145f617eb578a994753c",
"coingecko_id": "avalanche-2",
},
"LINK": {
"pyth_id": "0x63f4f4755a5a67c64c781d45763b33a72666a15e6b91c0fbdf3b2f205d5a6b01",
"coingecko_id": "chainlink",
},
"DOT": {
"pyth_id": "0x00a896677493a74421b33362a7447785b13612f0e340d418641a33716a5067a3",
"coingecko_id": "polkadot",
},
"MATIC": {
"pyth_id": "0x737ac3c13709b45da8128ff9e1058a984f86a048035656111b8a365e4921648a",
"coingecko_id": "matic-network",
},
}
class PriceFetcher:
# ====================================================================
# THE CRITICAL FIX IS HERE
# ====================================================================
# Using the new, correct endpoint for Pyth Network price feeds.
PYTH_URL = "https://hermes.pyth.network/v2/price_feeds"
# ====================================================================
AGGREGATOR_URL = "https://api.coingecko.com/api/v3/simple/price"
def __init__(self, client: httpx.AsyncClient):
self.client = client
self._prices: Dict[str, Dict[str, Optional[float]]] = {}
self._lock = asyncio.Lock()
async def _fetch_pyth_prices(self) -> Dict[str, float]:
pyth_ids = [v['pyth_id'] for v in ASSET_CONFIG.values()]
# The new endpoint requires the '0x' prefix for IDs
params = [("ids[]", f"0x{pid}") for pid in pyth_ids]
try:
resp = await self.client.get(self.PYTH_URL, params=params, timeout=10)
resp.raise_for_status()
# The new endpoint returns a list directly
price_data_list = resp.json()
# Map the returned prices back to our asset symbols
id_to_symbol = {f"0x{v['pyth_id']}": k for k, v in ASSET_CONFIG.items()}
return {
id_to_symbol[item['id']]: int(item['price']['price']) / (10 ** abs(int(item['price']['expo'])))
for item in price_data_list if item['id'] in id_to_symbol
}
except Exception as e:
logger.error(f"β Oracle Error (Pyth): {e}")
return {}
async def _fetch_aggregator_prices(self) -> Dict[str, float]:
coingecko_ids = ",".join([v['coingecko_id'] for v in ASSET_CONFIG.values()])
params = {"ids": coingecko_ids, "vs_currencies": "usd"}
try:
resp = await self.client.get(self.AGGREGATOR_URL, params=params, timeout=10)
resp.raise_for_status()
data = resp.json()
id_to_symbol = {v['coingecko_id']: k for k, v in ASSET_CONFIG.items()}
return {id_to_symbol[cg_id]: prices['usd'] for cg_id, prices in data.items()}
except Exception as e:
logger.error(f"β Oracle Error (Aggregator): {e}")
return {}
async def update_prices_async(self):
pyth_task = self._fetch_pyth_prices()
agg_task = self._fetch_aggregator_prices()
pyth_prices, agg_prices = await asyncio.gather(pyth_task, agg_task)
async with self._lock:
for symbol in ASSET_CONFIG.keys():
self._prices[symbol] = {
"pyth": pyth_prices.get(symbol),
"chainlink_agg": agg_prices.get(symbol)
}
logger.info(f"β
Multi-Asset Prices Updated")
def get_all_prices(self) -> Dict[str, Dict[str, Optional[float]]]:
return self._prices.copy() |