| """CoinCap provider implementation""" |
| from __future__ import annotations |
| from typing import List |
| from datetime import datetime |
| import sys |
| import os |
| sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) |
|
|
| from core.base_provider import BaseProvider |
| from core.models import OHLCV, Price |
|
|
|
|
| class CoinCapProvider(BaseProvider): |
| """CoinCap public API provider""" |
|
|
| |
| INTERVAL_MAP = { |
| "1m": "m1", |
| "5m": "m5", |
| "15m": "m15", |
| "1h": "h1", |
| "4h": "h4", |
| "1d": "d1", |
| "1w": "w1", |
| } |
|
|
| def __init__(self): |
| super().__init__( |
| name="coincap", |
| base_url="https://api.coincap.io/v2", |
| timeout=10 |
| ) |
|
|
| def _normalize_symbol(self, symbol: str) -> str: |
| """Normalize symbol to CoinCap format (lowercase)""" |
| symbol = symbol.upper().replace("/", "").replace("USDT", "").replace("-", "") |
| return symbol.lower() |
|
|
| async def fetch_ohlcv(self, symbol: str, interval: str, limit: int) -> List[OHLCV]: |
| """Fetch OHLCV data from CoinCap history endpoint""" |
| coin_id = self._normalize_symbol(symbol) |
| coincap_interval = self.INTERVAL_MAP.get(interval, "h1") |
|
|
| url = f"{self.base_url}/assets/{coin_id}/history" |
| params = { |
| "interval": coincap_interval |
| } |
|
|
| data = await self._make_request(url, params) |
|
|
| if "data" not in data: |
| raise Exception("No data returned from CoinCap") |
|
|
| |
| |
| history = data["data"][:limit] |
|
|
| ohlcv_list = [] |
| for point in history: |
| price = float(point.get("priceUsd", 0)) |
| ohlcv_list.append(OHLCV( |
| timestamp=int(point.get("time", 0)), |
| open=price, |
| high=price, |
| low=price, |
| close=price, |
| volume=0.0 |
| )) |
|
|
| return ohlcv_list |
|
|
| async def fetch_prices(self, symbols: List[str]) -> List[Price]: |
| """Fetch current prices from CoinCap""" |
| url = f"{self.base_url}/assets" |
| params = { |
| "limit": 100 |
| } |
|
|
| data = await self._make_request(url, params) |
|
|
| if "data" not in data: |
| raise Exception("No data returned from CoinCap") |
|
|
| |
| requested = {self._normalize_symbol(s) for s in symbols} |
|
|
| prices = [] |
| for asset in data["data"]: |
| if asset["id"] in requested or asset["symbol"].lower() in requested: |
| prices.append(Price( |
| symbol=asset["symbol"], |
| name=asset["name"], |
| price=float(asset["priceUsd"]), |
| priceUsd=float(asset["priceUsd"]), |
| change24h=float(asset.get("changePercent24Hr", 0)), |
| volume24h=float(asset.get("volumeUsd24Hr", 0)), |
| marketCap=float(asset.get("marketCapUsd", 0)), |
| rank=int(asset.get("rank", 0)), |
| lastUpdate=datetime.now().isoformat() |
| )) |
|
|
| return prices |
|
|