- 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>
183 lines
6.5 KiB
Python
183 lines
6.5 KiB
Python
"""
|
|
Enhanced Daily Market Data - Price action, sentiment, security, whales.
|
|
|
|
Pulls from ALL our data sources to create comprehensive daily analysis.
|
|
"""
|
|
|
|
import asyncio
|
|
import logging
|
|
import os
|
|
from datetime import UTC, datetime
|
|
|
|
import httpx
|
|
|
|
logger = logging.getLogger("market_data")
|
|
|
|
|
|
async def get_price_action() -> dict:
|
|
"""Get real-time prices and 24h changes for major assets."""
|
|
try:
|
|
from app.caching_shield.solana_tracker import get_solana_tracker
|
|
|
|
st = get_solana_tracker()
|
|
|
|
assets = {
|
|
"solana": "So11111111111111111111111111111111111111112",
|
|
"usdc": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
|
|
}
|
|
prices = {}
|
|
for name, mint in assets.items():
|
|
r = await st.get_price(mint)
|
|
if r:
|
|
prices[name] = {"price": r.get("price", 0), "liquidity": r.get("liquidity", 0)}
|
|
|
|
# Also get trending tokens
|
|
trending = await st.get_tokens_trending(limit=5)
|
|
|
|
return {"major_assets": prices, "trending": trending}
|
|
except Exception as e:
|
|
return {"error": str(e), "major_assets": {}, "trending": []}
|
|
|
|
|
|
async def get_fear_greed() -> dict:
|
|
"""Get Fear & Greed index from our local MCP."""
|
|
try:
|
|
# Use our crypto-feargreed-mcp
|
|
async with httpx.AsyncClient(timeout=10) as c:
|
|
r = await c.get("https://api.alternative.me/fng/?limit=2")
|
|
if r.status_code == 200:
|
|
data = r.json().get("data", [])
|
|
if data:
|
|
current = data[0]
|
|
return {
|
|
"value": int(current.get("value", 50)),
|
|
"classification": current.get("value_classification", "Neutral"),
|
|
"timestamp": current.get("timestamp", ""),
|
|
}
|
|
except Exception:
|
|
pass
|
|
return {"value": 50, "classification": "Neutral"}
|
|
|
|
|
|
async def get_top_movers() -> dict:
|
|
"""Get top gainers and losers from CoinGecko."""
|
|
try:
|
|
key = os.getenv("COINGECKO_API_KEY", "")
|
|
async with httpx.AsyncClient(timeout=10) as c:
|
|
# Top gainers
|
|
r = await c.get(
|
|
"https://pro-api.coingecko.com/api/v3/search/trending",
|
|
headers={"x-cg-pro-api-key": key} if key else {},
|
|
)
|
|
if r.status_code == 200:
|
|
coins = r.json().get("coins", [])
|
|
return {
|
|
"trending": [c.get("item", {}).get("name") for c in coins[:5]],
|
|
"trending_score": [c.get("item", {}).get("market_cap_rank") for c in coins[:5]],
|
|
}
|
|
except Exception:
|
|
pass
|
|
return {"trending": [], "trending_score": []}
|
|
|
|
|
|
async def get_security_alerts() -> dict:
|
|
"""Get recent security incidents from our risk scanner + news."""
|
|
alerts = []
|
|
try:
|
|
# Check recent hacks from known sources
|
|
async with httpx.AsyncClient(timeout=10) as c:
|
|
# Rekt News (crypto hacks database)
|
|
r = await c.get("https://api.rekt.news/api/v2/leaderboard?limit=5")
|
|
if r.status_code == 200:
|
|
hacks = r.json()
|
|
if isinstance(hacks, list):
|
|
for h in hacks[:3]:
|
|
alerts.append(
|
|
{
|
|
"type": "hack",
|
|
"project": h.get("name", "Unknown"),
|
|
"amount_lost": h.get("totalLost", ""),
|
|
"date": h.get("date", ""),
|
|
}
|
|
)
|
|
except Exception:
|
|
pass
|
|
|
|
try:
|
|
# Check RugCheck for recent rug pulls
|
|
async with httpx.AsyncClient(timeout=10) as c:
|
|
r = await c.get("https://api.rugcheck.xyz/v1/stats/recent")
|
|
if r.status_code == 200:
|
|
rug_data = r.json()
|
|
alerts.append(
|
|
{
|
|
"type": "rug_check",
|
|
"recent_scans": len(rug_data) if isinstance(rug_data, list) else 0,
|
|
}
|
|
)
|
|
except Exception:
|
|
pass
|
|
|
|
return {"alerts": alerts, "count": len(alerts)}
|
|
|
|
|
|
async def get_whale_activity() -> dict:
|
|
"""Get whale movement summary."""
|
|
try:
|
|
from app.caching_shield.solana_tracker import get_solana_tracker
|
|
|
|
st = get_solana_tracker()
|
|
|
|
# Get trending tokens with high volume (whale activity indicator)
|
|
trending = await st.get_tokens_trending(limit=10)
|
|
if trending:
|
|
high_volume = list(trending.get("data", trending.get("tokens", []))[:5])
|
|
return {"trending_high_volume": len(high_volume), "chains": ["solana"]}
|
|
except Exception:
|
|
pass
|
|
return {"trending_high_volume": 0}
|
|
|
|
|
|
async def get_prediction_markets() -> dict:
|
|
"""Get Polymarket odds for crypto-related markets."""
|
|
try:
|
|
async with httpx.AsyncClient(timeout=10) as c:
|
|
r = await c.get("https://gamma-api.polymarket.com/events?tag=crypto&limit=5&active=true")
|
|
if r.status_code == 200:
|
|
events = r.json()
|
|
markets = []
|
|
for e in events[:5]:
|
|
markets.append(
|
|
{
|
|
"title": e.get("title", ""),
|
|
"volume": e.get("volume", 0),
|
|
"liquidity": e.get("liquidity", 0),
|
|
}
|
|
)
|
|
return {"active_markets": len(markets), "top_markets": markets}
|
|
except Exception:
|
|
pass
|
|
return {"active_markets": 0, "top_markets": []}
|
|
|
|
|
|
async def get_daily_rundown_data() -> dict:
|
|
"""Get ALL daily data for the AI market rundown."""
|
|
results = await asyncio.gather(
|
|
get_price_action(),
|
|
get_fear_greed(),
|
|
get_top_movers(),
|
|
get_security_alerts(),
|
|
get_whale_activity(),
|
|
get_prediction_markets(),
|
|
return_exceptions=True,
|
|
)
|
|
|
|
return {
|
|
"price_action": results[0] if not isinstance(results[0], Exception) else {},
|
|
"fear_greed": results[1] if not isinstance(results[1], Exception) else {},
|
|
"top_movers": results[2] if not isinstance(results[2], Exception) else {},
|
|
"security_alerts": results[3] if not isinstance(results[3], Exception) else {},
|
|
"whale_activity": results[4] if not isinstance(results[4], Exception) else {},
|
|
"prediction_markets": results[5] if not isinstance(results[5], Exception) else {},
|
|
"timestamp": datetime.now(UTC).isoformat(),
|
|
}
|