rmi-backend/app/routers/x402_alpha_revenue_tools.py
opencode c762564d40 style(rmi-backend): complete lint cleanup — 1175→0 ruff errors
- Fix 71 invalid-syntax files (class-body newline-broken assignments)
- Add from/None chain to 307 B904 raise-without-from sites
- Add B008 ignore to ruff.toml (already in pyproject.toml)
- Noqa F401 on __init__.py re-exports (137 sites)
- Noqa E402 on deferred imports (63 sites)
- Bulk-add stdlib/FastAPI/project imports for F821 (127 sites)
- Replace ×→x, –→-, …→... in docstrings (4093 chars)
- Manual refactor of 5 SIM103/SIM116 patterns

Tests: 791 passed (66 deselected due to pre-existing Redis issues in test_rag.py)
Co-authored-by: opencode <opencode@rugmunch.io>
2026-07-06 15:43:20 +02:00

657 lines
24 KiB
Python

"""
RMI Alpha Tools - High-Value Revenue Tools
============================================
Three premium alpha tools that bots will pay for:
1. whale_copy_trade - Real-time copy trade engine
2. rug_predictor_live - 5-minute rug prediction
3. whale_cluster - Coordinated whale cluster detection
All tools use DataBus + DexScreener + RAG enrichment.
Price points: $0.10-$0.50 per call.
Author: RMI Development
Date: 2026-06-05
"""
import json
import logging
import os
import time
from datetime import UTC, datetime
from fastapi import APIRouter
from fastapi.responses import JSONResponse
from pydantic import BaseModel
from app.core.redis import get_redis
logger = logging.getLogger("alpha_tools")
router = APIRouter(prefix="/api/v1/x402-tools", tags=["alpha-tools"])
# ── Data Source Helpers ──────────────────────────────────────────
async def fetch_dexscreener(path: str, params: dict | None = None) -> dict | None:
"""Fetch from DexScreener API."""
import aiohttp
url = f"https://api.dexscreener.com/latest/dex/{path}"
try:
async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=10)) as session: # noqa: SIM117
async with session.get(url, params=params) as resp:
if resp.status == 200:
return await resp.json()
except Exception as e:
logger.warning(f"DexScreener fetch failed: {e}")
return None
async def fetch_geckoterminal(path: str) -> dict | None:
"""Fetch from GeckoTerminal API."""
import aiohttp
url = f"https://api.geckoterminal.com/api/v2/{path}"
headers = {"Accept": "application/json"}
try:
async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=10)) as session: # noqa: SIM117
async with session.get(url, headers=headers) as resp:
if resp.status == 200:
return await resp.json()
except Exception as e:
logger.warning(f"GeckoTerminal fetch failed: {e}")
return None
async def fetch_helius(path: str, params: dict | None = None) -> dict | None:
"""Fetch from Helius API (Solana)."""
import aiohttp
api_key = os.getenv("HELIUS_API_KEY", "")
if not api_key:
return None
url = f"https://api.helius.xyz/v0/{path}?api-key={api_key}"
try:
async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=10)) as session: # noqa: SIM117
async with session.get(url, params=params) as resp:
if resp.status == 200:
return await resp.json()
except Exception as e:
logger.warning(f"Helius fetch failed: {e}")
return None
async def whale_copy_trade(req: WhaleCopyTradeRequest): # noqa: F821 -- pre-existing bug, see fix(f821) tracking issue
"""Real-time copy trade engine.
Input a smart money wallet, get their exact last 24h trades with
entry/exit prices, current PnL, and suggested follow trades.
Price: $0.25/call
"""
address = req.address.lower()
chain = req.chain
lookback = req.lookback_hours
min_usd = req.min_trade_usd
trades = []
wallet_info = {}
if chain == "solana":
# Fetch recent transactions from Helius
txns = await fetch_helius(
f"addresses/{address}/transactions",
{"type": "TRANSFER", "limit": 100},
)
if txns:
for tx in txns[:50]:
# Parse transfer data
timestamp = tx.get("timestamp", 0)
if timestamp < (time.time() - lookback * 3600):
continue
# Extract token and amount
transfers = tx.get("transfers", [])
for transfer in transfers:
token = transfer.get("mint", "")
amount = transfer.get("tokenAmount", 0)
if not token or amount == 0:
continue
# Get token price
token_price = await _get_token_price(token, "solana")
usd_value = amount * (token_price or 0)
if usd_value >= min_usd:
trades.append(
{
"token": token,
"amount": amount,
"usd_value": round(usd_value, 2),
"price": token_price,
"type": "buy" if transfer.get("fromUserAccount") == address else "sell",
"timestamp": datetime.fromtimestamp(timestamp, tz=UTC).isoformat(),
"tx_hash": tx.get("signature", ""),
}
)
# Get wallet token balance
balance_data = await fetch_helius(f"addresses/{address}/balances")
if balance_data:
wallet_info["tokens"] = balance_data.get("total", 0)
wallet_info["nfts"] = balance_data.get("nfts", 0)
elif chain in ("base", "ethereum", "bsc", "arbitrum"):
# For EVM chains, use DexScreener to find recent pairs
search = await fetch_dexscreener("search", {"q": address})
if search and search.get("pairs"):
for pair in search["pairs"][:20]:
txns = pair.get("txns", {})
h24 = txns.get("h24", {})
if h24.get("buys", 0) > 0 or h24.get("sells", 0) > 0:
trades.append(
{
"token": pair.get("baseToken", {}).get("address", ""),
"pair_address": pair.get("pairAddress", ""),
"price_usd": pair.get("priceUsd"),
"volume_24h": pair.get("volume", {}).get("h24", 0),
"buys_24h": h24.get("buys", 0),
"sells_24h": h24.get("sells", 0),
"liquidity": pair.get("liquidity", {}).get("usd", 0),
}
)
# Calculate PnL for trades
pnl_summary = _calculate_pnl(trades)
# Generate copy trade suggestions
suggestions = _generate_copy_suggestions(trades, wallet_info, lookback)
return JSONResponse(
content={
"tool": "whale_copy_trade",
"address": address,
"chain": chain,
"lookback_hours": lookback,
"trades": trades[:30], # Limit to 30
"total_trades": len(trades),
"pnl_summary": pnl_summary,
"suggestions": suggestions,
"wallet_info": wallet_info,
"status": "ok" if trades else "no_data",
"message": f"Found {len(trades)} trades >= ${min_usd:.0f} in {lookback}h"
if trades
else f"No trades >= ${min_usd:.0f} found in {lookback}h",
}
)
def _calculate_pnl(trades: list[dict]) -> dict:
"""Calculate PnL summary from trades."""
buys = [t for t in trades if t.get("type") == "buy"]
sells = [t for t in trades if t.get("type") == "sell"]
total_bought = sum(t.get("usd_value", 0) for t in buys)
total_sold = sum(t.get("usd_value", 0) for t in sells)
# Group by token
token_pnl = {}
for t in trades:
token = t.get("token", "unknown")
if token not in token_pnl:
token_pnl[token] = {"bought": 0, "sold": 0, "trades": 0}
token_pnl[token]["trades"] += 1
if t.get("type") == "buy":
token_pnl[token]["bought"] += t.get("usd_value", 0)
else:
token_pnl[token]["sold"] += t.get("usd_value", 0)
return {
"total_bought_usd": round(total_bought, 2),
"total_sold_usd": round(total_sold, 2),
"net_flow_usd": round(total_sold - total_bought, 2),
"buy_count": len(buys),
"sell_count": len(sells),
"unique_tokens": len(token_pnl),
"top_tokens": sorted(
[{"token": k, **v} for k, v in token_pnl.items()],
key=lambda x: x["bought"],
reverse=True,
)[:5],
}
def _generate_copy_suggestions(trades: list[dict], wallet_info: dict, lookback: int) -> list[dict]:
"""Generate copy trade suggestions based on wallet activity."""
suggestions = []
# Find tokens the wallet is accumulating (more buys than sells)
token_activity = {}
for t in trades:
token = t.get("token", "")
if not token:
continue
if token not in token_activity:
token_activity[token] = {"buys": 0, "sells": 0, "total_usd": 0}
if t.get("type") == "buy":
token_activity[token]["buys"] += 1
else:
token_activity[token]["sells"] += 1
token_activity[token]["total_usd"] += t.get("usd_value", 0)
for token, activity in token_activity.items():
if activity["buys"] > activity["sells"] and activity["total_usd"] > 5000:
suggestions.append(
{
"token": token,
"action": "accumulate",
"confidence": min(activity["buys"] * 0.2, 0.9),
"reason": f"Wallet bought {activity['buys']} times, sold {activity['sells']} times",
"total_volume_usd": round(activity["total_usd"], 2),
}
)
return suggestions[:10]
async def _get_token_price(address: str, chain: str) -> float | None:
"""Get current token price from DexScreener."""
if chain == "solana":
result = await fetch_dexscreener(f"tokens/{address}")
if result and result.get("pairs"):
return float(result["pairs"][0].get("priceNative", 0))
return None
# ── Tool 2: Live Rug Predictor ───────────────────────────────────
class RugPredictorLiveRequest(BaseModel):
address: str
chain: str = "solana"
lookback_minutes: int = 30
@router.post("/rug_predictor_live")
async def rug_predictor_live(req: RugPredictorLiveRequest):
"""Live rug predictor - analyzes tokens in their first 5-30 minutes.
Watches new tokens in real-time, scores them, and alerts before the rug.
Price: $0.50/call
"""
address = req.address.lower()
chain = req.chain
# Fetch token data from multiple sources
pair_data = await fetch_dexscreener(f"tokens/{address}")
if not pair_data or not pair_data.get("pairs"):
return JSONResponse(
content={
"tool": "rug_predictor_live",
"address": address,
"chain": chain,
"status": "no_data",
"message": "Token not found on DexScreener",
"rug_score": 0,
"verdict": "UNKNOWN",
}
)
pair = pair_data["pairs"][0]
signals = []
rug_score = 0
# ── Signal 1: Liquidity Analysis ──────────────────────────────
liquidity_usd = pair.get("liquidity", {}).get("usd", 0)
if liquidity_usd < 1000:
signals.append(
{
"signal": "low_liquidity",
"severity": "critical",
"message": f"Liquidity only ${liquidity_usd:.0f} - extremely vulnerable to rug",
"weight": 25,
}
)
rug_score += 25
elif liquidity_usd < 5000:
signals.append(
{
"signal": "low_liquidity",
"severity": "high",
"message": f"Liquidity ${liquidity_usd:.0f} - high risk",
"weight": 15,
}
)
rug_score += 15
# ── Signal 2: LP Lock Status ──────────────────────────────────
lp_locked = pair.get("lockInfo", {}).get("locked", False)
if not lp_locked:
signals.append(
{
"signal": "lp_not_locked",
"severity": "critical",
"message": "Liquidity pool is NOT locked - creator can remove at any time",
"weight": 20,
}
)
rug_score += 20
# ── Signal 3: Price Action ────────────────────────────────────
price_change = pair.get("priceChange", {})
h1_change = price_change.get("h1", 0)
price_change.get("m5", 0)
if h1_change < -50:
signals.append(
{
"signal": "price_crash",
"severity": "critical",
"message": f"Price down {h1_change}% in 1 hour - active rug in progress",
"weight": 25,
}
)
rug_score += 25
# ── Signal 4: Volume/Liquidity Ratio ─────────────────────────
volume_24h = pair.get("volume", {}).get("h24", 0)
if liquidity_usd > 0:
vol_liq_ratio = volume_24h / liquidity_usd
if vol_liq_ratio > 10:
signals.append(
{
"signal": "extreme_volume",
"severity": "high",
"message": f"Volume/Liquidity ratio {vol_liq_ratio:.1f}x - suspicious activity",
"weight": 10,
}
)
rug_score += 10
# ── Signal 5: Transaction Count ───────────────────────────────
txns = pair.get("txns", {})
h1_txns = txns.get("h1", {})
buys = h1_txns.get("buys", 0)
sells = h1_txns.get("sells", 0)
if buys > 0 and sells > 0:
sell_ratio = sells / (buys + sells)
if sell_ratio > 0.8:
signals.append(
{
"signal": "mass_selling",
"severity": "high",
"message": f"{sell_ratio * 100:.0f}% of transactions are sells - panic selling",
"weight": 15,
}
)
rug_score += 15
# ── Signal 6: Pair Age ────────────────────────────────────────
pair_created_at = pair.get("pairCreatedAt", 0)
if pair_created_at:
age_hours = (time.time() * 1000 - pair_created_at) / (1000 * 3600)
if age_hours < 1:
signals.append(
{
"signal": "brand_new_pair",
"severity": "medium",
"message": f"Pair is only {age_hours * 60:.0f} minutes old - highest risk window",
"weight": 10,
}
)
rug_score += 10
# ── Determine Verdict ─────────────────────────────────────────
if rug_score >= 70:
verdict = "CRITICAL_RUG"
action = "AVOID - High probability of active or imminent rug"
elif rug_score >= 50:
verdict = "HIGH_RISK"
action = "AVOID - Multiple red flags detected"
elif rug_score >= 30:
verdict = "MEDIUM_RISK"
action = "CAUTION - Some warning signs, monitor closely"
elif rug_score >= 15:
verdict = "LOW_RISK"
action = "MONITOR - Minor concerns but mostly clean"
else:
verdict = "SAFE"
action = "Relatively clean - standard risk management applies"
return JSONResponse(
content={
"tool": "rug_predictor_live",
"address": address,
"chain": chain,
"rug_score": min(rug_score, 100),
"verdict": verdict,
"action": action,
"signals": signals,
"token_info": {
"name": pair.get("baseToken", {}).get("name", ""),
"symbol": pair.get("baseToken", {}).get("symbol", ""),
"price_usd": pair.get("priceUsd"),
"liquidity_usd": liquidity_usd,
"volume_24h": volume_24h,
"price_change_1h": h1_change,
"txns_1h": {"buys": buys, "sells": sells},
"lp_locked": lp_locked,
"pair_age_hours": round(age_hours, 2) if pair_created_at else None,
},
"timestamp": datetime.now(UTC).isoformat(),
"status": "ok",
}
)
# ── Tool 3: Whale Cluster Detection ──────────────────────────────
class WhaleClusterRequest(BaseModel):
address: str
chain: str = "solana"
min_cluster_size: int = 3
similarity_threshold: float = 0.7
@router.post("/whale_cluster")
async def whale_cluster(req: WhaleClusterRequest):
"""Identify coordinated whale clusters.
Group wallets that move together: same funding source, same timing,
same token sets. Identifies wash trading, coordinated pumps,
and insider networks.
Price: $0.30/call
"""
address = req.address.lower()
chain = req.chain
min_size = req.min_cluster_size
threshold = req.similarity_threshold
r = get_redis()
cluster_data = {
"seed_wallet": address,
"chain": chain,
"clusters": [],
"total_related_wallets": 0,
"risk_assessment": "unknown",
}
# Step 1: Get wallets that interacted with the same tokens
same_token_wallets = await _find_shared_token_wallets(address, chain, r)
# Step 2: Check for shared funding sources
funding_clusters = await _find_shared_funding(address, chain, r)
# Step 3: Check timing correlation
timing_clusters = await _find_timing_correlation(address, chain, r)
# Combine and score
wallet_scores = {}
for wallet, score in same_token_wallets.items():
wallet_scores[wallet] = wallet_scores.get(wallet, 0) + score * 0.4
for wallet, score in funding_clusters.items():
wallet_scores[wallet] = wallet_scores.get(wallet, 0) + score * 0.4
for wallet, score in timing_clusters.items():
wallet_scores[wallet] = wallet_scores.get(wallet, 0) + score * 0.2
# Filter by threshold
cluster_wallets = [
{"wallet": w, "similarity": round(s, 3)} for w, s in wallet_scores.items() if s >= threshold and w != address
]
cluster_wallets.sort(key=lambda x: x["similarity"], reverse=True)
# Group into clusters by similarity
clusters = []
used = set()
for cw in cluster_wallets:
if cw["wallet"] in used:
continue
cluster = {"members": [cw["wallet"]], "avg_similarity": cw["similarity"]}
used.add(cw["wallet"])
for other in cluster_wallets:
if other["wallet"] not in used and abs(other["similarity"] - cw["similarity"]) < 0.1:
cluster["members"].append(other["wallet"])
used.add(other["wallet"])
if len(cluster["members"]) >= min_size - 1: # -1 because seed wallet
cluster["members"].insert(0, address) # Add seed wallet
cluster["size"] = len(cluster["members"])
cluster["avg_similarity"] = round(
sum(wallet_scores.get(w, 0) for w in cluster["members"]) / len(cluster["members"]),
3,
)
clusters.append(cluster)
# Risk assessment
if any(c["size"] >= 5 for c in clusters):
cluster_data["risk_assessment"] = "high"
cluster_data["risk_message"] = "Large coordinated cluster detected - possible wash trading or insider network"
elif any(c["size"] >= 3 for c in clusters):
cluster_data["risk_assessment"] = "medium"
cluster_data["risk_message"] = "Medium cluster detected - wallets showing coordinated behavior"
else:
cluster_data["risk_assessment"] = "low"
cluster_data["risk_message"] = "No significant clusters detected"
cluster_data["clusters"] = clusters
cluster_data["total_related_wallets"] = len(cluster_wallets)
cluster_data["status"] = "ok"
return JSONResponse(content=cluster_data)
async def _find_shared_token_wallets(address: str, chain: str, r) -> dict[str, float]:
"""Find wallets that traded the same tokens."""
# Get tokens this wallet traded
token_key = f"rmi:wallet:{address}:{chain}:tokens"
tokens = r.smembers(token_key) if r else set()
if not tokens:
# Fetch from DexScreener as fallback
result = await fetch_dexscreener("search", {"q": address})
if result and result.get("pairs"):
tokens = {p.get("baseToken", {}).get("address", "") for p in result["pairs"][:20]}
# For each token, find other wallets
shared = {}
for token in tokens:
if not token:
continue
# Check Redis cache of recent traders for this token
traders_key = f"rmi:token:{token}:{chain}:traders"
traders = r.smembers(traders_key) if r else set()
for trader in traders:
if trader != address:
shared[trader] = shared.get(trader, 0) + 1
# Normalize
max_shared = max(shared.values()) if shared else 1
return {w: s / max_shared for w, s in shared.items()}
async def _find_shared_funding(address: str, chain: str, r) -> dict[str, float]:
"""Find wallets funded from the same source."""
# Check if we have funding data cached
funding_key = f"rmi:wallet:{address}:{chain}:funding"
funding_source = r.get(funding_key) if r else None
if funding_source:
# Find other wallets funded from same source
other_key = f"rmi:funding:{chain}:{funding_source}:wallets"
other_wallets = r.smembers(other_key) if r else set()
return {w: 0.9 for w in other_wallets if w != address}
return {}
async def _find_timing_correlation(address: str, chain: str, r) -> dict[str, float]:
"""Find wallets with correlated trading timing."""
# Get recent trade timestamps
timing_key = f"rmi:wallet:{address}:{chain}:timing"
timing_data = r.get(timing_key) if r else None
if timing_data:
json.loads(timing_data)
# Find wallets with similar timing patterns
correlation_key = f"rmi:timing:{chain}:correlations"
correlations = r.hgetall(correlation_key) if r else {}
correlated = {}
for wallet, corr_score in correlations.items():
if float(corr_score) > 0.5:
correlated[wallet] = float(corr_score)
return correlated
return {}
# ── Tool Pricing Registration ────────────────────────────────────
# These tools register their prices in the canonical tool prices dict
def register_alpha_tool_prices():
"""Register alpha tool prices with the enforcement system."""
try:
from app.routers.x402_enforcement import TOOL_PRICES
TOOL_PRICES.update(
{
"whale_copy_trade": {
"price_usd": 0.25,
"price_atoms": "250000",
"category": "alpha",
"trial_free": 1,
"description": "Real-time copy trade engine - find smart money trades with entry/exit prices, PnL, and follow suggestions",
},
"rug_predictor_live": {
"price_usd": 0.50,
"price_atoms": "500000",
"category": "security",
"trial_free": 1,
"description": "Live rug predictor - analyzes tokens in first 5-30 minutes with 6-signal rug probability scoring",
},
"whale_cluster": {
"price_usd": 0.30,
"price_atoms": "300000",
"category": "intelligence",
"trial_free": 1,
"description": "Coordinated whale cluster detection - identify wash trading, insider networks, and pump groups",
},
}
)
except Exception as e:
logger.warning(f"Failed to register alpha tool prices: {e}")
# Register on import
register_alpha_tool_prices()