""" Gate.io Public API Integration Fetch candle data for USDT perpetual futures """ import aiohttp import pandas as pd from typing import List, Dict, Optional from datetime import datetime, timedelta import logging logger = logging.getLogger(__name__) class GateIOClient: """Gate.io public API client for candle data""" def __init__(self, base_url: str = "https://api.gateio.ws/api/v4"): self.base_url = base_url self.session = None async def __aenter__(self): self.session = aiohttp.ClientSession() return self async def __aexit__(self, exc_type, exc_val, exc_tb): if self.session: await self.session.close() async def get_usdt_perpetuals(self) -> List[Dict]: """Get list of USDT perpetual futures""" url = f"{self.base_url}/futures/usdt/contracts" async with self.session.get(url) as response: data = await response.json() # Filter for USDT-settled perpetuals perpetuals = [ { 'symbol': c['name'], 'name': c.get('name', ''), 'quanto_multiplier': c.get('quanto_multiplier', 1), 'tick_size': c.get('order_price_round', 1e-8), 'position_size': c.get('position_size', 1) } for c in data if c.get('name', '').endswith('_USDT') and c.get('quanto_multiplier') is not None ] return perpetuals async def get_candles( self, symbol: str, interval: str = '1h', limit: int = 1000, from_: Optional[int] = None, to: Optional[int] = None ) -> List[Dict]: """Get candle data for a symbol""" url = f"{self.base_url}/futures/usdt/candlesticks" params = { 'contract': symbol, 'interval': interval, 'limit': limit } if from_: params['from'] = from_ if to: params['to'] = to async with self.session.get(url, params=params) as response: data = await response.json() # Gate.io returns data in reverse chronological order candles = [ { 'timestamp': datetime.fromtimestamp(int(c[0])), 'volume': float(c[1]), 'close': float(c[2]), 'high': float(c[3]), 'low': float(c[4]), 'open': float(c[5]), 'quote_volume': float(c[1]) * float(c[2]) # Approximate } for c in reversed(data) ] return candles async def get_ticker(self, symbol: str) -> Dict: """Get current ticker data""" url = f"{self.base_url}/futures/usdt/ticker" params = {'contract': symbol} async with self.session.get(url, params=params) as response: data = await response.json() if isinstance(data, list) and len(data) > 0: return { 'symbol': symbol, 'last_price': float(data[0].get('last', 0)), 'volume_24h': float(data[0].get('volume_24h', 0)), 'quote_volume_24h': float(data[0].get('volume_24h_quote', 0)), 'high_24h': float(data[0].get('high_24h', 0)), 'low_24h': float(data[0].get('low_24h', 0)) } return {} async def filter_symbols( self, min_price: float = 0.10, min_volume_24h: float = 50000.0 ) -> List[Dict]: """Filter symbols by price and volume criteria""" perpetuals = await self.get_usdt_perpetuals() filtered = [] for contract in perpetuals: symbol = contract['symbol'] ticker = await self.get_ticker(symbol) if ticker: last_price = ticker.get('last_price', 0) volume_24h = ticker.get('volume_24h', 0) if last_price > 0 and last_price < min_price and volume_24h >= min_volume_24h: filtered.append({ **contract, **ticker }) return filtered async def backfill_candles( self, symbol: str, days: int = 30, interval: str = '1h' ) -> pd.DataFrame: """Backfill historical candle data""" to_time = int(datetime.now().timestamp()) from_time = int((datetime.now() - timedelta(days=days)).timestamp()) candles = await self.get_candles( symbol, interval=interval, from_=from_time, to=to_time ) df = pd.DataFrame(candles) if not df.empty: df = df.sort_values('timestamp') return df