docs: expand AVE Claw Hackathon research with code examples and guides
- Added 3 runnable Python scripts for Data REST, Data WebSocket, and Trading APIs - Expanded research with 7 new sections: - Extended API documentation with full request/response examples - Working code examples with CLI commands - Security guide (API keys, HMAC signing, private key protection) - Testing strategies (dry-run, paper trading, testnets) - Troubleshooting guide with error codes - Competitive analysis vs DexScreener, Moralis, CoinGecko - Quick reference appendix Research doc grew from 242 to 906 lines
This commit is contained in:
494
docs/scripts/ave_data_rest.py
Normal file
494
docs/scripts/ave_data_rest.py
Normal file
@@ -0,0 +1,494 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
AVE Cloud Data REST API - Token Search, Prices, Klines, Holders, Risk
|
||||
|
||||
Usage:
|
||||
export AVE_API_KEY=your_api_key_here
|
||||
export API_PLAN=free # free, normal, pro
|
||||
|
||||
python3 docs/scripts/ave_data_rest.py search --keyword PEPE
|
||||
python3 docs/scripts/ave_data_rest.py price --token-ids "PEPE-bsc,TRUMP-bsc"
|
||||
python3 docs/scripts/ave_data_rest.py trending --chain bsc
|
||||
python3 docs/scripts/ave_data_rest.py token --token-id "0x6982508145454Ce325dDbE47a25d4ec3d2311933-bsc"
|
||||
python3 docs/scripts/ave_data_rest.py risk --token-id "0x6982508145454Ce325dDbE47a25d4ec3d2311933-bsc"
|
||||
python3 docs/scripts/ave_data_rest.py holders --token-id "0x6982508145454Ce325dDbE47a25d4ec3d2311933-bsc"
|
||||
python3 docs/scripts/ave_data_rest.py klines --token-id "0x6982508145454Ce325dDbE47a25d4ec3d2311933-bsc"
|
||||
python3 docs/scripts/ave_data_rest.py pairs --pair-id "0x16b9a82891338f9ba80e2d6970fdda79d1eb0dae-bsc"
|
||||
python3 docs/scripts/ave_data_rest.py wallet-pnl --wallet "0xd9c500dff816a1da21a48a732d3498bf09dc9aeb" --chain bsc --token "0x55d398326f99059fF775485246999027B3197955"
|
||||
python3 docs/scripts/ave_data_rest.py wallet-info --wallet "0xd9c500dff816a1da21a48a732d3498bf09dc9aeb" --chain bsc
|
||||
python3 docs/scripts/ave_data_rest.py smart-wallets --chain bsc
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
|
||||
import requests
|
||||
|
||||
BASE_URL = "https://prod.ave-api.com"
|
||||
API_KEY = os.getenv("AVE_API_KEY", "")
|
||||
API_PLAN = os.getenv("API_PLAN", "free")
|
||||
|
||||
CU_COSTS = {
|
||||
"search": 5,
|
||||
"price": 100,
|
||||
"trending": 5,
|
||||
"token": 5,
|
||||
"risk": 10,
|
||||
"holders": 10,
|
||||
"klines_token": 10,
|
||||
"klines_pair": 10,
|
||||
"pairs": 5,
|
||||
"wallet_pnl": 5,
|
||||
"wallet_info": 5,
|
||||
"wallet_tokens": 10,
|
||||
"smart_wallet": 5,
|
||||
}
|
||||
|
||||
TIER_LIMITS = {
|
||||
"free": {"tps": 1, "data_wss": False, "proxy_wallet": False},
|
||||
"normal": {"tps": 5, "data_wss": False, "proxy_wallet": True},
|
||||
"pro": {"tps": 20, "data_wss": True, "proxy_wallet": True},
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class ApiResponse:
|
||||
status: int
|
||||
data: Optional[dict] = None
|
||||
error: Optional[str] = None
|
||||
|
||||
|
||||
def make_headers():
|
||||
return {
|
||||
"X-API-KEY": API_KEY,
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
|
||||
def handle_response(response: requests.Response) -> ApiResponse:
|
||||
if response.status_code == 200:
|
||||
return ApiResponse(status=200, data=response.json())
|
||||
elif response.status_code == 401:
|
||||
return ApiResponse(
|
||||
status=401, error="Unauthorized - invalid or missing API key"
|
||||
)
|
||||
elif response.status_code == 403:
|
||||
return ApiResponse(
|
||||
status=403, error="Forbidden - API key expired or plan limits reached"
|
||||
)
|
||||
elif response.status_code == 429:
|
||||
return ApiResponse(status=429, error="Rate limited - TPS exceeded")
|
||||
else:
|
||||
return ApiResponse(status=response.status_code, error=response.text)
|
||||
|
||||
|
||||
def search_tokens(
|
||||
keyword: str, chain: Optional[str] = None, limit: int = 100
|
||||
) -> ApiResponse:
|
||||
params = {"keyword": keyword, "limit": limit}
|
||||
if chain:
|
||||
params["chain"] = chain
|
||||
response = requests.get(
|
||||
f"{BASE_URL}/v2/tokens", headers=make_headers(), params=params
|
||||
)
|
||||
return handle_response(response)
|
||||
|
||||
|
||||
def get_token_price(token_ids: list) -> ApiResponse:
|
||||
data = {"token_ids": token_ids}
|
||||
response = requests.post(
|
||||
f"{BASE_URL}/v2/tokens/price", headers=make_headers(), json=data
|
||||
)
|
||||
return handle_response(response)
|
||||
|
||||
|
||||
def get_trending_tokens(chain: str, page: int = 0, page_size: int = 50) -> ApiResponse:
|
||||
params = {"chain": chain, "current_page": page, "page_size": page_size}
|
||||
response = requests.get(
|
||||
f"{BASE_URL}/v2/tokens/trending", headers=make_headers(), params=params
|
||||
)
|
||||
return handle_response(response)
|
||||
|
||||
|
||||
def get_token_detail(token_id: str) -> ApiResponse:
|
||||
response = requests.get(f"{BASE_URL}/v2/tokens/{token_id}", headers=make_headers())
|
||||
return handle_response(response)
|
||||
|
||||
|
||||
def get_token_risk(token_id: str) -> ApiResponse:
|
||||
response = requests.get(
|
||||
f"{BASE_URL}/v2/contracts/{token_id}", headers=make_headers()
|
||||
)
|
||||
return handle_response(response)
|
||||
|
||||
|
||||
def get_top_holders(token_id: str, limit: int = 100) -> ApiResponse:
|
||||
params = {"limit": limit} if limit != 100 else {}
|
||||
response = requests.get(
|
||||
f"{BASE_URL}/v2/tokens/top100/{token_id}", headers=make_headers(), params=params
|
||||
)
|
||||
return handle_response(response)
|
||||
|
||||
|
||||
def get_klines_by_token(
|
||||
token_id: str, interval: str = "1h", limit: int = 100
|
||||
) -> ApiResponse:
|
||||
params = {"interval": interval, "limit": limit}
|
||||
response = requests.get(
|
||||
f"{BASE_URL}/v2/klines/token/{token_id}", headers=make_headers(), params=params
|
||||
)
|
||||
return handle_response(response)
|
||||
|
||||
|
||||
def get_klines_by_pair(
|
||||
pair_id: str, interval: str = "1h", limit: int = 100
|
||||
) -> ApiResponse:
|
||||
params = {"interval": interval, "limit": limit}
|
||||
response = requests.get(
|
||||
f"{BASE_URL}/v2/klines/pair/{pair_id}", headers=make_headers(), params=params
|
||||
)
|
||||
return handle_response(response)
|
||||
|
||||
|
||||
def get_pair_detail(pair_id: str) -> ApiResponse:
|
||||
response = requests.get(f"{BASE_URL}/v2/pairs/{pair_id}", headers=make_headers())
|
||||
return handle_response(response)
|
||||
|
||||
|
||||
def get_wallet_pnl(
|
||||
wallet_address: str,
|
||||
chain: str,
|
||||
token_address: str,
|
||||
from_time: Optional[int] = None,
|
||||
to_time: Optional[int] = None,
|
||||
) -> ApiResponse:
|
||||
params = {
|
||||
"wallet_address": wallet_address,
|
||||
"chain": chain,
|
||||
"token_address": token_address,
|
||||
}
|
||||
if from_time:
|
||||
params["from_time"] = from_time
|
||||
if to_time:
|
||||
params["to_time"] = to_time
|
||||
response = requests.get(
|
||||
f"{BASE_URL}/v2/address/pnl", headers=make_headers(), params=params
|
||||
)
|
||||
return handle_response(response)
|
||||
|
||||
|
||||
def get_wallet_info(wallet_address: str, chain: str) -> ApiResponse:
|
||||
params = {"wallet_address": wallet_address, "chain": chain}
|
||||
response = requests.get(
|
||||
f"{BASE_URL}/v2/address/walletinfo", headers=make_headers(), params=params
|
||||
)
|
||||
return handle_response(response)
|
||||
|
||||
|
||||
def get_wallet_tokens(
|
||||
wallet_address: str,
|
||||
chain: str,
|
||||
sort: str = "last_txn_time",
|
||||
sort_dir: str = "desc",
|
||||
hide_sold: int = 0,
|
||||
hide_small: int = 0,
|
||||
) -> ApiResponse:
|
||||
params = {
|
||||
"wallet_address": wallet_address,
|
||||
"chain": chain,
|
||||
"sort": sort,
|
||||
"sort_dir": sort_dir,
|
||||
"hide_sold": hide_sold,
|
||||
"hide_small": hide_small,
|
||||
}
|
||||
response = requests.get(
|
||||
f"{BASE_URL}/v2/address/walletinfo/tokens",
|
||||
headers=make_headers(),
|
||||
params=params,
|
||||
)
|
||||
return handle_response(response)
|
||||
|
||||
|
||||
def get_smart_wallets(
|
||||
chain: str, sort: str = "total_profit", sort_dir: str = "desc"
|
||||
) -> ApiResponse:
|
||||
params = {"chain": chain, "sort": sort, "sort_dir": sort_dir}
|
||||
response = requests.get(
|
||||
f"{BASE_URL}/v2/address/smart_wallet/list",
|
||||
headers=make_headers(),
|
||||
params=params,
|
||||
)
|
||||
return handle_response(response)
|
||||
|
||||
|
||||
def format_json(data):
|
||||
return json.dumps(data, indent=2, ensure_ascii=False)
|
||||
|
||||
|
||||
def print_cu_warning(operation: str):
|
||||
cu_cost = CU_COSTS.get(operation, "unknown")
|
||||
print(f"[CU Cost: {cu_cost}]", file=sys.stderr)
|
||||
|
||||
|
||||
def cmd_search(args):
|
||||
result = search_tokens(args.keyword, args.chain, args.limit)
|
||||
if result.error:
|
||||
print(f"Error: {result.error}")
|
||||
sys.exit(1)
|
||||
print_cu_warning("search")
|
||||
print(format_json(result.data))
|
||||
|
||||
|
||||
def cmd_price(args):
|
||||
token_ids = [t.strip() for t in args.token_ids.split(",")]
|
||||
result = get_token_price(token_ids)
|
||||
if result.error:
|
||||
print(f"Error: {result.error}")
|
||||
sys.exit(1)
|
||||
print_cu_warning("price")
|
||||
print(format_json(result.data))
|
||||
|
||||
|
||||
def cmd_trending(args):
|
||||
result = get_trending_tokens(args.chain, args.page, args.page_size)
|
||||
if result.error:
|
||||
print(f"Error: {result.error}")
|
||||
sys.exit(1)
|
||||
print_cu_warning("trending")
|
||||
print(format_json(result.data))
|
||||
|
||||
|
||||
def cmd_token(args):
|
||||
result = get_token_detail(args.token_id)
|
||||
if result.error:
|
||||
print(f"Error: {result.error}")
|
||||
sys.exit(1)
|
||||
print_cu_warning("token")
|
||||
print(format_json(result.data))
|
||||
|
||||
|
||||
def cmd_risk(args):
|
||||
result = get_token_risk(args.token_id)
|
||||
if result.error:
|
||||
print(f"Error: {result.error}")
|
||||
sys.exit(1)
|
||||
print_cu_warning("risk")
|
||||
print(format_json(result.data))
|
||||
|
||||
|
||||
def cmd_holders(args):
|
||||
result = get_top_holders(args.token_id, args.limit)
|
||||
if result.error:
|
||||
print(f"Error: {result.error}")
|
||||
sys.exit(1)
|
||||
print_cu_warning("holders")
|
||||
print(format_json(result.data))
|
||||
|
||||
|
||||
def cmd_klines(args):
|
||||
if args.pair_id:
|
||||
result = get_klines_by_pair(args.pair_id, args.interval, args.limit)
|
||||
print_cu_warning("klines_pair")
|
||||
else:
|
||||
result = get_klines_by_token(args.token_id, args.interval, args.limit)
|
||||
print_cu_warning("klines_token")
|
||||
if result.error:
|
||||
print(f"Error: {result.error}")
|
||||
sys.exit(1)
|
||||
print(format_json(result.data))
|
||||
|
||||
|
||||
def cmd_pairs(args):
|
||||
result = get_pair_detail(args.pair_id)
|
||||
if result.error:
|
||||
print(f"Error: {result.error}")
|
||||
sys.exit(1)
|
||||
print_cu_warning("pairs")
|
||||
print(format_json(result.data))
|
||||
|
||||
|
||||
def cmd_wallet_pnl(args):
|
||||
result = get_wallet_pnl(
|
||||
args.wallet, args.chain, args.token, args.from_time, args.to_time
|
||||
)
|
||||
if result.error:
|
||||
print(f"Error: {result.error}")
|
||||
sys.exit(1)
|
||||
print_cu_warning("wallet_pnl")
|
||||
print(format_json(result.data))
|
||||
|
||||
|
||||
def cmd_wallet_info(args):
|
||||
result = get_wallet_info(args.wallet, args.chain)
|
||||
if result.error:
|
||||
print(f"Error: {result.error}")
|
||||
sys.exit(1)
|
||||
print_cu_warning("wallet_info")
|
||||
print(format_json(result.data))
|
||||
|
||||
|
||||
def cmd_wallet_tokens(args):
|
||||
result = get_wallet_tokens(
|
||||
args.wallet,
|
||||
args.chain,
|
||||
args.sort,
|
||||
args.sort_dir,
|
||||
args.hide_sold,
|
||||
args.hide_small,
|
||||
)
|
||||
if result.error:
|
||||
print(f"Error: {result.error}")
|
||||
sys.exit(1)
|
||||
print_cu_warning("wallet_tokens")
|
||||
print(format_json(result.data))
|
||||
|
||||
|
||||
def cmd_smart_wallets(args):
|
||||
result = get_smart_wallets(args.chain, args.sort, args.sort_dir)
|
||||
if result.error:
|
||||
print(f"Error: {result.error}")
|
||||
sys.exit(1)
|
||||
print_cu_warning("smart_wallet")
|
||||
print(format_json(result.data))
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="AVE Cloud Data REST API")
|
||||
parser.add_argument(
|
||||
"--api-key", default=API_KEY, help="AVE API key (or set AVE_API_KEY env)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--api-plan",
|
||||
default=API_PLAN,
|
||||
choices=["free", "normal", "pro"],
|
||||
help="API plan",
|
||||
)
|
||||
subparsers = parser.add_subparsers(dest="command", help="Commands")
|
||||
|
||||
p_search = subparsers.add_parser("search", help="Search tokens by keyword")
|
||||
p_search.add_argument("--keyword", required=True, help="Token symbol or address")
|
||||
p_search.add_argument("--chain", help="Chain name (bsc, solana, eth, base)")
|
||||
p_search.add_argument(
|
||||
"--limit", type=int, default=100, help="Number of results (max 300)"
|
||||
)
|
||||
|
||||
p_price = subparsers.add_parser("price", help="Get batch token prices")
|
||||
p_price.add_argument(
|
||||
"--token-ids",
|
||||
required=True,
|
||||
help="Comma-separated token IDs (e.g. PEPE-bsc,TRUMP-bsc)",
|
||||
)
|
||||
|
||||
p_trending = subparsers.add_parser("trending", help="Get trending tokens")
|
||||
p_trending.add_argument("--chain", required=True, help="Chain name")
|
||||
p_trending.add_argument("--page", type=int, default=0, help="Page number")
|
||||
p_trending.add_argument(
|
||||
"--page-size", type=int, default=50, help="Results per page (max 100)"
|
||||
)
|
||||
|
||||
p_token = subparsers.add_parser("token", help="Get token details and top pairs")
|
||||
p_token.add_argument(
|
||||
"--token-id", required=True, help="Token ID (address-chain format)"
|
||||
)
|
||||
|
||||
p_risk = subparsers.add_parser("risk", help="Get token risk information")
|
||||
p_risk.add_argument(
|
||||
"--token-id", required=True, help="Token ID (address-chain format)"
|
||||
)
|
||||
|
||||
p_holders = subparsers.add_parser("holders", help="Get top 100 token holders")
|
||||
p_holders.add_argument(
|
||||
"--token-id", required=True, help="Token ID (address-chain format)"
|
||||
)
|
||||
p_holders.add_argument(
|
||||
"--limit", type=int, default=100, help="Number of holders (max 100)"
|
||||
)
|
||||
|
||||
p_klines = subparsers.add_parser("klines", help="Get kline/candlestick data")
|
||||
p_klines.add_argument("--token-id", help="Token ID (use OR --pair-id)")
|
||||
p_klines.add_argument("--pair-id", help="Pair ID (use OR --token-id)")
|
||||
p_klines.add_argument(
|
||||
"--interval", default="1h", help="Interval (1m, 5m, 15m, 30m, 1h, 4h, 1d)"
|
||||
)
|
||||
p_klines.add_argument("--limit", type=int, default=100, help="Number of klines")
|
||||
|
||||
p_pairs = subparsers.add_parser("pairs", help="Get pair details")
|
||||
p_pairs.add_argument(
|
||||
"--pair-id", required=True, help="Pair ID (address-chain format)"
|
||||
)
|
||||
|
||||
p_wallet_pnl = subparsers.add_parser(
|
||||
"wallet-pnl", help="Get wallet PnL for a token"
|
||||
)
|
||||
p_wallet_pnl.add_argument("--wallet", required=True, help="Wallet address")
|
||||
p_wallet_pnl.add_argument("--chain", required=True, help="Chain name")
|
||||
p_wallet_pnl.add_argument("--token", required=True, help="Token address")
|
||||
p_wallet_pnl.add_argument(
|
||||
"--from-time", type=int, help="Unix epoch seconds (earliest 15 days ago)"
|
||||
)
|
||||
p_wallet_pnl.add_argument("--to-time", type=int, help="Unix epoch seconds")
|
||||
|
||||
p_wallet_info = subparsers.add_parser(
|
||||
"wallet-info", help="Get wallet info (all tokens on chain)"
|
||||
)
|
||||
p_wallet_info.add_argument("--wallet", required=True, help="Wallet address")
|
||||
p_wallet_info.add_argument("--chain", required=True, help="Chain name")
|
||||
|
||||
p_wallet_tokens = subparsers.add_parser(
|
||||
"wallet-tokens", help="Get all tokens holding in wallet"
|
||||
)
|
||||
p_wallet_tokens.add_argument("--wallet", required=True, help="Wallet address")
|
||||
p_wallet_tokens.add_argument("--chain", required=True, help="Chain name")
|
||||
p_wallet_tokens.add_argument("--sort", default="last_txn_time", help="Sort field")
|
||||
p_wallet_tokens.add_argument(
|
||||
"--sort-dir", default="desc", help="Sort direction (desc/asc)"
|
||||
)
|
||||
p_wallet_tokens.add_argument(
|
||||
"--hide-sold", type=int, default=0, help="Hide sold tokens (0/1)"
|
||||
)
|
||||
p_wallet_tokens.add_argument(
|
||||
"--hide-small", type=int, default=0, help="Hide small balances (0/1)"
|
||||
)
|
||||
|
||||
p_smart = subparsers.add_parser(
|
||||
"smart-wallets", help="Get smart wallet list (for copy trading)"
|
||||
)
|
||||
p_smart.add_argument("--chain", required=True, help="Chain name")
|
||||
p_smart.add_argument("--sort", default="total_profit", help="Sort field")
|
||||
p_smart.add_argument("--sort-dir", default="desc", help="Sort direction (desc/asc)")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.api_key:
|
||||
print("Error: API key required. Set AVE_API_KEY env or use --api-key")
|
||||
sys.exit(1)
|
||||
|
||||
if not args.command:
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
cmd_map = {
|
||||
"search": cmd_search,
|
||||
"price": cmd_price,
|
||||
"trending": cmd_trending,
|
||||
"token": cmd_token,
|
||||
"risk": cmd_risk,
|
||||
"holders": cmd_holders,
|
||||
"klines": cmd_klines,
|
||||
"pairs": cmd_pairs,
|
||||
"wallet-pnl": cmd_wallet_pnl,
|
||||
"wallet-info": cmd_wallet_info,
|
||||
"wallet-tokens": cmd_wallet_tokens,
|
||||
"smart-wallets": cmd_smart_wallets,
|
||||
}
|
||||
|
||||
cmd_map[args.command](args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user