TradingAgents/tradingagents/dataflows/ccxt_vendor.py

315 lines
9.7 KiB
Python

"""
CCXT Crypto Data Vendor - Multi-exchange cryptocurrency market data
Supports: Binance, Coinbase, Kraken, and 100+ other exchanges
"""
import ccxt
import pandas as pd
from datetime import datetime, timedelta
from typing import Optional, Dict, List, Any
import os
class CCXTVendor:
"""Wrapper for CCXT library to fetch crypto market data from multiple exchanges."""
def __init__(self, exchange_id: str = "binance", api_key: str = None, api_secret: str = None):
"""
Initialize CCXT exchange connection.
Args:
exchange_id: Exchange name (binance, coinbase, kraken, etc.)
api_key: API key for authenticated endpoints (optional)
api_secret: API secret for authenticated endpoints (optional)
"""
self.exchange_id = exchange_id
# Get API credentials from environment if not provided
if api_key is None:
api_key = os.getenv(f"{exchange_id.upper()}_API_KEY", "")
if api_secret is None:
api_secret = os.getenv(f"{exchange_id.upper()}_API_SECRET", "")
# Initialize exchange
exchange_class = getattr(ccxt, exchange_id)
self.exchange = exchange_class({
'apiKey': api_key,
'secret': api_secret,
'enableRateLimit': True, # Respect rate limits
'options': {
'defaultType': 'spot', # Default to spot markets
}
})
# Load markets
self.exchange.load_markets()
def get_ohlcv(
self,
symbol: str,
timeframe: str = '1d',
since: Optional[str] = None,
limit: int = 100
) -> pd.DataFrame:
"""
Fetch OHLCV (Open, High, Low, Close, Volume) data.
Args:
symbol: Trading pair (e.g., 'BTC/USDT', 'ETH/USD')
timeframe: Candle timeframe ('1m', '5m', '15m', '1h', '4h', '1d', '1w')
since: Start date (ISO format or timestamp)
limit: Number of candles to fetch
Returns:
DataFrame with columns: timestamp, open, high, low, close, volume
"""
# Convert since to timestamp if provided
since_ts = None
if since:
if isinstance(since, str):
since_dt = pd.to_datetime(since)
since_ts = int(since_dt.timestamp() * 1000)
else:
since_ts = since
# Fetch OHLCV data
ohlcv = self.exchange.fetch_ohlcv(
symbol=symbol,
timeframe=timeframe,
since=since_ts,
limit=limit
)
# Convert to DataFrame
df = pd.DataFrame(
ohlcv,
columns=['timestamp', 'open', 'high', 'low', 'close', 'volume']
)
# Convert timestamp to datetime
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
df.set_index('timestamp', inplace=True)
return df
def get_ticker(self, symbol: str) -> Dict[str, Any]:
"""
Fetch current ticker information (24h stats).
Args:
symbol: Trading pair (e.g., 'BTC/USDT')
Returns:
Dictionary with current price, volume, changes, etc.
"""
ticker = self.exchange.fetch_ticker(symbol)
return ticker
def get_order_book(self, symbol: str, limit: int = 20) -> Dict[str, Any]:
"""
Fetch order book (bids and asks).
Args:
symbol: Trading pair
limit: Depth of order book
Returns:
Dictionary with 'bids' and 'asks' arrays
"""
order_book = self.exchange.fetch_order_book(symbol, limit=limit)
return order_book
def get_trades(self, symbol: str, since: Optional[str] = None, limit: int = 100) -> pd.DataFrame:
"""
Fetch recent trades.
Args:
symbol: Trading pair
since: Start time (ISO format or timestamp)
limit: Number of trades
Returns:
DataFrame with trade history
"""
# Convert since to timestamp if provided
since_ts = None
if since:
if isinstance(since, str):
since_dt = pd.to_datetime(since)
since_ts = int(since_dt.timestamp() * 1000)
else:
since_ts = since
trades = self.exchange.fetch_trades(symbol, since=since_ts, limit=limit)
df = pd.DataFrame(trades)
if 'timestamp' in df.columns:
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
return df
def get_markets(self) -> List[str]:
"""
Get list of available trading pairs.
Returns:
List of trading pair symbols
"""
return list(self.exchange.markets.keys())
def get_balance(self) -> Dict[str, Any]:
"""
Fetch account balance (requires API key).
Returns:
Dictionary with account balances
"""
balance = self.exchange.fetch_balance()
return balance
# Convenience functions for integration with existing dataflow interface
def get_crypto_ohlcv(
symbol: str,
timeframe: str = '1d',
since: Optional[str] = None,
limit: int = 100,
exchange: str = "binance"
) -> str:
"""
Get crypto OHLCV data formatted as string.
Args:
symbol: Trading pair (e.g., 'BTC/USDT')
timeframe: Candle timeframe
since: Start date
limit: Number of candles
exchange: Exchange name
Returns:
Formatted string with OHLCV data
"""
vendor = CCXTVendor(exchange_id=exchange)
df = vendor.get_ohlcv(symbol, timeframe, since, limit)
# Format as string for LLM consumption
result = f"OHLCV Data for {symbol} on {exchange} ({timeframe} timeframe):\n\n"
result += df.to_string()
result += f"\n\nLatest Price: ${df['close'].iloc[-1]:.2f}"
result += f"\n24h Change: {((df['close'].iloc[-1] / df['close'].iloc[-2] - 1) * 100):.2f}%"
result += f"\n24h High: ${df['high'].iloc[-1]:.2f}"
result += f"\n24h Low: ${df['low'].iloc[-1]:.2f}"
result += f"\n24h Volume: {df['volume'].iloc[-1]:,.0f}"
return result
def get_crypto_ticker(symbol: str, exchange: str = "binance") -> str:
"""
Get current crypto ticker information.
Args:
symbol: Trading pair
exchange: Exchange name
Returns:
Formatted string with ticker data
"""
vendor = CCXTVendor(exchange_id=exchange)
ticker = vendor.get_ticker(symbol)
result = f"Ticker for {symbol} on {exchange}:\n\n"
result += f"Last Price: ${ticker.get('last', 0):.2f}\n"
result += f"Bid: ${ticker.get('bid', 0):.2f}\n"
result += f"Ask: ${ticker.get('ask', 0):.2f}\n"
result += f"24h High: ${ticker.get('high', 0):.2f}\n"
result += f"24h Low: ${ticker.get('low', 0):.2f}\n"
result += f"24h Volume: {ticker.get('quoteVolume', 0):,.0f}\n"
result += f"24h Change: {ticker.get('percentage', 0):.2f}%\n"
return result
def get_crypto_order_book(symbol: str, limit: int = 20, exchange: str = "binance") -> str:
"""
Get order book depth.
Args:
symbol: Trading pair
limit: Order book depth
exchange: Exchange name
Returns:
Formatted string with order book data
"""
vendor = CCXTVendor(exchange_id=exchange)
order_book = vendor.get_order_book(symbol, limit)
result = f"Order Book for {symbol} on {exchange} (Top {limit}):\n\n"
result += "ASKS (Sell Orders):\n"
for price, amount in order_book['asks'][:10]:
result += f" ${price:.2f} - {amount:.4f}\n"
result += "\nBIDS (Buy Orders):\n"
for price, amount in order_book['bids'][:10]:
result += f" ${price:.2f} - {amount:.4f}\n"
# Calculate spread
if order_book['bids'] and order_book['asks']:
best_bid = order_book['bids'][0][0]
best_ask = order_book['asks'][0][0]
spread = best_ask - best_bid
spread_pct = (spread / best_bid) * 100
result += f"\nSpread: ${spread:.2f} ({spread_pct:.3f}%)"
return result
def get_crypto_fundamentals(symbol: str, exchange: str = "binance") -> str:
"""
Get crypto fundamental metrics (volume, liquidity, market data).
Note: For true on-chain fundamentals, use Glassnode or Messari.
This provides exchange-level trading fundamentals.
Args:
symbol: Trading pair
exchange: Exchange name
Returns:
Formatted string with fundamental data
"""
vendor = CCXTVendor(exchange_id=exchange)
# Get ticker for current stats
ticker = vendor.get_ticker(symbol)
# Get order book for liquidity analysis
order_book = vendor.get_order_book(symbol, limit=100)
# Calculate liquidity metrics
bid_liquidity = sum(price * amount for price, amount in order_book['bids'][:20])
ask_liquidity = sum(price * amount for price, amount in order_book['asks'][:20])
total_liquidity = bid_liquidity + ask_liquidity
result = f"Fundamental Metrics for {symbol} on {exchange}:\n\n"
result += f"Market Price: ${ticker.get('last', 0):.2f}\n"
result += f"24h Volume (USD): ${ticker.get('quoteVolume', 0):,.0f}\n"
result += f"24h Trades: {ticker.get('info', {}).get('count', 'N/A')}\n"
result += f"Bid Liquidity (Top 20): ${bid_liquidity:,.0f}\n"
result += f"Ask Liquidity (Top 20): ${ask_liquidity:,.0f}\n"
result += f"Total Liquidity: ${total_liquidity:,.0f}\n"
result += f"Bid/Ask Ratio: {bid_liquidity/ask_liquidity if ask_liquidity > 0 else 0:.2f}\n"
# Calculate volatility from recent data
try:
df = vendor.get_ohlcv(symbol, timeframe='1h', limit=24)
returns = df['close'].pct_change().dropna()
volatility = returns.std() * 100
result += f"1-Day Volatility: {volatility:.2f}%\n"
except:
pass
return result