Skip to content

Commit cac718e

Browse files
committed
Add detailed testing script for CoinGecko API
- Introduced a new testing script `test_coingecko_detailed.py` that provides comprehensive testing for the CoinGecko API, allowing users to fetch detailed token information from multiple endpoints. - The script includes functionality to specify the number of top tokens to fetch, with a default of 10, and aggregates market data and historical volumes for enhanced analysis. - Implemented structured logging to track the progress and results of the tests, improving traceability and debugging capabilities. - Updated the existing `test_coingecko.py` script to include a new argument for specifying the number of days for historical volume data, enhancing its functionality and usability. - These changes aim to improve the testing framework for CoinGecko API interactions, facilitating better data validation and analysis for developers and users alike.
1 parent c73e2bf commit cac718e

File tree

2 files changed

+191
-51
lines changed

2 files changed

+191
-51
lines changed

backend/tests/test_coingecko.py

Lines changed: 51 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,10 @@
33
Run this directly to see the output in your terminal.
44
55
Usage:
6-
python test_coingecko.py --symbol BTC --coin-id bitcoin --num-tokens 10
7-
python test_coingecko.py -s ETH -c ethereum -n 5
6+
python test_coingecko.py --symbol BTC --coin-id bitcoin --num-tokens 10 --days 30
7+
python test_coingecko.py -s ETH -c ethereum -n 5 -d 7
88
"""
99

10-
import asyncio
11-
import aiohttp
1210
import argparse
1311
import sys
1412
from pathlib import Path
@@ -31,65 +29,67 @@ def parse_arguments():
3129
help='CoinGecko coin ID (e.g., bitcoin, ethereum)')
3230
parser.add_argument('-n', '--num-tokens', type=int, default=10,
3331
help='Number of top tokens to fetch (default: 10, max: 250)')
32+
parser.add_argument('-d', '--days', type=int, default=30,
33+
help='Number of days for historical volume data (default: 30)')
3434
return parser.parse_args()
3535

36-
async def run_tests(symbol: str, coin_id: str, num_tokens: int):
36+
def run_tests(target_symbol: str, target_coin_id: str, num_tokens: int, days: int):
3737
"""Run tests for CoinGecko API functions."""
3838
print("\n=== Starting CoinGecko API Tests ===\n")
3939

40-
async with aiohttp.ClientSession() as session:
41-
print(f"Fetching data for {num_tokens} top tokens by market cap...")
42-
market_data = await fetch_all_coingecko_market_data(session, num_tokens)
40+
print(f"Fetching data for {num_tokens} top tokens by market cap...")
41+
market_data = fetch_all_coingecko_market_data(num_tokens)
42+
43+
if not market_data:
44+
print("Error: No market data received")
45+
return
46+
47+
total_tokens = len(market_data)
48+
print(f"\nReceived data for {total_tokens} tokens")
49+
if total_tokens < num_tokens:
50+
print(f"Note: Requested {num_tokens} tokens but only received {total_tokens}.")
51+
print("This is normal if you requested more tokens than are available on CoinGecko.")
52+
53+
print("\nTop 10 tokens by market cap (or all if less than 10):")
54+
tokens_to_show = sorted(
55+
market_data.items(),
56+
key=lambda x: x[1].get('market_cap_rank', float('inf'))
57+
)[:min(10, total_tokens)]
58+
59+
for current_coin_id, data in tokens_to_show:
60+
symbol = data.get('symbol', '???')
61+
rank = data.get('market_cap_rank', 'N/A')
62+
price = data.get('current_price', 'N/A')
63+
print(f"#{rank}: {symbol} @ ${price}")
64+
65+
# Test specific coin data if provided
66+
if target_coin_id and target_symbol:
67+
print(f"\nDetailed data for {target_symbol}:")
68+
coin_data = market_data.get(target_coin_id)
4369

44-
if not market_data:
45-
print("Error: No market data received")
70+
if not coin_data:
71+
print(f"Error: Could not find data for {target_symbol} ({target_coin_id})")
4672
return
4773

48-
total_tokens = len(market_data)
49-
print(f"\nReceived data for {total_tokens} tokens")
50-
if total_tokens < num_tokens:
51-
print(f"Note: Requested {num_tokens} tokens but only received {total_tokens}.")
52-
print("This is normal if you requested more tokens than are available on CoinGecko.")
53-
54-
print("\nTop 10 tokens by market cap (or all if less than 10):")
55-
tokens_to_show = sorted(
56-
market_data.items(),
57-
key=lambda x: x[1].get('market_cap_rank', float('inf'))
58-
)[:min(10, total_tokens)]
59-
60-
for coin_id, data in tokens_to_show:
61-
symbol = data.get('symbol', '???')
62-
rank = data.get('market_cap_rank', 'N/A')
63-
price = data.get('current_price', 'N/A')
64-
print(f"#{rank}: {symbol} @ ${price}")
74+
# Display detailed coin information
75+
print(f"Name: {coin_data.get('name')}")
76+
print(f"Current Price: ${coin_data.get('current_price')}")
77+
print(f"Market Cap Rank: #{coin_data.get('market_cap_rank')}")
78+
print(f"Market Cap: ${coin_data.get('market_cap'):,}")
79+
print(f"Circulating Supply: {coin_data.get('circulating_supply'):,.2f} {target_symbol}")
80+
print(f"All-Time High: ${coin_data.get('ath_price')}")
6581

66-
# Test specific coin data if provided
67-
if coin_id and symbol:
68-
print(f"\nDetailed data for {symbol}:")
69-
coin_data = market_data.get(coin_id)
70-
71-
if not coin_data:
72-
print(f"Error: Could not find data for {symbol} ({coin_id})")
73-
return
74-
75-
# Display detailed coin information
76-
print(f"Name: {coin_data.get('name')}")
77-
print(f"Current Price: ${coin_data.get('current_price')}")
78-
print(f"Market Cap Rank: #{coin_data.get('market_cap_rank')}")
79-
print(f"Market Cap: ${coin_data.get('market_cap'):,}")
80-
print(f"Circulating Supply: {coin_data.get('circulating_supply'):,.2f} {symbol}")
81-
print(f"All-Time High: ${coin_data.get('ath_price')}")
82-
83-
print("\nFetching historical volume data...")
84-
volume_data = await fetch_coingecko_historical_volume(coin_id)
85-
if volume_data:
86-
print(f"30-day Volume: ${volume_data:,.2f}")
87-
else:
88-
print("Error: Could not fetch volume data")
82+
print(f"\nFetching {days}-day historical volume data...")
83+
volume_data = fetch_coingecko_historical_volume(target_coin_id, number_of_days=days)
84+
if volume_data:
85+
print(f"{days}-day Volume: ${volume_data:,.2f}")
86+
else:
87+
print("Error: Could not fetch volume data")
8988

9089
if __name__ == "__main__":
9190
args = parse_arguments()
9291
print(f"Starting CoinGecko API tests for {args.symbol} ({args.coin_id})...")
9392
print(f"Will fetch top {args.num_tokens} tokens by market cap...")
94-
asyncio.run(run_tests(args.symbol, args.coin_id, args.num_tokens))
93+
print(f"Historical volume period: {args.days} days")
94+
run_tests(args.symbol, args.coin_id, args.num_tokens, args.days)
9595
print("\nTests completed!")
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
"""
2+
Detailed testing script for CoinGecko API that aggregates data from multiple endpoints.
3+
Provides comprehensive token information including market data and historical volumes.
4+
5+
Usage:
6+
python test_coingecko_detailed.py --num-tokens 10
7+
python test_coingecko_detailed.py -n 5
8+
"""
9+
10+
import argparse
11+
import json
12+
import sys
13+
from pathlib import Path
14+
from typing import List, Dict
15+
import logging
16+
17+
# Configure logging
18+
logging.basicConfig(
19+
level=logging.INFO,
20+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
21+
)
22+
logger = logging.getLogger(__name__)
23+
24+
# Add the parent directory to the Python path so we can import the utils
25+
sys.path.append(str(Path(__file__).parent.parent.parent))
26+
27+
from backend.utils.coingecko_api import (
28+
fetch_all_coingecko_market_data,
29+
fetch_coingecko_historical_volume,
30+
fetch_all_coingecko_volumes
31+
)
32+
33+
def parse_arguments():
34+
parser = argparse.ArgumentParser(description='Test CoinGecko API with detailed output')
35+
parser.add_argument('-n', '--num-tokens', type=int, default=10,
36+
help='Number of top tokens to fetch (default: 10, max: 250)')
37+
return parser.parse_args()
38+
39+
def format_token_data(market_data: Dict, volume_30d: float) -> Dict:
40+
"""Format token data according to the specified structure."""
41+
return {
42+
"symbol": market_data.get('symbol', ''),
43+
"coingecko_data": {
44+
"coingecko_name": market_data.get('name'),
45+
"coingecko_id": market_data.get('id'),
46+
"coingecko_image_url": market_data.get('image_url'),
47+
"coingecko_current_price": market_data.get('current_price'),
48+
"coingecko_market_cap_rank": market_data.get('market_cap_rank'),
49+
"coingecko_market_cap": market_data.get('market_cap'),
50+
"coingecko_fully_diluted_valuation": market_data.get('fully_diluted_valuation'),
51+
"coingecko_total_volume_24h": market_data.get('total_volume_24h'),
52+
"coingecko_mc_derived": market_data.get('market_cap'), # Same as market_cap for now
53+
"coingecko_circulating": market_data.get('circulating_supply'),
54+
"coingecko_total_supply": market_data.get('total_supply'),
55+
"coingecko_max_supply": market_data.get('max_supply'),
56+
"coingecko_ath_price": market_data.get('ath_price'),
57+
"coingecko_ath_change_percentage": market_data.get('ath_change_percentage'),
58+
"coingecko_volume_30d": volume_30d
59+
}
60+
}
61+
62+
def run_detailed_test(num_tokens: int = 10) -> None:
63+
"""
64+
Run a detailed test of CoinGecko API functionality.
65+
66+
Args:
67+
num_tokens: Number of tokens to fetch data for (default: 10)
68+
"""
69+
logger.info(f"Starting detailed test for {num_tokens} tokens")
70+
71+
# Fetch market data for all tokens
72+
market_data = fetch_all_coingecko_market_data()
73+
if not market_data or isinstance(market_data, dict) and "error" in market_data:
74+
logger.error("Failed to fetch market data")
75+
return
76+
77+
# Convert dictionary to list and sort by market cap
78+
tokens_list = []
79+
for coin_id, token_data in market_data.items():
80+
token_data['id'] = coin_id # Add coin_id to the token data
81+
tokens_list.append(token_data)
82+
83+
# Sort tokens by market cap and take top N
84+
sorted_tokens = sorted(
85+
tokens_list,
86+
key=lambda x: float(x.get("market_cap", 0) or 0), # Handle None values
87+
reverse=True
88+
)[:num_tokens]
89+
90+
# Get list of coin IDs for volume fetch
91+
coin_ids = [token.get("id") for token in sorted_tokens]
92+
logger.info(f"Fetching 30d volume data for top {len(coin_ids)} tokens")
93+
94+
# Fetch 30d volume data for all tokens at once
95+
volume_data = fetch_all_coingecko_volumes(coin_ids)
96+
97+
results = []
98+
total_tokens = len(sorted_tokens)
99+
100+
for idx, token in enumerate(sorted_tokens, 1):
101+
coin_id = token.get("id")
102+
symbol = token.get("symbol", "").upper()
103+
104+
# Get the 30d volume from the volume data
105+
total_volume = volume_data.get(coin_id, 0)
106+
107+
results.append({
108+
"symbol": symbol,
109+
"name": token.get("name"),
110+
"market_cap": token.get("market_cap", 0),
111+
"total_volume_30d": total_volume
112+
})
113+
114+
# Log progress
115+
progress = (idx / total_tokens) * 100
116+
logger.info(f"Progress: {progress:.1f}% ({idx}/{total_tokens})")
117+
118+
# Format and display results
119+
if results:
120+
print("\nResults:")
121+
print(f"{'Symbol':<10} {'Name':<20} {'Market Cap':>15} {'30d Volume':>20}")
122+
print("-" * 65)
123+
124+
for r in results:
125+
print(
126+
f"{r['symbol']:<10} "
127+
f"{r['name'][:20]:<20} "
128+
f"${r['market_cap']:>14,.0f} "
129+
f"${r['total_volume_30d']:>19,.0f}"
130+
)
131+
else:
132+
logger.warning("No results to display")
133+
134+
if __name__ == "__main__":
135+
args = parse_arguments()
136+
print(f"Starting detailed CoinGecko API tests for {args.num_tokens} tokens...")
137+
138+
run_detailed_test(args.num_tokens)
139+
140+
print("\nTests completed!")

0 commit comments

Comments
 (0)