Really-amin's picture
Upload 577 files
b190b45 verified
/**
* Real Data Fetcher
* Fetches real cryptocurrency data from multiple providers with intelligent fallback
* Uses crypto_resources_unified with 200+ endpoints
*/
import { API_REGISTRY, getTotalEndpointsCount } from './api-registry.js';
export class RealDataFetcher {
constructor() {
this.failedProviders = new Map();
this.providerStats = new Map();
this.cache = new Map();
}
/**
* Fetch market data with provider fallback
*/
async fetchMarketData(limit = 50) {
const providers = [
{ name: 'CoinGecko', fetcher: () => this.fetchFromCoinGecko(limit) },
{ name: 'Binance', fetcher: () => this.fetchFromBinance(limit) },
{ name: 'CoinMarketCap', fetcher: () => this.fetchFromCoinMarketCap(limit) }
];
return this.tryProviders(providers, 'market_data');
}
/**
* Fetch trending coins
*/
async fetchTrendingCoins() {
const providers = [
{ name: 'CoinGecko Trending', fetcher: () => this.fetchCoinGeckoTrending() },
{ name: 'CoinCap Top', fetcher: () => this.fetchCoinCapTop() }
];
return this.tryProviders(providers, 'trending');
}
/**
* Fetch sentiment data
*/
async fetchSentimentData(timeframe = '1D') {
const providers = [
{ name: 'Fear & Greed', fetcher: () => this.fetchFearGreedIndex() },
{ name: 'LunarCrush', fetcher: () => this.fetchLunarCrushSentiment() }
];
return this.tryProviders(providers, 'sentiment');
}
/**
* Fetch on-chain analytics
*/
async fetchOnChainAnalytics() {
const providers = [
{ name: 'Glassnode', fetcher: () => this.fetchGlassnodeData() },
{ name: 'Covalent', fetcher: () => this.fetchCovalentData() }
];
return this.tryProviders(providers, 'onchain');
}
/**
* Fetch latest news
*/
async fetchLatestNews(query = 'cryptocurrency') {
const providers = [
{ name: 'NewsAPI', fetcher: () => this.fetchNewsAPI(query) },
{ name: 'CryptoPanic', fetcher: () => this.fetchCryptoPanic() }
];
return this.tryProviders(providers, 'news');
}
/**
* Try multiple providers with fallback
*/
async tryProviders(providers, category) {
for (const provider of providers) {
try {
console.log(`[RealDataFetcher] Trying ${provider.name}...`);
const data = await provider.fetcher();
if (data) {
console.log(`[RealDataFetcher] ✅ ${provider.name} succeeded`);
this.recordProviderSuccess(provider.name);
return data;
}
} catch (error) {
console.warn(`[RealDataFetcher] ❌ ${provider.name} failed:`, error.message);
this.recordProviderFailure(provider.name);
}
}
console.error('[RealDataFetcher] All providers failed for', category);
return null;
}
/**
* ========================================================================
* COINGECKO ENDPOINTS
* ========================================================================
*/
async fetchFromCoinGecko(limit = 50) {
try {
const url = `https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=${Math.min(limit, 250)}&sparkline=true&price_change_percentage=7d`;
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
return {
coins: data.map(coin => ({
rank: coin.market_cap_rank,
name: coin.name,
symbol: coin.symbol.toUpperCase(),
price: coin.current_price,
volume_24h: coin.total_volume,
market_cap: coin.market_cap,
change_24h: coin.price_change_percentage_24h,
change_7d: coin.price_change_percentage_7d_in_currency,
image: coin.image
})),
timestamp: new Date().toISOString(),
source: 'coingecko'
};
} catch (error) {
console.error('[CoinGecko] Error:', error);
throw error;
}
}
async fetchCoinGeckoTrending() {
try {
const url = 'https://api.coingecko.com/api/v3/search/trending';
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
return {
coins: data.coins.slice(0, 10).map((item, i) => ({
rank: i + 1,
name: item.item.name,
symbol: item.item.symbol.toUpperCase(),
price: item.item.data.price,
market_cap: item.item.data.market_cap,
change_24h: item.item.data.price_change_percentage_24h,
image: item.item.large
})),
source: 'coingecko_trending'
};
} catch (error) {
console.error('[CoinGecko Trending] Error:', error);
throw error;
}
}
async fetchGlobalMarketData() {
try {
const url = 'https://api.coingecko.com/api/v3/global';
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
return {
total_market_cap: data.data.total_market_cap.usd,
total_volume: data.data.total_24h_vol.usd,
btc_dominance: data.data.btc_dominance,
active_cryptocurrencies: data.data.active_cryptocurrencies
};
} catch (error) {
console.error('[CoinGecko Global] Error:', error);
throw error;
}
}
/**
* ========================================================================
* BINANCE ENDPOINTS
* ========================================================================
*/
async fetchFromBinance(limit = 50) {
try {
const url = 'https://api.binance.com/api/v3/ticker/24hr';
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
// Filter to top trading pairs
return {
coins: data.slice(0, limit).map((ticker, i) => ({
rank: i + 1,
symbol: ticker.symbol.replace('USDT', ''),
price: parseFloat(ticker.lastPrice),
volume_24h: parseFloat(ticker.volume),
change_24h: parseFloat(ticker.priceChangePercent)
})),
source: 'binance'
};
} catch (error) {
console.error('[Binance] Error:', error);
throw error;
}
}
/**
* ========================================================================
* COINMARKETCAP ENDPOINTS
* ========================================================================
*/
async fetchFromCoinMarketCap(limit = 50) {
try {
// Note: This requires a CMC API key
const key = API_REGISTRY.market.coinmarketcap.key;
if (!key) throw new Error('CoinMarketCap key not configured');
const url = `https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?limit=${limit}&convert=USD`;
const response = await fetch(url, {
headers: {
'X-CMC_PRO_API_KEY': key
}
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
return {
coins: data.data.map((coin, i) => ({
rank: coin.cmc_rank,
name: coin.name,
symbol: coin.symbol,
price: coin.quote.USD.price,
volume_24h: coin.quote.USD.volume_24h,
market_cap: coin.quote.USD.market_cap,
change_24h: coin.quote.USD.percent_change_24h
})),
source: 'coinmarketcap'
};
} catch (error) {
console.error('[CoinMarketCap] Error:', error);
throw error;
}
}
/**
* ========================================================================
* COINCAP ENDPOINTS
* ========================================================================
*/
async fetchCoinCapTop() {
try {
const url = 'https://api.coincap.io/v2/assets?limit=50';
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
return {
coins: data.data.map((coin, i) => ({
rank: parseInt(coin.rank),
name: coin.name,
symbol: coin.symbol,
price: parseFloat(coin.priceUsd),
volume_24h: parseFloat(coin.volumeUsd24Hr),
market_cap: parseFloat(coin.marketCapUsd),
change_24h: parseFloat(coin.changePercent24Hr)
})),
source: 'coincap'
};
} catch (error) {
console.error('[CoinCap] Error:', error);
throw error;
}
}
/**
* ========================================================================
* SENTIMENT ENDPOINTS
* ========================================================================
*/
async fetchFearGreedIndex() {
try {
const url = 'https://api.alternative.me/fng/?limit=30';
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
return {
current: data.data[0],
history: data.data,
source: 'fear_greed'
};
} catch (error) {
console.error('[Fear & Greed] Error:', error);
throw error;
}
}
async fetchLunarCrushSentiment() {
try {
// This would need a real LunarCrush API key
throw new Error('LunarCrush requires API key');
} catch (error) {
console.error('[LunarCrush] Error:', error);
throw error;
}
}
/**
* ========================================================================
* ON-CHAIN ANALYTICS ENDPOINTS
* ========================================================================
*/
async fetchGlassnodeData() {
try {
// Glassnode requires API key
throw new Error('Glassnode requires API key');
} catch (error) {
console.error('[Glassnode] Error:', error);
throw error;
}
}
async fetchCovalentData() {
try {
// Covalent requires API key
throw new Error('Covalent requires API key');
} catch (error) {
console.error('[Covalent] Error:', error);
throw error;
}
}
/**
* ========================================================================
* NEWS ENDPOINTS
* ========================================================================
*/
async fetchNewsAPI(query = 'cryptocurrency') {
try {
const key = 'pub_346789abc123def456789ghi012345jkl';
const url = `https://newsapi.org/v2/everything?q=${query}&sortBy=publishedAt&language=en&pageSize=50&apiKey=${key}`;
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
return {
articles: data.articles.slice(0, 50).map(article => ({
title: article.title,
description: article.description,
url: article.url,
source: article.source.name,
published_at: article.publishedAt,
image: article.urlToImage
})),
source: 'newsapi'
};
} catch (error) {
console.error('[NewsAPI] Error:', error);
throw error;
}
}
async fetchCryptoPanic() {
try {
const url = 'https://cryptopanic.com/api/v1/posts/?auth_token=optional&limit=50';
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
return {
articles: data.results.slice(0, 50).map(article => ({
title: article.title,
url: article.link,
source: article.source.title,
kind: article.kind,
published_at: article.published_at
})),
source: 'cryptopanic'
};
} catch (error) {
console.error('[CryptoPanic] Error:', error);
throw error;
}
}
/**
* ========================================================================
* PROVIDER STATISTICS
* ========================================================================
*/
recordProviderSuccess(providerName) {
const stats = this.providerStats.get(providerName) || { success: 0, failures: 0 };
stats.success++;
this.providerStats.set(providerName, stats);
// Reset failure count
this.failedProviders.delete(providerName);
}
recordProviderFailure(providerName) {
const stats = this.providerStats.get(providerName) || { success: 0, failures: 0 };
stats.failures++;
this.providerStats.set(providerName, stats);
// Mark as failed if too many failures
const failures = (this.failedProviders.get(providerName) || 0) + 1;
this.failedProviders.set(providerName, failures);
}
getProviderStats() {
return Object.fromEntries(this.providerStats);
}
getTotalEndpoints() {
return getTotalEndpointsCount();
}
}
export const realDataFetcher = new RealDataFetcher();
export default realDataFetcher;