File size: 4,624 Bytes
97873ec
76f2683
 
 
97873ec
fd31489
476cee0
2ac36a0
fd31489
 
19f368e
fd31489
c95cace
 
 
 
 
76f2683
 
 
 
 
 
 
 
 
 
 
 
 
c95cace
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76f2683
c95cace
76f2683
fd31489
76f2683
 
431e338
19f368e
fd31489
76f2683
19f368e
476cee0
76f2683
 
 
476cee0
c95cace
19f368e
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
"""
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.
"""
import asyncio
import logging
from typing import Dict, Optional
import httpx

logger = logging.getLogger(__name__)

# ====================================================================
#              THE "MONEY-SPINNING" UPGRADE IS HERE
# ====================================================================
# This is our master watchlist. The engine will hunt for opportunities
# across all 10 of these high-volume assets simultaneously.
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:
    PYTH_URL = "https://hermes.pyth.network/v2/updates/price/latest"
    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()]
        params = [("ids[]", pid) for pid in pyth_ids]
        try:
            resp = await self.client.get(self.PYTH_URL, params=params, timeout=10)
            resp.raise_for_status()
            parsed_data = resp.json().get('parsed', [])
            
            id_to_symbol = {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 parsed_data 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()