#!/usr/bin/env python3 """ Crypto Price Fetcher Pulls historical OHLCV data from Binance public API (no key needed). """ import json import time import urllib.request from datetime import datetime, timezone # Binance intl is geo-blocked from US; use Binance US BINANCE_KLINES = "https://api.binance.us/api/v3/klines" BINANCE_TICKER = "https://api.binance.us/api/v3/ticker/price" def get_price_at_time(symbol, timestamp_ms, interval='1m'): """Get the candle at a specific timestamp.""" url = f"{BINANCE_KLINES}?symbol={symbol}&interval={interval}&startTime={timestamp_ms}&limit=1" req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'}) try: resp = urllib.request.urlopen(req, timeout=10) data = json.loads(resp.read()) if data: return { 'open_time': data[0][0], 'open': float(data[0][1]), 'high': float(data[0][2]), 'low': float(data[0][3]), 'close': float(data[0][4]), 'volume': float(data[0][5]), } except Exception as e: print(f"Error fetching {symbol}: {e}") return None def get_klines(symbol, interval='1h', start_time_ms=None, end_time_ms=None, limit=1000): """Get historical klines/candlestick data.""" params = f"symbol={symbol}&interval={interval}&limit={limit}" if start_time_ms: params += f"&startTime={start_time_ms}" if end_time_ms: params += f"&endTime={end_time_ms}" url = f"{BINANCE_KLINES}?{params}" req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'}) try: resp = urllib.request.urlopen(req, timeout=15) raw = json.loads(resp.read()) return [{ 'open_time': k[0], 'open': float(k[1]), 'high': float(k[2]), 'low': float(k[3]), 'close': float(k[4]), 'volume': float(k[5]), 'close_time': k[6], } for k in raw] except Exception as e: print(f"Error fetching klines for {symbol}: {e}") return [] def get_all_klines(symbol, interval, start_time_ms, end_time_ms): """Paginate through all klines between two timestamps.""" all_klines = [] current_start = start_time_ms while current_start < end_time_ms: batch = get_klines(symbol, interval, current_start, end_time_ms) if not batch: break all_klines.extend(batch) current_start = batch[-1]['close_time'] + 1 time.sleep(0.1) # Rate limiting return all_klines def get_current_price(symbol): """Get current price.""" url = f"{BINANCE_TICKER}?symbol={symbol}" req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'}) try: resp = urllib.request.urlopen(req, timeout=10) data = json.loads(resp.read()) return float(data['price']) except Exception as e: print(f"Error fetching current price for {symbol}: {e}") return None def normalize_symbol(ticker): """Convert signal ticker to Binance symbol format.""" # Remove USDT suffix if present, then add it back ticker = ticker.upper().replace('USDT', '').replace('/', '') return f"{ticker}USDT" def datetime_to_ms(dt_str): """Convert datetime string to milliseconds timestamp.""" # Handle various formats for fmt in ['%Y-%m-%dT%H:%M:%S', '%Y-%m-%d %H:%M:%S', '%Y-%m-%d']: try: dt = datetime.strptime(dt_str, fmt).replace(tzinfo=timezone.utc) return int(dt.timestamp() * 1000) except ValueError: continue return None if __name__ == '__main__': # Test with current signals for ticker in ['ASTERUSDT', 'HYPEUSDT']: symbol = normalize_symbol(ticker) price = get_current_price(symbol) print(f"{symbol}: ${price}") # Get last 24h of 1h candles now_ms = int(time.time() * 1000) day_ago = now_ms - (24 * 60 * 60 * 1000) klines = get_klines(symbol, '1h', day_ago, now_ms) if klines: highs = [k['high'] for k in klines] lows = [k['low'] for k in klines] print(f" 24h range: ${min(lows):.4f} - ${max(highs):.4f}") print(f" Candles: {len(klines)}") print()