import asyncio import os from datetime import datetime import httpx BIRDEYE_API_KEY = os.getenv("BIRDEYE_API_KEY", "") BASE_URL = "https://public-api.birdeye.so" HEADERS = {"X-API-KEY": BIRDEYE_API_KEY, "accept": "application/json"} class BirdeyeClient: def __init__(self): self.headers = HEADERS self.client = httpx.AsyncClient(timeout=30.0) self.last_call = 0 async def _call(self, endpoint: str, params: dict | None = None) -> dict: import time now = time.time() wait = 0.6 - (now - self.last_call) if wait > 0: await asyncio.sleep(wait) self.last_call = time.time() try: r = await self.client.get(f"{BASE_URL}{endpoint}", headers=self.headers, params=params or {}) return r.json() if r.status_code == 200 else {"error": f"HTTP {r.status_code}"} except Exception as e: return {"error": str(e)} async def get_price(self, address: str) -> dict: return await self._call("/defi/price", {"address": address}) async def get_token_overview(self, address: str) -> dict: return await self._call("/defi/token_overview", {"address": address}) async def get_new_listings(self, limit: int = 20) -> list: r = await self._call("/defi/v2/tokens/new_listing", {"limit": limit, "offset": 0}) return r.get("data", {}).get("items", []) if isinstance(r, dict) else [] async def security_scan(self, address: str) -> dict: """Derived security analysis using ALL Birdeye market data""" overview = await self.get_token_overview(address) await asyncio.sleep(0.6) d = overview.get("data", {}) if isinstance(overview, dict) else {} if not d: return {"address": address, "error": "No data", "risk_score": -1} score = 0 flags = [] signals = [] # 1. LIQUIDITY HEALTH (0-25 pts) mcap = d.get("marketCap", 0) or 0 liq = d.get("liquidity", 0) or 0 if mcap > 0 and liq > 0: ratio = liq / mcap if ratio < 0.05: score += 25 flags.append("CRITICAL: Liquidity/MCap < 5% — easy manipulation") elif ratio < 0.15: score += 15 flags.append("WARNING: Low liquidity ratio") elif ratio > 0.5: signals.append("Strong liquidity backing") else: signals.append("Normal liquidity levels") # 2. PRICE VOLATILITY (0-20 pts) changes = [abs(d.get(f"priceChange{t}Percent") or 0) for t in ["1m", "5m", "30m"]] avg_chg = sum(changes) / max(len(changes), 1) if avg_chg > 20: score += 20 flags.append("EXTREME volatility — pump/dump in progress") elif avg_chg > 5: score += 10 flags.append("High volatility — watch for manipulation") elif avg_chg < 1: signals.append("Stable price action") # 3. HOLDER HEALTH (0-20 pts) holders = d.get("holder", 0) or 0 if holders < 20: score += 20 flags.append(f"Very few holders ({holders}) — high concentration") elif holders < 100: score += 10 flags.append(f"Low holder count ({holders})") elif holders > 500: signals.append(f"Healthy holder base ({holders:,}) wallets") # Check wallet change for suspicious activity uw_change = d.get("uniqueWallet30mChangePercent", 0) or 0 if uw_change > 50: flags.append(f"Suspicious +{uw_change:.0f}% wallet growth in 30m — possible bots") elif uw_change > 20: flags.append(f"Rapid wallet growth +{uw_change:.0f}%") # 4. TRADE ACTIVITY (0-15 pts) last_trade = d.get("lastTradeUnixTime", 0) or 0 if last_trade > 0: mins = (datetime.utcnow().timestamp() - last_trade) / 60 if mins > 60: score += 15 flags.append(f"No trades for {int(mins)} min — possible dead token") elif mins > 30: score += 5 flags.append(f"Low activity — last trade {int(mins)} min ago") else: signals.append("Active trading") # 5. METADATA QUALITY (0-10 pts) ext = d.get("extensions", {}) has_web = bool(ext.get("website")) has_social = bool(ext.get("twitter") or ext.get("discord")) has_desc = bool(ext.get("description")) if not has_web and not has_social: score += 10 flags.append("No website or socials — anonymous project") elif not has_web: score += 5 flags.append("No website — transparency concern") elif has_desc: signals.append("Complete metadata — transparent project") # 6. VOLUME/MARKET CAP RATIO (0-10 pts) — wash trading detection v24h = d.get("v24hUSD", 0) or 0 if mcap > 0 and v24h > 0: v_ratio = v24h / mcap if v_ratio > 5: score += 10 flags.append(f"Volume {v_ratio:.1f}x MarketCap — WASH TRADING likely") elif v_ratio > 2: score += 5 flags.append(f"Volume {v_ratio:.1f}x MarketCap — possible wash trading") elif v_ratio > 0.1: signals.append("Healthy volume/market cap ratio") # 7. BUY/SELL RATIO ANALYSIS (bonus signal) buy24h = d.get("buy24h", 0) or 0 sell24h = d.get("sell24h", 0) or 0 if buy24h > 0 and sell24h > 0: if sell24h > buy24h * 2: flags.append("Heavy sell pressure — 2x more sells than buys") elif buy24h > sell24h * 1.5: signals.append("Buy pressure dominant — bullish signal") # VERDICT if score >= 60: verdict = "HIGH RISK" elif score >= 35: verdict = "MEDIUM RISK" elif score >= 15: verdict = "LOW-MEDIUM RISK" else: verdict = "LOW RISK" return { "address": address, "token_name": d.get("name", "Unknown"), "symbol": d.get("symbol", "???"), "risk_score": min(score, 100), "risk_level": verdict, "price": d.get("price", 0), "market_cap": mcap, "liquidity": liq, "fdv": d.get("fdv", 0), "holders": holders, "number_markets": d.get("numberMarkets", 0), "volume_24h": v24h, "buy_24h": buy24h, "sell_24h": sell24h, "price_change_24h": d.get("priceChange24hPercent", 0), "wallet_growth_30m": uw_change, "last_trade": d.get("lastTradeHumanTime", ""), "flags": flags, "positive_signals": signals, "metadata": {"website": has_web, "socials": has_social, "description": has_desc}, "analyzed_at": datetime.utcnow().isoformat(), "birdeye_powered": True, } async def new_token_radar(self, limit: int = 20, min_liquidity: float = 1000) -> dict: tokens = await self.get_new_listings(limit) scored = [] for t in tokens: liq = t.get("liquidity", 0) or 0 if liq < min_liquidity: continue score = 0 reasons = [] if liq > 10000: score += 25 reasons.append("Good liquidity") uw = t.get("uniqueWallet30m", 0) or 0 if uw > 50: score += min(uw * 0.2, 20) reasons.append(f"{uw} recent wallets") score += min(t.get("trade24h", 0) or 0 * 0.01, 10) scored.append({**t, "opportunity_score": min(score, 50), "score_reasons": reasons}) return { "tokens": sorted(scored, key=lambda x: x.get("opportunity_score", 0), reverse=True), "count": len(scored), } # ── Wallet Intelligence ────────────────────────────────────────────────── async def get_wallet_networth(self, wallet: str) -> dict: """Get wallet net worth in USD and token breakdown.""" return await self._call("/v1/wallet/networth", {"wallet": wallet}) async def get_wallet_pnl(self, wallet: str, timeframe: str = "7d") -> dict: """Get wallet profit/loss for a given timeframe.""" return await self._call("/v1/wallet/pnl", {"wallet": wallet, "time_frame": timeframe}) async def get_wallet_smart_money_status(self, wallet: str) -> dict: """Check if wallet is tagged as smart money.""" return await self._call("/v1/wallet/smart_money", {"wallet": wallet}) async def close(self): await self.client.aclose()