signal-engine / gateio_client.py
josephrw's picture
Upload folder using huggingface_hub
51f3427 verified
Raw
History Blame Contribute Delete
4.98 kB
"""
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