import json import random import requests from datetime import datetime from config import settings def searx_news(symbol: str, limit: int = 8): q = f"{symbol} stock news earnings guidance analyst" params = {"q": q, "format": "json", "language": "en"} try: r = requests.get(settings.searx_url, params=params, timeout=20) r.raise_for_status() data = r.json() out = [] for it in data.get("results", [])[:limit]: out.append({"title": it.get("title", ""), "url": it.get("url", ""), "content": it.get("content", "")[:400]}) return out except Exception: return [] def ollama_decide(symbol: str, context_items: list): prompt = { "task": "You are a strict trading policy engine. Return JSON only.", "constraints": { "actions": ["buy", "sell", "hold"], "max_order_usd": settings.max_order_usd, "style": "conservative intraday swing", }, "symbol": symbol, "news": context_items, "output_schema": { "action": "buy|sell|hold", "confidence": "0-1", "reason": "short rationale", "order_usd": f"<= {settings.max_order_usd}", }, } payload = { "model": settings.ollama_model, "prompt": json.dumps(prompt), "stream": False, "format": "json", } try: r = requests.post(f"{settings.ollama_url}/api/generate", json=payload, timeout=40) r.raise_for_status() resp = r.json().get("response", "{}") d = json.loads(resp) action = d.get("action", "hold").lower() if action not in {"buy", "sell", "hold"}: action = "hold" confidence = float(d.get("confidence", 0.5)) order_usd = min(float(d.get("order_usd", settings.max_order_usd)), settings.max_order_usd) reason = d.get("reason", "fallback") return {"action": action, "confidence": confidence, "order_usd": order_usd, "reason": reason} except Exception: # resilient fallback to hold or tiny buy return { "action": random.choice(["hold", "hold", "buy"]), "confidence": 0.3, "order_usd": min(1.0, settings.max_order_usd), "reason": "fallback-mode", } def alpaca_headers(): return { "APCA-API-KEY-ID": settings.alpaca_key, "APCA-API-SECRET-KEY": settings.alpaca_secret, "Content-Type": "application/json", } def alpaca_last_price(symbol: str): url = f"https://data.alpaca.markets/v2/stocks/{symbol}/trades/latest" try: r = requests.get(url, headers=alpaca_headers(), timeout=20) r.raise_for_status() return float(r.json()["trade"]["p"]) except Exception: return None def place_order(symbol: str, action: str, order_usd: float): if action not in {"buy", "sell"}: return None payload = { "symbol": symbol, "side": action, "type": "market", "time_in_force": "day", "notional": round(order_usd, 2), } try: r = requests.post(f"{settings.alpaca_base}/v2/orders", headers=alpaca_headers(), json=payload, timeout=20) return {"ok": r.ok, "status": r.status_code, "json": r.json() if r.text else {}} except Exception as e: return {"ok": False, "status": 0, "json": {"error": str(e)}} def account_snapshot(): try: r = requests.get(f"{settings.alpaca_base}/v2/account", headers=alpaca_headers(), timeout=20) r.raise_for_status() return r.json() except Exception: return {}