+#!/usr/bin/env python3
+"""
+CoinGecko Integration Update Script
+This script handles updating and synchronizing data from CoinGecko API.
+"""
+
+import os
+import sys
+import json
+import time
+import logging
+import requests
+from datetime import datetime
+
+# Constants
+JSON_INDENT = 2
+DEFAULT_TOP_COINS_LIMIT = 250
+DEFAULT_PAGE = 1
+REQUEST_TIMEOUT = 30
+API_VERSION = "1.0.0"
+ERROR_EXIT_CODE = 1
+
+# Configure logging
+logging.basicConfig(
+ level=logging.INFO,
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
+ handlers=[logging.StreamHandler(sys.stdout)]
+)
+logger = logging.getLogger("coingecko-integration")
+
+# Configuration
+COINGECKO_API_BASE = "https://api.coingecko.com/api/v3"
+COINGECKO_API_KEY = os.environ.get("COINGECKO_API_KEY", "")
+if not COINGECKO_API_KEY:
+ logger.warning("COINGECKO_API_KEY not set - running in free tier mode with rate limits")
+DATA_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "data", "coingecko")
+
+
+def get_headers():
+ """
+ Returns HTTP headers for CoinGecko API requests, including an API key if set.
+ """
+ headers = {
+ "Accept": "application/json"
+ }
+ if COINGECKO_API_KEY:
+ headers["X-CG-Pro-API-Key"] = COINGECKO_API_KEY
+ return headers
+
+
+def respect_rate_limits(response):
+ """Check response headers and respect rate limits"""
+ remaining = response.headers.get('X-RateLimit-Remaining')
+ if remaining and int(remaining) < 5:
+ reset_time = response.headers.get('X-RateLimit-Reset')
+ if reset_time:
+ sleep_time = max(int(reset_time) - time.time(), 0) + 1
+ logger.info(f"Rate limit approaching, sleeping for {sleep_time} seconds")
+ time.sleep(sleep_time)
+ else:
+ logger.info("Rate limit approaching, sleeping for 10 seconds")
+ time.sleep(10)
+ else:
+ # Add a small delay between requests regardless
+ time.sleep(0.5)
+
+def fetch_coins_list():
+ """
+ Fetches the complete list of coins from the CoinGecko API and saves it to a local JSON file.
+
+ Returns:
+ The list of coins as parsed from the API response, or None if the request fails.
+ """
+ url = f"{COINGECKO_API_BASE}/coins/list"
+ logger.info(f"Fetching coins list from {url}")
+
+ try:
+ response = requests.get(url, headers=get_headers())
+ try:
+ response.raise_for_status()
+ except requests.exceptions.RequestException as e:
+ logger.error(
+ f"Error fetching coins list: {str(e)}, "
+ f"Status Code: {e.response.status_code if hasattr(e, 'response') else 'N/A'}, "
+ f"Response: {e.response.text if hasattr(e, 'response') else 'N/A'}, "
+ f"URL: {url}"
+ )
+
+def fetch_coins_list():
+ raise
+ coins = response.json()
+
+ # Save to file
+ output_file = os.path.join(DATA_DIR, "coins_list.json")
+ with open(output_file, "w") as f:
+ json.dump(coins, f, indent=JSON_INDENT)
+
+ logger.info(f"Saved {len(coins)} coins to {output_file}")
+ return coins
+ except Exception as e:
+ logger.error(f"Error fetching coins list: {e}")
+ return None
+
+def fetch_global_data():
+ """
+ Fetches global cryptocurrency market data from the CoinGecko API.
+
+ Retrieves overall market statistics, saves the data to 'global.json' in the data directory,
+ and returns the parsed JSON data. Returns None if the request fails.
+ """
+ url = f"{COINGECKO_API_BASE}/global"
+ logger.info(f"Fetching global market data from {url}")
+
+ try:
+ response = requests.get(url, headers=get_headers())
+ response.raise_for_status()
+ respect_rate_limits(response)
+ global_data = response.json()
+
+ # Save to file
+ output_file = os.path.join(DATA_DIR, "global.json")
+ with open(output_file, "w") as f:
+ json.dump(global_data, f, indent=JSON_INDENT)
+
+ logger.info(f"Saved global market data to {output_file}")
+ return global_data
+ except Exception as e:
+ except Exception as e:
+ logger.error(
+ f"Error fetching top coins: {str(e)}, "
+ f"Status Code: {e.response.status_code if hasattr(e, 'response') else 'N/A'}, "
+ f"Response: {e.response.text if hasattr(e, 'response') else 'N/A'}, "
+ f"URL: {url}"
+ )
+ return None
+ return None
+
+def fetch_top_coins(limit=DEFAULT_TOP_COINS_LIMIT):
+ """
+ Fetches detailed market data for the top cryptocurrencies by market capitalization.
+
+ Retrieves market data for the top `limit` coins from the CoinGecko API, including price change percentages over 1 hour, 24 hours, and 7 days. Saves the resulting data to a JSON file in the data directory. Returns the data as a list of dictionaries, or None if the request fails.
+
+ Args:
+ limit: The number of top coins to fetch (default is 250).
+
+ Returns:
+ A list of dictionaries containing market data for each coin, or None on failure.
+ """
+ url = f"{COINGECKO_API_BASE}/coins/markets"
+ params = {
+ "vs_currency": "usd",
+ "order": "market_cap_desc",
+ "per_page": limit,
+ "page": DEFAULT_PAGE,
+ "sparkline": False,
+ "price_change_percentage": "1h,24h,7d"
+ }
+
+ logger.info(f"Fetching top {limit} coins from {url}")
+
+ try:
+ response = requests.get(url, headers=get_headers(), params=params, timeout=REQUEST_TIMEOUT)
+ response.raise_for_status()
+ respect_rate_limits(response)
+ top_coins = response.json()
+
+ # Save to file
+ output_file = os.path.join(DATA_DIR, f"top_{limit}_coins.json")
+ logger.info(f"Fetching top {limit} coins from {url}")
+