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/coin_prices.py
1#!/usr/bin/env python32"""3CoinGecko Coin Prices at Multiple Timestamps Tool45This tool provides functionality to fetch coin prices at multiple specific timestamps6using the CoinGecko Pro API. It supports flexible time input formats including "now"7for current prices and various date formats for historical prices.89MCP Tool Schema compliant with CLI interface supporting:10- --help: Output complete Schema (JSON, pretty-printed)11- --<field>: Output specific Schema field1213Dependencies:14- requests: For HTTP API calls15- python-dotenv: For environment variable management1617Environment Variables Required:18- COINGECKO_API_KEY: Your CoinGecko Pro API key (required for API access)1920API Limitations:21- Rate limits apply based on your CoinGecko Pro subscription22- Historical data available from February 9, 2018 onwards23- Current price endpoint updates every 20 seconds for Pro API tiers24"""2526import os27from dotenv import load_dotenv28import time29import argparse30import json31from typing import List, Dict, Any, Union32from datetime import datetime3334try:35from .utils import parse_flexible_time, format_dd_mm_yyyy_date, search_coin_by_name36except ImportError:37from utils import parse_flexible_time, format_dd_mm_yyyy_date, search_coin_by_name3839from core.http_client import proxied_get4041# Load environment variables from agent_framework root directory42# Path: extensions/trading/tools/coingecko/ -> go up 4 levels to agent_framework/43project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', '..'))44load_dotenv(os.path.join(project_root, '.env'))4546# MCP Tool Schema47MCP_TOOL_SCHEMA = {48"name": "get_coin_prices_at_timestamps",49"title": "CoinGecko Coin Prices at Multiple Timestamps",50"description": "Fetch cryptocurrency prices at multiple specific timestamps using CoinGecko Pro API. Supports flexible time input formats including 'now' for current prices and various date formats for historical prices.",51"inputSchema": {52"type": "object",53"properties": {54"coin_ids": {55"type": ["string", "array"],56"items": {57"type": "string"58},59"description": "Single coin ID/symbol or list of coin IDs/symbols. Supports comma-separated strings, individual strings, or arrays. Accepts both full names ('bitcoin') and symbols ('BTC'). Case-insensitive.",60"examples": ["bitcoin", "BTC", "BTC,ETH,SOL", ["bitcoin", "ethereum", "solana"]]61},62"timestamps": {63"type": "array",64"items": {65"type": "string"66},67"description": "List of timestamp strings in various formats. Supports 'now' for current price, Unix timestamps, ISO dates, and natural language expressions.",68"default": ["now"],69"examples": [["now"], ["now", "2023-01-01", "yesterday"], ["now", "1640995200", "last month"]]70},71"vs_currency": {72"type": "string",73"description": "The target currency for price quotes",74"default": "usd",75"examples": ["usd", "eur", "btc"]76},77"rate_limit_delay": {78"type": "number",79"description": "Delay between API calls in seconds to respect rate limits",80"default": 1.0,81"minimum": 0.1,82"maximum": 10.083}84},85"required": ["coin_ids"],86"additionalProperties": False87},88"outputSchema": {89"type": "array",90"items": {91"type": "object",92"properties": {93"timestamp": {94"type": "string",95"description": "Original timestamp input"96},97"parsed_timestamp": {98"type": ["integer", "null"],99"description": "Unix timestamp (for historical data) or null for errors"100},101"date": {102"type": "string",103"description": "Human readable date"104},105"price": {106"type": ["number", "null"],107"description": "Price in specified currency or null for errors"108},109"coin_id": {110"type": "string",111"description": "CoinGecko coin ID used for API call"112},113"coin_symbol": {114"type": "string",115"description": "Coin symbol (e.g., 'BTC')"116},117"coin_name": {118"type": "string",119"description": "Coin name (e.g., 'Bitcoin')"120},121"status": {122"type": "string",123"enum": ["success", "error"],124"description": "Status of the price fetch operation"125},126"error": {127"type": ["string", "null"],128"description": "Error message if status is 'error', otherwise null"129}130},131"required": ["timestamp", "parsed_timestamp", "date", "price", "coin_id", "coin_symbol", "coin_name", "status", "error"]132}133},134"annotations": {135"path": "tools/coingecko/coin_prices.py",136"function": "get_coin_prices_at_timestamps",137"examples": [138{139"name": "Single coin current price",140"input": {141"coin_ids": "bitcoin"142},143"output": [144{145"timestamp": "now",146"parsed_timestamp": 1672531200,147"date": "2023-01-01 00:00:00 UTC",148"price": 16547.32,149"coin_id": "bitcoin",150"coin_symbol": "BTC",151"coin_name": "Bitcoin",152"status": "success",153"error": None154}155]156},157{158"name": "Multiple coins with timestamps",159"input": {160"coin_ids": "BTC,ETH,SOL",161"timestamps": ["now", "yesterday"]162},163"output": [164{165"timestamp": "now",166"parsed_timestamp": 1672531200,167"date": "2023-01-01 00:00:00 UTC",168"price": 16547.32,169"coin_id": "bitcoin",170"coin_symbol": "BTC",171"coin_name": "Bitcoin",172"status": "success",173"error": None174},175{176"timestamp": "yesterday",177"parsed_timestamp": 1672444800,178"date": "2022-12-31 00:00:00 UTC",179"price": 16625.11,180"coin_id": "bitcoin",181"coin_symbol": "BTC",182"coin_name": "Bitcoin",183"status": "success",184"error": None185}186]187}188],189"env_vars": {190"COINGECKO_API_KEY": {191"description": "CoinGecko Pro API key for authentication",192"required": True,193"url": "https://coingecko.com/en/api"194}195},196"data_availability": {197"current": "Real-time updates every 20 seconds",198"historical": "Available from February 9, 2018 onwards"199}200}201}202203204def get_coin_prices_at_timestamps(coin_ids: Union[str, List[str]],205timestamps: List[str] = None,206vs_currency: str = 'usd',207rate_limit_delay: float = 1.0) -> List[Dict[str, Any]]:208"""209Get multiple coin prices at multiple specific timestamps.210211This function fetches prices for one or more coins at multiple timestamps using CoinGecko API.212It automatically handles different time input formats and uses appropriate213API endpoints for current vs historical prices with rate limiting protection.214215Args:216coin_ids (Union[str, List[str]]): Single coin ID/symbol or list of coin IDs/symbols217- String: "bitcoin" or "BTC" for single coin218- String with comma-separation: "BTC,ETH,SOL" for multiple coins219- List: ["bitcoin", "ethereum", "solana"] for multiple coins220timestamps (List[str], optional): List of timestamp strings in various formats (default: ["now"]):221- "now": Current price222- Unix timestamp (1640995200)223- ISO date ('2023-01-01' or '2023-01-01 10:30:00')224- Natural language ('2 weeks ago', 'last month', 'yesterday')225vs_currency (str): The target currency (default: 'usd')226rate_limit_delay (float): Delay between API calls in seconds (default: 1.0)227228Returns:229List[Dict[str, Any]]: List of price data dictionaries with:230- timestamp: Original timestamp input231- parsed_timestamp: Unix timestamp (for historical data)232- date: Human readable date233- price: Price in specified currency234- coin_id: CoinGecko coin ID used for API call235- coin_symbol: Coin symbol (e.g., 'BTC')236- coin_name: Coin name (e.g., 'Bitcoin')237- status: 'success' or 'error'238- error: Error message (if status is 'error')239240Raises:241ValueError: If coin_ids is invalid242ConnectionError: If API requests fail after retries243244Example Usage:245# Single coin with multiple timestamps246timestamps = ["now", "2023-01-01", "30 days ago"]247prices = get_coin_prices_at_timestamps("BTC", timestamps)248249# Multiple coins with single timestamp250prices = get_coin_prices_at_timestamps(["BTC", "ETH", "SOL"], ["now"])251252# Multiple coins with multiple timestamps (comma-separated string)253prices = get_coin_prices_at_timestamps("BTC,ETH,SOL", ["now", "yesterday"])254255for price_data in prices:256print(f"{price_data['coin_symbol']} on {price_data['date']}: ${price_data['price']}")257"""258if not coin_ids:259raise ValueError("coin_ids parameter is required")260261# Default to current price if no timestamps provided262if timestamps is None:263timestamps = ["now"]264elif not isinstance(timestamps, list):265raise ValueError("timestamps must be a list")266267# Parse coin_ids input - handle string, comma-separated string, or list268if isinstance(coin_ids, str):269# Check if it's comma-separated270if ',' in coin_ids:271coin_list = [coin.strip() for coin in coin_ids.split(',') if coin.strip()]272else:273coin_list = [coin_ids.strip()]274elif isinstance(coin_ids, list):275coin_list = [str(coin).strip() for coin in coin_ids if str(coin).strip()]276else:277raise ValueError("coin_ids must be a string or list of strings")278279if not coin_list:280raise ValueError("No valid coin IDs provided")281282# Resolve all coin IDs283resolved_coins = []284for coin_input in coin_list:285coin_result = search_coin_by_name(coin_input)286if not coin_result:287raise ValueError(f"Could not find coin with symbol or name: {coin_input}")288resolved_coins.append(coin_result)289290results = []291total_api_calls = len(resolved_coins) * len(timestamps)292call_count = 0293294# Get prices for each coin at each timestamp295for coin_data in resolved_coins:296actual_coin_id = coin_data['id']297coin_symbol = coin_data['symbol']298coin_name = coin_data['name']299300for timestamp in timestamps:301try:302result = _get_single_price(actual_coin_id, timestamp, vs_currency)303result['coin_id'] = actual_coin_id304result['coin_symbol'] = coin_symbol305result['coin_name'] = coin_name306results.append(result)307308call_count += 1309# Add rate limiting delay between API calls (except after the last call)310if call_count < total_api_calls:311time.sleep(rate_limit_delay)312313except Exception as e:314error_result = {315'timestamp': timestamp,316'parsed_timestamp': None,317'date': str(timestamp),318'price': None,319'coin_id': actual_coin_id,320'coin_symbol': coin_symbol,321'coin_name': coin_name,322'status': 'error',323'error': str(e)324}325results.append(error_result)326continue327328return results329330331def _get_single_price(coin_id: str, timestamp: str, vs_currency: str) -> Dict[str, Any]:332"""Get price for a single timestamp."""333timestamp_str = str(timestamp).strip().lower()334335# Handle "now" case - get current price336if timestamp_str == "now":337return _get_current_price(coin_id, vs_currency, timestamp)338339# Parse timestamp and get historical price340try:341parsed_ts = parse_flexible_time(timestamp)342return _get_historical_price(coin_id, parsed_ts, vs_currency, timestamp)343except Exception as e:344raise ValueError(f"Invalid timestamp format '{timestamp}': {e}")345346347def _get_current_price(coin_id: str, vs_currency: str, original_timestamp: str) -> Dict[str, Any]:348"""Get current price using simple/price endpoint."""349url = "https://pro-api.coingecko.com/api/v3/simple/price"350params = {351'ids': coin_id,352'vs_currencies': vs_currency,353'include_last_updated_at': 'true'354}355356headers = {"x-cg-pro-api-key": os.getenv("COINGECKO_API_KEY")}357358if not headers["x-cg-pro-api-key"]:359raise ValueError("COINGECKO_API_KEY environment variable is required")360361# Retry logic362for attempt in range(3):363try:364resp = proxied_get(url, params=params, headers=headers, timeout=15)365resp.raise_for_status()366data = resp.json()367368if coin_id not in data:369raise ValueError(f"Coin '{coin_id}' not found")370371coin_data = data[coin_id]372price = coin_data.get(vs_currency)373last_updated = coin_data.get('last_updated_at')374375if price is None:376raise ValueError(f"Price not available for currency '{vs_currency}'")377378return {379'timestamp': original_timestamp,380'parsed_timestamp': last_updated,381'date': datetime.utcfromtimestamp(last_updated).strftime('%Y-%m-%d %H:%M:%S UTC') if last_updated else 'now',382'price': price,383'status': 'success',384'error': None385}386387except Exception as e:388if attempt == 2: # Last attempt389raise ConnectionError(f"Failed to fetch current price: {e}")390time.sleep(1)391392393def _get_historical_price(coin_id: str, timestamp: int, vs_currency: str, original_timestamp: str) -> Dict[str, Any]:394"""Get historical price using history endpoint."""395# Format date for CoinGecko API (dd-mm-yyyy)396date_str = format_dd_mm_yyyy_date(timestamp)397398url = f"https://pro-api.coingecko.com/api/v3/coins/{coin_id}/history"399params = {400'date': date_str,401'localization': 'false'402}403404headers = {"x-cg-pro-api-key": os.getenv("COINGECKO_API_KEY")}405406if not headers["x-cg-pro-api-key"]:407raise ValueError("COINGECKO_API_KEY environment variable is required")408409# Retry logic410for attempt in range(3):411try:412resp = proxied_get(url, params=params, headers=headers, timeout=15)413resp.raise_for_status()414data = resp.json()415416# Extract price from market_data417if 'market_data' not in data or 'current_price' not in data['market_data']:418raise ValueError(f"Price data not available for date {date_str}")419420price_data = data['market_data']['current_price']421price = price_data.get(vs_currency)422423if price is None:424raise ValueError(f"Price not available for currency '{vs_currency}' on date {date_str}")425426return {427'timestamp': original_timestamp,428'parsed_timestamp': timestamp,429'date': datetime.utcfromtimestamp(timestamp).strftime('%Y-%m-%d %H:%M:%S UTC'),430'price': price,431'status': 'success',432'error': None433}434435except Exception as e:436if attempt == 2: # Last attempt437raise ConnectionError(f"Failed to fetch historical price for {date_str}: {e}")438time.sleep(1)439440441def main():442"""Command-line interface for the coin prices at timestamps tool with MCP Schema support."""443444import sys445446# Handle MCP Schema CLI arguments (only if they don't contain coin_ids or timestamps)447if len(sys.argv) > 1 and not any("coin_ids" in arg or "timestamps" in arg for arg in sys.argv[1:]):448arg = sys.argv[1]449450# Handle --help (complete schema) - only if no other arguments451if arg == "--schema" and len(sys.argv) == 2:452print(json.dumps(MCP_TOOL_SCHEMA, indent=2, sort_keys=True, ensure_ascii=False))453return 0454455# Handle specific field requests (e.g., --name, --description, --inputSchema)456if arg.startswith("--") and len(arg) > 2 and len(sys.argv) == 2:457field_name = arg[2:] # Remove "--" prefix458if field_name in MCP_TOOL_SCHEMA:459field_value = MCP_TOOL_SCHEMA[field_name]460print(json.dumps(field_value, indent=2, sort_keys=True, ensure_ascii=False))461return 0462463# Original CLI functionality464parser = argparse.ArgumentParser(465description="Get coin prices at multiple specific timestamps from CoinGecko API.",466epilog="""INPUT:467--coin_ids: Coin symbols/names (BTC, bitcoin, "BTC,ETH,SOL")468--timestamps: Time points ("now,yesterday,2023-01-01,last month")469470OUTPUT:471JSON array with price data for each coin-timestamp combination:472[{"timestamp": "now", "price": 16547.32, "coin_symbol": "BTC",473"coin_name": "Bitcoin", "date": "2023-01-01 00:00:00 UTC", "status": "success"}]474475EXAMPLE:476python coin_prices.py --coin_ids BTC --timestamps "now,yesterday"477python coin_prices.py --coin_ids "BTC,ETH" --timestamps "now"478""",479formatter_class=argparse.RawDescriptionHelpFormatter480)481482parser.add_argument('--coin_ids', required=True,483help='Coin id(s), symbol(s), or comma-separated list (e.g., "bitcoin", "BTC", "BTC,ETH,SOL")')484parser.add_argument('--timestamps',485help='Comma-separated list of timestamps (e.g., "now,2023-01-01,yesterday"). Default: "now"')486487args = parser.parse_args()488489try:490# Parse comma-separated timestamps or use default491if args.timestamps:492timestamps = [ts.strip() for ts in args.timestamps.split(',') if ts.strip()]493if not timestamps:494raise ValueError("No valid timestamps provided")495else:496timestamps = None # Will default to ["now"] in function497498# Call main function with fixed parameters499results = get_coin_prices_at_timestamps(500coin_ids=args.coin_ids,501timestamps=timestamps,502vs_currency='usd',503rate_limit_delay=1.0504)505506# Output in JSON format only507print(json.dumps(results, ensure_ascii=False, indent=2))508509except Exception as e:510error_result = {511"error": str(e),512"timestamp": datetime.now().isoformat(),513"status": "failed"514}515print(json.dumps(error_result, indent=2, ensure_ascii=False))516return 1517518return 0519520521if __name__ == "__main__":522exit(main())