merge: chore/cleanup-remove-bloat-and-secrets into main
This commit is contained in:
commit
bde2f3a97d
1173 changed files with 437609 additions and 0 deletions
655
app/routers/x402_alpha_revenue_tools.py
Normal file
655
app/routers/x402_alpha_revenue_tools.py
Normal file
|
|
@ -0,0 +1,655 @@
|
|||
"""
|
||||
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
|
||||
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
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):
|
||||
"""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()
|
||||
Loading…
Add table
Add a link
Reference in a new issue