Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Access CoinGecko crypto market data: spot prices, OHLC, trending coins, exchange listings, NFTs, and global stats.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
tools/exchanges.py
1#!/usr/bin/env python32"""3CoinGecko Exchanges Tools45Tools for fetching spot exchange data, tickers, and volume history.6"""78import os9from dotenv import load_dotenv10import json11import argparse12from typing import Dict, Any, Optional1314from core.http_client import proxied_get1516# Load environment variables17load_dotenv()181920def get_api_key() -> str:21"""Get CoinGecko API key from environment."""22api_key = os.getenv("COINGECKO_API_KEY")23if not api_key:24raise ValueError("COINGECKO_API_KEY environment variable is required")25return api_key262728def get_exchanges(29per_page: int = 100,30page: int = 131) -> Dict[str, Any]:32"""33Get all spot exchanges with volumes and trust scores.3435Args:36per_page: Results per page (max 250)37page: Page number3839Returns:40Dictionary with list of exchanges41"""42api_key = get_api_key()4344url = "https://pro-api.coingecko.com/api/v3/exchanges"45headers = {"x-cg-pro-api-key": api_key}46params = {47"per_page": min(per_page, 250),48"page": page49}5051response = proxied_get(url, headers=headers, params=params, timeout=30)52response.raise_for_status()53data = response.json()5455exchanges = []56for exchange in data:57exchanges.append({58"id": exchange.get("id", ""),59"name": exchange.get("name", ""),60"year_established": exchange.get("year_established"),61"country": exchange.get("country"),62"description": exchange.get("description"),63"url": exchange.get("url"),64"image": exchange.get("image"),65"has_trading_incentive": exchange.get("has_trading_incentive"),66"trust_score": exchange.get("trust_score"),67"trust_score_rank": exchange.get("trust_score_rank"),68"trade_volume_24h_btc": exchange.get("trade_volume_24h_btc"),69"trade_volume_24h_btc_normalized": exchange.get("trade_volume_24h_btc_normalized")70})7172return {73"exchanges": exchanges,74"count": len(exchanges),75"page": page,76"per_page": per_page77}787980def get_exchange(exchange_id: str) -> Dict[str, Any]:81"""82Get detailed exchange data including tickers and BTC volume.8384Args:85exchange_id: CoinGecko exchange id (e.g., binance, coinbase-exchange)8687Returns:88Dictionary with detailed exchange data89"""90api_key = get_api_key()9192url = f"https://pro-api.coingecko.com/api/v3/exchanges/{exchange_id}"93headers = {"x-cg-pro-api-key": api_key}9495response = proxied_get(url, headers=headers, timeout=30)96response.raise_for_status()97data = response.json()9899result = {100"id": data.get("id", ""),101"name": data.get("name", ""),102"year_established": data.get("year_established"),103"country": data.get("country"),104"description": data.get("description"),105"url": data.get("url"),106"image": data.get("image"),107"facebook_url": data.get("facebook_url"),108"reddit_url": data.get("reddit_url"),109"telegram_url": data.get("telegram_url"),110"slack_url": data.get("slack_url"),111"other_url_1": data.get("other_url_1"),112"other_url_2": data.get("other_url_2"),113"twitter_handle": data.get("twitter_handle"),114"has_trading_incentive": data.get("has_trading_incentive"),115"centralized": data.get("centralized"),116"public_notice": data.get("public_notice"),117"alert_notice": data.get("alert_notice"),118"trust_score": data.get("trust_score"),119"trust_score_rank": data.get("trust_score_rank"),120"trade_volume_24h_btc": data.get("trade_volume_24h_btc"),121"trade_volume_24h_btc_normalized": data.get("trade_volume_24h_btc_normalized"),122"tickers_count": len(data.get("tickers", []))123}124125# Include sample tickers (first 10)126tickers = data.get("tickers", [])[:10]127result["sample_tickers"] = [128{129"base": t.get("base", ""),130"target": t.get("target", ""),131"last": t.get("last"),132"volume": t.get("volume"),133"trust_score": t.get("trust_score"),134"bid_ask_spread_percentage": t.get("bid_ask_spread_percentage")135}136for t in tickers137]138139return result140141142def get_exchange_tickers(143exchange_id: str,144coin_ids: Optional[str] = None,145include_exchange_logo: bool = False,146page: int = 1,147order: str = "volume_desc",148depth: bool = False149) -> Dict[str, Any]:150"""151Get all trading pairs on an exchange.152153Args:154exchange_id: CoinGecko exchange id155coin_ids: Comma-separated coin ids to filter156include_exchange_logo: Include exchange logo157page: Page number158order: Sort order (trust_score_desc, trust_score_asc, volume_desc, volume_asc)159depth: Include order book depth160161Returns:162Dictionary with exchange tickers163"""164api_key = get_api_key()165166url = f"https://pro-api.coingecko.com/api/v3/exchanges/{exchange_id}/tickers"167headers = {"x-cg-pro-api-key": api_key}168params = {169"include_exchange_logo": str(include_exchange_logo).lower(),170"page": page,171"order": order,172"depth": str(depth).lower()173}174if coin_ids:175params["coin_ids"] = coin_ids176177response = proxied_get(url, headers=headers, params=params, timeout=30)178response.raise_for_status()179data = response.json()180181tickers = []182for ticker in data.get("tickers", []):183ticker_data = {184"base": ticker.get("base", ""),185"target": ticker.get("target", ""),186"coin_id": ticker.get("coin_id"),187"target_coin_id": ticker.get("target_coin_id"),188"last": ticker.get("last"),189"volume": ticker.get("volume"),190"converted_last": ticker.get("converted_last", {}),191"converted_volume": ticker.get("converted_volume", {}),192"trust_score": ticker.get("trust_score"),193"bid_ask_spread_percentage": ticker.get("bid_ask_spread_percentage"),194"timestamp": ticker.get("timestamp"),195"last_traded_at": ticker.get("last_traded_at"),196"last_fetch_at": ticker.get("last_fetch_at"),197"is_anomaly": ticker.get("is_anomaly"),198"is_stale": ticker.get("is_stale"),199"trade_url": ticker.get("trade_url")200}201if depth:202ticker_data["cost_to_move_up_usd"] = ticker.get("cost_to_move_up_usd")203ticker_data["cost_to_move_down_usd"] = ticker.get("cost_to_move_down_usd")204tickers.append(ticker_data)205206return {207"name": data.get("name", ""),208"tickers": tickers,209"count": len(tickers),210"page": page211}212213214def get_exchange_volume_chart(215exchange_id: str,216days: int = 30217) -> Dict[str, Any]:218"""219Get historical volume chart for an exchange.220221Args:222exchange_id: CoinGecko exchange id223days: Number of days (1, 7, 14, 30, 90, 180, 365)224225Returns:226Dictionary with volume history data227"""228api_key = get_api_key()229230url = f"https://pro-api.coingecko.com/api/v3/exchanges/{exchange_id}/volume_chart"231headers = {"x-cg-pro-api-key": api_key}232params = {"days": days}233234response = proxied_get(url, headers=headers, params=params, timeout=30)235response.raise_for_status()236data = response.json()237238# Data is array of [timestamp, volume_btc]239volume_data = []240for item in data:241if isinstance(item, list) and len(item) >= 2:242volume_data.append({243"timestamp": item[0],244"volume_btc": item[1]245})246247return {248"exchange_id": exchange_id,249"days": days,250"volume_history": volume_data,251"count": len(volume_data)252}253254255def main():256"""CLI interface for exchanges tools."""257parser = argparse.ArgumentParser(258description="CoinGecko Exchanges Tools",259formatter_class=argparse.RawDescriptionHelpFormatter260)261262subparsers = parser.add_subparsers(dest="command")263264# List command265list_parser = subparsers.add_parser("list", help="Get all exchanges")266list_parser.add_argument("--per-page", type=int, default=100)267list_parser.add_argument("--page", type=int, default=1)268269# Exchange command270exch_parser = subparsers.add_parser("get", help="Get exchange details")271exch_parser.add_argument("exchange_id", help="Exchange ID")272273# Tickers command274tickers_parser = subparsers.add_parser("tickers", help="Get exchange tickers")275tickers_parser.add_argument("exchange_id", help="Exchange ID")276tickers_parser.add_argument("--coins", help="Filter by coin ids")277tickers_parser.add_argument("--page", type=int, default=1)278tickers_parser.add_argument("--order", default="volume_desc")279280# Volume chart command281chart_parser = subparsers.add_parser("volume-chart", help="Get exchange volume history")282chart_parser.add_argument("exchange_id", help="Exchange ID")283chart_parser.add_argument("--days", type=int, default=30)284285args = parser.parse_args()286287try:288if args.command == "list":289result = get_exchanges(args.per_page, args.page)290elif args.command == "get":291result = get_exchange(args.exchange_id)292elif args.command == "tickers":293result = get_exchange_tickers(294exchange_id=args.exchange_id,295coin_ids=args.coins,296page=args.page,297order=args.order298)299elif args.command == "volume-chart":300result = get_exchange_volume_chart(args.exchange_id, args.days)301else:302parser.print_help()303return 0304305print(json.dumps(result, indent=2, ensure_ascii=False))306return 0307308except Exception as e:309print(json.dumps({"error": str(e)}, indent=2))310return 1311312313if __name__ == "__main__":314exit(main())315