import requests import time from decimal import Decimal, ROUND_HALF_UP # --- Global Cache --- # CACHE = {} CACHE_DURATION = 120 # TTL set to 2 minutes # Headers to mimic browser behavior and avoid basic scraping blocks STEALTH_HEADERS = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8", "Connection": "keep-alive", } # --- Helper --- # def format_compact(num): """Converts large numbers into human-readable strings (e.g., 1.5M, 2B).""" if num is None or num == 0: return "0" for unit in ['', 'K', 'M', 'B', 'T']: if abs(num) < 1000.0: return f"{num:,.2f}{unit}".replace(".00", "") num /= 1000.0 return f"{num:,.2f}P" def calculate_deep_dive(coin_id: str, user_keys: dict): """Fetches market data from CoinGecko, calculates ratios, and returns a structured payload.""" coin_id = coin_id.strip().lower() # --- Cache Check --- # now = time.time() global CACHE CACHE = {k: v for k, v in CACHE.items() if now < v['expires']} if coin_id in CACHE: print(f" ⚡ Serving {coin_id} from cache") return CACHE[coin_id]['data'] # --- API Request Setup --- # cg_key = str(user_keys.get("COINGECKO_API_KEY", "")).strip() base_url = "https://api.coingecko.com/api/v3" headers = STEALTH_HEADERS.copy() # Handle both Demo and Pro API key formats if cg_key and cg_key != "CONFIG_REQUIRED_CG": headers["x-cg-demo-api-key" if cg_key.startswith("CG-") else "x-cg-pro-api-key"] = cg_key try: # Request only necessary fields to reduce payload size and latency url = (f"{base_url}/coins/{coin_id}?" "localization=false&" "tickers=false&" "market_data=true&" "community_data=false&" "developer_data=false&" "sparkline=false&" "price_change_percentage=1h") r = requests.get(url, headers=headers, timeout=15) if r.status_code == 429: return {"status": "error", "message": "Rate Limit Hit. Please wait."} if r.status_code != 200: return {"status": "error", "message": f"API {r.status_code}"} res = r.json() mkt = res.get('market_data', {}) symbol = res.get('symbol', '').upper() # --- Logic Extraction --- mcap = mkt.get('market_cap', {}).get('usd', 0) or 0 vol = mkt.get('total_volume', {}).get('usd', 0) or 0 p_ch_24h = mkt.get('price_change_percentage_24h', 0) or 0 p_ch_1h = mkt.get('price_change_percentage_1h_in_currency', {}).get('usd', 0) or 0 d_vol = Decimal(str(vol)) d_mcap = Decimal(str(mcap)) # Volume-to-Market-Cap Ratio (VTMR) vtmr_val = (d_vol / d_mcap).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP) if d_mcap > 0 else 0 # Volume-to-Price-Change (VTPC) - Proxy for liquidity absorption vtpc_val = vol / abs(p_ch_24h) if p_ch_24h != 0 else 0 current_price = mkt.get('current_price', {}).get('usd', 0) # --- Link Construction --- # links = { "cg": f"https://www.coingecko.com/en/coins/{coin_id}", "tv": f"https://www.tradingview.com/chart/?symbol={symbol}USDT" } # --- Payload Construction --- # data_payload = { "status": "success", "vitals": { "name": res.get('name', 'Unknown'), "symbol": symbol, "price": f"${current_price:,.8f}" if current_price < 1 else f"${current_price:,.2f}", "mcap": f"${format_compact(mcap)}", "vol24h": f"${format_compact(vol)}" }, "ratios": { "vtmr": f"{vtmr_val}x", "vtpc": f"${format_compact(vtpc_val)}" }, "velocity": { "h1": f"{p_ch_1h:+.2f}%", "h24": f"{p_ch_24h:+.2f}%", "d7": f"{(mkt.get('price_change_percentage_7d') or 0):+.2f}%", "m1": f"{(mkt.get('price_change_percentage_30d') or 0):+.2f}%", "y1": f"{(mkt.get('price_change_percentage_1y') or 0):+.2f}%" }, "supply": { "total": format_compact(mkt.get('total_supply', 0)) }, "links": links } # --- Save to Cache --- # CACHE[coin_id] = {'data': data_payload, 'expires': now + CACHE_DURATION} return data_payload except Exception as e: return {"status": "error", "message": str(e)}