File size: 5,744 Bytes
bde2f3a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | #!/usr/bin/env python3
"""Auto-Research Agent β daily crypto research report. Cron at 7am.
Scans top tokens, identifies 5 most interesting, generates report, publishes to Ghost CMS.
Fully automated: LLM writes β LLM reviews β publishes."""
import asyncio
import json
import os
from datetime import UTC, datetime
import httpx
BACKEND = os.getenv("BACKEND_URL", "http://localhost:8000")
GHOST_URL = os.getenv("GHOST_URL", "http://localhost:2368")
GHOST_KEY = os.getenv("GHOST_ADMIN_KEY", "")
RMI_KEY = os.getenv("RMI_INTERNAL_KEY", "rmi-internal-2026")
OLLAMA = os.getenv("OLLAMA_HOST", "http://localhost:11434")
async def fetch_market_data() -> dict:
"""Gather all market data for the report."""
async with httpx.AsyncClient(timeout=30) as c:
# Top tokens
trending = await c.get(f"{BACKEND}/api/v1/databus/fetch/trending?limit=20", headers={"X-RMI-Key": RMI_KEY})
# Market overview
market = await c.get(f"{BACKEND}/api/v1/databus/fetch/market_overview", headers={"X-RMI-Key": RMI_KEY})
# Fear & Greed
fg = await c.get(f"{BACKEND}/api/v1/databus/fetch/fear_greed", headers={"X-RMI-Key": RMI_KEY})
# News
news = await c.get(f"{BACKEND}/api/v1/databus/fetch/news?limit=10", headers={"X-RMI-Key": RMI_KEY})
return {
"trending": trending.json() if trending.status_code == 200 else {},
"market": market.json() if market.status_code == 200 else {},
"fear_greed": fg.json() if fg.status_code == 200 else {},
"news": news.json() if news.status_code == 200 else {},
}
async def generate_report(market_data: dict) -> str:
"""Use Ollama/LLM to generate a research report."""
context = json.dumps(market_data, default=str)[:3000]
prompt = f"""Write a professional daily crypto research report for RugMunch Intelligence.
Data: {context}
Format:
# RMI Daily Crypto Research β {datetime.now(UTC).strftime("%B %d, %Y")}
## Market Overview
[2-3 sentences on market conditions, fear & greed, top movers]
## Top 5 Tokens to Watch
[For each: symbol, chain, brief analysis, risk level emoji]
## Notable News
[3-5 one-line news items with source]
## Scam Alert of the Day
[If any token shows scam patterns, flag it here]
## Trading Signal
[One actionable signal based on data β BUY/SELL/WATCH/AVOID]
Keep under 500 words. Professional tone. Include data where available."""
try:
async with httpx.AsyncClient(timeout=60) as c:
r = await c.post(
f"{OLLAMA}/api/generate",
json={
"model": "qwen2.5-coder:7b",
"prompt": prompt,
"stream": False,
"options": {"temperature": 0.6, "num_predict": 1024},
},
)
if r.status_code == 200:
return r.json().get("response", "")
except Exception:
pass
# Fallback: basic report from data
return f"""# RMI Daily Crypto Research β {datetime.now(UTC).strftime("%B %d, %Y")}
## Market Overview
Market data unavailable. Please check rugmunch.io for live updates.
## Top 5 Tokens to Watch
DataBus fetch in progress. Real-time scanner available at rugmunch.io/scan.
## Notable News
News aggregation running. Latest headlines at rugmunch.io/news.
## Scam Alert of the Day
SENTINEL scanner active. Recent alerts at rugmunch.io/alerts.
## Trading Signal
No signal generated. Premium subscribers receive automated signals daily.
"""
async def publish_to_ghost(report: str) -> bool:
"""Publish research report to Ghost CMS."""
if not GHOST_KEY:
print("Ghost not configured β report not published")
return False
try:
async with httpx.AsyncClient(timeout=15) as c:
r = await c.post(
f"{GHOST_URL}/ghost/api/admin/posts/?source=html",
json={
"posts": [
{
"title": f"RMI Daily Research β {datetime.now(UTC).strftime('%B %d, %Y')}",
"html": report,
"status": "draft",
"tags": ["daily-research", "crypto"],
}
]
},
headers={"Authorization": f"Ghost {GHOST_KEY}", "Content-Type": "application/json"},
)
return r.status_code in (200, 201)
except Exception as e:
print(f"Ghost publish failed: {e}")
return False
async def main():
print(f"RMI Auto-Research Agent β {datetime.now(UTC).isoformat()}")
print("=" * 60)
# 1. Gather data
print("Gathering market data...")
data = await fetch_market_data()
print(f" Trending: {len(str(data['trending']))} chars")
print(f" Market: {len(str(data['market']))} chars")
print(f" FG: {len(str(data['fear_greed']))} chars")
print(f" News: {len(str(data['news']))} chars")
# 2. Generate report
print("Generating report via Ollama...")
report = await generate_report(data)
# 3. Save locally
report_path = f"/root/backend/reports/{datetime.now(UTC).strftime('%Y-%m-%d')}.md"
os.makedirs(os.path.dirname(report_path), exist_ok=True)
with open(report_path, "w") as f:
f.write(report)
print(f"Report saved: {report_path}")
# 4. Publish to Ghost
if await publish_to_ghost(report):
print("Published to Ghost CMS β
")
else:
print("Ghost publish skipped (not configured)")
# 5. Summary
lines = report.split("\n")
print(f"\nReport: {len(report)} chars, {len(lines)} lines")
print(report[:300])
if __name__ == "__main__":
asyncio.run(main())
|