first commit

This commit is contained in:
k
2026-04-20 23:05:59 +08:00
commit 9a72566753
33 changed files with 2138 additions and 0 deletions
+125
View File
@@ -0,0 +1,125 @@
import json
import logging
import anthropic
from app.config import settings
logger = logging.getLogger(__name__)
from typing import Optional
_client: Optional[anthropic.AsyncAnthropic] = None
SYSTEM_PROMPT = (
"You are a crypto trading signal analyst. Analyze Donald Trump's social media posts "
"and determine their likely impact on cryptocurrency markets. "
"Return only valid JSON with no additional text or markdown."
)
USER_PROMPT_TEMPLATE = """Analyze this Trump post for cryptocurrency trading signals.
Post: {text}
Respond with JSON only:
{{
"relevant": <true if this post could affect crypto/macro markets>,
"asset": "BTC" | "ETH" | null,
"sentiment": "bullish" | "bearish" | "neutral",
"signal": "buy" | "sell" | "short" | "hold",
"confidence": <0-100>,
"reasoning": "<1-2 sentences explaining the signal and why>"
}}
Signal rules:
- "buy": bullish crypto/macro signal → expect price rise → go long
- "sell": bearish signal for existing longs → exit position
- "short": strong bearish signal → expect price drop → go short
- "hold": relevant but unclear direction, or neutral macro
Bullish examples: pro-crypto policy, BTC reserve, anti-regulation, dollar weakness, tariff deal, market optimism
Bearish examples: SEC crackdowns, war escalation, economic sanctions, crypto ban threats, crisis
Not relevant: personal attacks, sports, endorsements, unrelated politics
Be strict on relevance — most posts are NOT relevant to crypto."""
_FALLBACK = {
"relevant": False,
"asset": None,
"sentiment": "neutral",
"signal": "hold",
"confidence": 0,
"reasoning": "",
}
def _get_client() -> anthropic.AsyncAnthropic:
global _client
if _client is None:
_client = anthropic.AsyncAnthropic(api_key=settings.anthropic_api_key)
return _client
async def analyze_post(text: str) -> dict:
"""Run Claude signal analysis on a Trump post.
Returns dict with keys: relevant, asset, sentiment, signal, confidence, reasoning.
Falls back to neutral hold on any error.
"""
try:
client = _get_client()
message = await client.messages.create(
model="claude-haiku-4-5-20251001",
max_tokens=300,
system=SYSTEM_PROMPT,
messages=[
{
"role": "user",
"content": USER_PROMPT_TEMPLATE.format(text=text[:2000]),
}
],
)
raw = message.content[0].text.strip()
if raw.startswith("```"):
lines = raw.split("\n")
raw = "\n".join(lines[1:-1] if lines[-1].strip() == "```" else lines[1:])
result = json.loads(raw)
sentiment = result.get("sentiment", "neutral")
if sentiment not in ("bullish", "bearish", "neutral"):
sentiment = "neutral"
signal = result.get("signal", "hold")
if signal not in ("buy", "sell", "short", "hold"):
signal = "hold"
confidence = int(result.get("confidence", 0))
confidence = max(0, min(100, confidence))
relevant = bool(result.get("relevant", False))
asset = result.get("asset")
if asset not in ("BTC", "ETH", None):
asset = None
reasoning = str(result.get("reasoning", ""))[:500]
return {
"relevant": relevant,
"asset": asset,
"sentiment": sentiment,
"signal": signal,
"confidence": confidence,
"reasoning": reasoning,
}
except anthropic.APIError as exc:
logger.error("Anthropic API error: %s", exc)
return dict(_FALLBACK)
except json.JSONDecodeError as exc:
logger.error("Failed to parse Claude JSON response: %s", exc)
return dict(_FALLBACK)
except Exception as exc:
logger.error("Unexpected error in analyze_post: %s", exc)
return dict(_FALLBACK)