Files
robinhood/backend/app/services/openrouter.py
T

140 lines
4.9 KiB
Python

import httpx
import json
from typing import List
from app.config import settings
from app.schemas.schemas import (
AIAnalysisRequest,
AIAnalysisResponse,
Recommendation,
RiskLevel,
SupportResistance,
)
class OpenRouterService:
def __init__(self):
self.base_url = settings.OPENROUTER_BASE_URL
self.api_key = settings.OPENROUTER_API_KEY
self.model = settings.OPENROUTER_MODEL
async def analyze_scenario(self, request: AIAnalysisRequest) -> AIAnalysisResponse:
"""
Analyze trading scenario using Claude 3.5 Sonnet via OpenRouter
Args:
request: AIAnalysisRequest with price data and indicators
Returns:
AIAnalysisResponse with recommendation and analysis
"""
# Prepare recent price data for analysis
recent_prices = request.price_data[-50:] if len(request.price_data) > 50 else request.price_data
# Format price data for the AI
price_summary = f"Current Price: ${request.current_price:.2f}\n"
price_summary += f"Recent Close Prices: {[f'${p.close:.2f}' for p in recent_prices[-10:]]}\n"
# Calculate basic statistics
prices = [p.close for p in recent_prices]
avg_price = sum(prices) / len(prices)
price_range = max(prices) - min(prices)
# Create analysis prompt
prompt = f"""You are a senior quantitative analyst specializing in gold (XAU/USD) trading. Analyze the following market data and provide a trading recommendation.
Market Data:
{price_summary}
Average Price (last 50 periods): ${avg_price:.2f}
Price Range: ${price_range:.2f}
Technical Indicators:
{json.dumps(request.indicators, indent=2)}
Based on this data, provide:
1. A clear recommendation: BUY, SELL, or HOLD
2. Confidence level (0-100%)
3. Detailed reasoning (2-3 sentences)
4. Support and resistance levels (up to 3 each)
5. Risk level assessment: LOW, MEDIUM, or HIGH
Respond in JSON format:
{{
"recommendation": "BUY|SELL|HOLD",
"confidence": 0-100,
"reasoning": "Your detailed analysis here",
"support_levels": [price1, price2, price3],
"resistance_levels": [price1, price2, price3],
"risk_level": "LOW|MEDIUM|HIGH"
}}
"""
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json",
"HTTP-Referer": settings.OPENROUTER_SITE_URL,
"X-Title": settings.OPENROUTER_SITE_NAME,
}
payload = {
"model": self.model,
"messages": [
{
"role": "system",
"content": "You are a professional gold trading analyst. Always respond with valid JSON.",
},
{"role": "user", "content": prompt},
],
"temperature": 0.7,
"max_tokens": 1000,
}
async with httpx.AsyncClient(timeout=60.0) as client:
response = await client.post(
f"{self.base_url}/chat/completions",
headers=headers,
json=payload,
)
response.raise_for_status()
data = response.json()
# Extract AI response
ai_content = data["choices"][0]["message"]["content"]
# Parse JSON response
try:
# Try to extract JSON from markdown code blocks if present
if "```json" in ai_content:
json_start = ai_content.find("```json") + 7
json_end = ai_content.find("```", json_start)
ai_content = ai_content[json_start:json_end].strip()
elif "```" in ai_content:
json_start = ai_content.find("```") + 3
json_end = ai_content.find("```", json_start)
ai_content = ai_content[json_start:json_end].strip()
analysis_data = json.loads(ai_content)
except json.JSONDecodeError:
# Fallback to default response if JSON parsing fails
return AIAnalysisResponse(
recommendation=Recommendation.HOLD,
confidence=50.0,
reasoning="Unable to parse AI response. Please try again.",
support_resistance=SupportResistance(support=[], resistance=[]),
risk_level=RiskLevel.MEDIUM,
)
# Map to response schema
return AIAnalysisResponse(
recommendation=Recommendation(analysis_data.get("recommendation", "HOLD")),
confidence=float(analysis_data.get("confidence", 50)),
reasoning=analysis_data.get("reasoning", "Analysis completed."),
support_resistance=SupportResistance(
support=analysis_data.get("support_levels", []),
resistance=analysis_data.get("resistance_levels", []),
),
risk_level=RiskLevel(analysis_data.get("risk_level", "MEDIUM")),
)
openrouter_service = OpenRouterService()