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_ohlc_range_by_id.py
1#!/usr/bin/env python32"""3CoinGecko Coin OHLC Chart within Time Range Tool - MCP Compliant45API Reference: https://docs.coingecko.com/reference/coins-id-ohlc-range6Returns: Array of [timestamp_ms, open, high, low, close] in USD7"""89import os10from dotenv import load_dotenv11import time12import argparse13import json14import sys15try:16from .utils import parse_flexible_time, split_time_range, merge_ohlc_data, get_days_difference, search_coin_by_name17except ImportError:18from utils import parse_flexible_time, split_time_range, merge_ohlc_data, get_days_difference, search_coin_by_name1920from core.http_client import proxied_get2122# Load environment variables from project root directory23project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', '..'))24load_dotenv(os.path.join(project_root, '.env'))2526# MCP Tool Schema27MCP_TOOL_SCHEMA = {28"name": "get_coin_ohlc_range_by_id",29"title": "CoinGecko OHLC Range",30"description": "Retrieve OHLC candlestick data within a custom time range for technical analysis. Supports unlimited time ranges (365+ days) with automatic data splitting for large ranges. Will return a lot of data, use with caution.",31"inputSchema": {32"$schema": "https://json-schema.org/draft/2020-12/schema",33"type": "object",34"properties": {35"coin_id": {36"type": "string",37"minLength": 1,38"description": "The coin ID (e.g., 'bitcoin', 'ethereum') or symbol (e.g., 'BTC', 'ETH')"39},40"from_timestamp": {41"type": ["string", "integer", "number"],42"description": "Start time - supports Unix timestamp, ISO date ('2023-01-01'), or natural language ('2 weeks ago', 'last month')"43},44"to_timestamp": {45"type": ["string", "integer", "number"],46"description": "End time - same formats as from_timestamp"47},48"interval": {49"type": ["string", "null"],50"enum": ["daily", "hourly", None],51"description": "Data interval - 'daily' (max 180 days per request), 'hourly' (max 31 days per request), or null for auto-selection based on time range"52}53},54"required": ["coin_id", "from_timestamp", "to_timestamp"],55"additionalProperties": False56},57"outputSchema": {58"$schema": "https://json-schema.org/draft/2020-12/schema",59"type": "array",60"items": {61"type": "array",62"items": [63{"type": "number", "description": "Unix timestamp in milliseconds"},64{"type": "number", "description": "Opening price in USD"},65{"type": "number", "description": "Highest price in USD"},66{"type": "number", "description": "Lowest price in USD"},67{"type": "number", "description": "Closing price in USD"}68],69"minItems": 5,70"maxItems": 571},72"description": "Array of OHLC candlestick data where each item is [timestamp_ms, open, high, low, close] in USD"73},74"annotations": {75"path": "tools/coingecko/coin_ohlc_range_by_id.py",76"function": "get_coin_ohlc_range_by_id",77"examples": [78{79"name": "basic_daily",80"arguments": {81"coin_id": "bitcoin",82"from_timestamp": "2024-01-01",83"to_timestamp": "2024-01-07",84"interval": "daily"85}86},87{88"name": "natural_language",89"arguments": {90"coin_id": "ETH",91"from_timestamp": "30 days ago",92"to_timestamp": "today",93"interval": "hourly"94}95}96]97}98}99100def get_coin_ohlc_range_by_id(coin_id, from_timestamp=None, to_timestamp=None, interval=None):101"""102Fetch OHLC chart data for a specific coin within a time range from CoinGecko Pro API.103Handles automatic data splitting for large time ranges. Currency fixed to USD.104"""105if not coin_id:106raise ValueError("coin_id parameter is required")107108if not from_timestamp or not to_timestamp:109raise ValueError("Both from_timestamp and to_timestamp are required")110111# Convert symbol to ID if needed112coin_result = search_coin_by_name(coin_id)113if not coin_result:114raise ValueError(f"Could not find coin with symbol or name: {coin_id}")115actual_coin_id = coin_result['id']116117# Currency is fixed to USD118vs_currency = 'usd'119120# Parse flexible time inputs121from_ts = parse_flexible_time(from_timestamp)122to_ts = parse_flexible_time(to_timestamp)123124if from_ts >= to_ts:125raise ValueError("from_timestamp must be earlier than to_timestamp")126127# Validate interval parameter128if interval is not None:129valid_intervals = ['daily', 'hourly']130if interval not in valid_intervals:131raise ValueError(f"interval must be one of {valid_intervals} or None, got: {interval}")132133# Determine optimal interval and splitting strategy134days_diff = get_days_difference(from_ts, to_ts)135136# Auto-select interval if not specified137if interval is None:138if days_diff <= 31:139interval = 'hourly'140else:141interval = 'daily'142143# Validate time range against interval limits144if interval == 'hourly' and days_diff > 31:145if days_diff <= 180:146# Switch to daily for better compatibility147interval = 'daily'148else:149# Split into hourly chunks (31 days each) then merge150return _fetch_ohlc_with_splitting(actual_coin_id, vs_currency, from_ts, to_ts, 'hourly', 31)151elif interval == 'daily' and days_diff > 180:152# Split into daily chunks (180 days each) then merge153return _fetch_ohlc_with_splitting(actual_coin_id, vs_currency, from_ts, to_ts, 'daily', 180)154155# Single request156return _fetch_single_ohlc_range(actual_coin_id, vs_currency, from_ts, to_ts, interval)157158159def _fetch_ohlc_with_splitting(coin_id, vs_currency, from_ts, to_ts, interval, max_days):160"""Fetch OHLC data with automatic splitting for large ranges."""161time_chunks = split_time_range(from_ts, to_ts, max_days=max_days)162data_chunks = []163164for chunk_start, chunk_end in time_chunks:165chunk_data = _fetch_single_ohlc_range(coin_id, vs_currency, chunk_start, chunk_end, interval)166data_chunks.append(chunk_data)167# Small delay between requests to be respectful to the API168time.sleep(0.5)169170# Merge all chunks171return merge_ohlc_data(data_chunks)172173174def _fetch_single_ohlc_range(coin_id, vs_currency, from_timestamp, to_timestamp, interval):175"""Fetch OHLC data for a single time range."""176url = f"https://pro-api.coingecko.com/api/v3/coins/{coin_id}/ohlc/range"177params = {178'vs_currency': vs_currency,179'from': from_timestamp,180'to': to_timestamp181}182if interval:183params['interval'] = interval184185headers = {"x-cg-pro-api-key": os.getenv("COINGECKO_API_KEY")}186187if not headers["x-cg-pro-api-key"]:188raise ValueError("COINGECKO_API_KEY environment variable is required")189190# Retry logic191for attempt in range(3):192try:193resp = proxied_get(url, params=params, headers=headers, timeout=15)194resp.raise_for_status()195return resp.json()196except Exception as e:197if attempt == 2: # Last attempt198raise ConnectionError(f"API request failed after retries: {e}")199time.sleep(1)200201202# Backward compatibility203def get_coin_ohlc_range(coin_id, vs_currency='usd', from_timestamp=None, to_timestamp=None, interval=None):204"""Backward compatibility wrapper for get_coin_ohlc_range_by_id. vs_currency is ignored (always USD)."""205return get_coin_ohlc_range_by_id(coin_id, from_timestamp, to_timestamp, interval)206207208def main():209"""Command-line interface for the coin OHLC range tool."""210211# Handle MCP schema queries first (single argument only)212if len(sys.argv) == 2:213arg = sys.argv[1]214215if arg == "--schema":216print(json.dumps(MCP_TOOL_SCHEMA, indent=2, sort_keys=True, ensure_ascii=False))217return 0218elif arg.startswith("--") and len(arg) > 2:219field_name = arg[2:] # Remove "--" prefix220if field_name in MCP_TOOL_SCHEMA:221field_value = MCP_TOOL_SCHEMA[field_name]222print(json.dumps(field_value, indent=2, sort_keys=True, ensure_ascii=False))223return 0224225parser = argparse.ArgumentParser(226description="Fetch OHLC chart data within a time range for a specific coin from CoinGecko API.",227epilog="""INPUT:228--coin_id: Coin ID/symbol (bitcoin, BTC, ethereum, ETH)229--from_timestamp: Start time ("30 days ago", "2023-01-01", timestamp)230--to_timestamp: End time ("today", "2023-12-31", timestamp)231--interval: Data interval (daily, hourly, or auto-select)232233OUTPUT:234JSON array of OHLC data points:235[{"timestamp": 1640995200, "open": 46300.45, "high": 47100.23,236"low": 45800.12, "close": 46950.78}]237238EXAMPLE:239python coin_ohlc_range_by_id.py --coin_id BTC --from_timestamp "30 days ago" --to_timestamp "today"240python coin_ohlc_range_by_id.py --coin_id ethereum --from_timestamp "2023-01-01" --to_timestamp "2023-12-31" --interval daily241""",242formatter_class=argparse.RawDescriptionHelpFormatter243)244245# MCP schema arguments246parser.add_argument('--schema', action='store_true', help='Output complete MCP Tool Schema (JSON)')247parser.add_argument('--name', action='store_true', help='Output tool name')248parser.add_argument('--title', action='store_true', help='Output tool title')249parser.add_argument('--description', action='store_true', help='Output tool description')250parser.add_argument('--inputSchema', action='store_true', help='Output input schema')251parser.add_argument('--outputSchema', action='store_true', help='Output output schema')252parser.add_argument('--annotations', action='store_true', help='Output annotations')253254# Original functionality arguments255parser.add_argument('--coin_id', help='Coin id (e.g., bitcoin) or symbol (e.g., BTC)')256parser.add_argument('--from_timestamp', type=str,257help='Start time - supports: Unix timestamp, ISO date (2023-01-01), natural language ("2 weeks ago", "last month")')258parser.add_argument('--to_timestamp', type=str,259help='End time - same formats as from_timestamp')260parser.add_argument('--interval', type=str, choices=['daily', 'hourly'],261help='Data interval - "daily" (max 180 days) or "hourly" (max 31 days). Auto-selected if not specified')262263args = parser.parse_args()264265# Handle MCP schema queries266if args.schema:267print(json.dumps(MCP_TOOL_SCHEMA, indent=2, sort_keys=True, ensure_ascii=False))268return 0269elif args.name:270print(json.dumps(MCP_TOOL_SCHEMA["name"], ensure_ascii=False))271return 0272elif args.title:273print(json.dumps(MCP_TOOL_SCHEMA["title"], ensure_ascii=False))274return 0275elif args.description:276print(json.dumps(MCP_TOOL_SCHEMA["description"], ensure_ascii=False))277return 0278elif args.inputSchema:279print(json.dumps(MCP_TOOL_SCHEMA["inputSchema"], indent=2, sort_keys=True, ensure_ascii=False))280return 0281elif args.outputSchema:282print(json.dumps(MCP_TOOL_SCHEMA["outputSchema"], indent=2, sort_keys=True, ensure_ascii=False))283return 0284elif args.annotations:285print(json.dumps(MCP_TOOL_SCHEMA["annotations"], indent=2, sort_keys=True, ensure_ascii=False))286return 0287288# Validate required arguments for normal operation289if not args.coin_id:290parser.error("--coin_id is required")291if not args.from_timestamp:292parser.error("--from_timestamp is required")293if not args.to_timestamp:294parser.error("--to_timestamp is required")295296try:297data = get_coin_ohlc_range_by_id(298coin_id=args.coin_id,299from_timestamp=args.from_timestamp,300to_timestamp=args.to_timestamp,301interval=args.interval302)303print(json.dumps(data, ensure_ascii=False, indent=2))304except Exception as e:305print(f"Failed to fetch OHLC range data: {e}")306return 1307308return 0309310311if __name__ == "__main__":312exit(main())