v1.1 autonomous upgrade: continuous curation, risk engine, scheduler hardening, systemd service

This commit is contained in:
2026-02-25 19:09:05 -08:00
parent cce199d73a
commit 4d17bf8018
14 changed files with 209 additions and 59 deletions

View File

@@ -1,12 +1,11 @@
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"
def searx_news(symbol: str, limit: int = 10):
q = f"{symbol} stock news earnings guidance analyst macro risk"
params = {"q": q, "format": "json", "language": "en"}
try:
r = requests.get(settings.searx_url, params=params, timeout=20)
@@ -14,19 +13,41 @@ def searx_news(symbol: str, limit: int = 8):
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]})
out.append({"title": it.get("title", ""), "url": it.get("url", ""), "content": it.get("content", "")[:500]})
return out
except Exception:
return []
def summarize_news_with_ollama(symbol: str, context_items: list):
payload = {
"model": settings.ollama_model,
"stream": False,
"prompt": json.dumps({
"task": "Summarize market-moving info into a concise, neutral brief.",
"symbol": symbol,
"news": context_items,
"format": {"summary": "<=140 words", "bullish_points": ["..."], "bearish_points": ["..."]}
}),
"format": "json",
}
try:
r = requests.post(f"{settings.ollama_url}/api/generate", json=payload, timeout=45)
r.raise_for_status()
resp = r.json().get("response", "{}")
parsed = json.loads(resp)
return parsed.get("summary", "no-summary")
except Exception:
return "summary-unavailable"
def ollama_decide(symbol: str, context_items: list):
prompt = {
"task": "You are a strict trading policy engine. Return JSON only.",
"task": "You are a conservative autonomous trading policy engine. Return strict JSON only.",
"constraints": {
"actions": ["buy", "sell", "hold"],
"max_order_usd": settings.max_order_usd,
"style": "conservative intraday swing",
"risk": "do not overtrade; prefer hold on weak signal",
},
"symbol": symbol,
"news": context_items,
@@ -44,19 +65,18 @@ def ollama_decide(symbol: str, context_items: list):
"format": "json",
}
try:
r = requests.post(f"{settings.ollama_url}/api/generate", json=payload, timeout=40)
r = requests.post(f"{settings.ollama_url}/api/generate", json=payload, timeout=45)
r.raise_for_status()
resp = r.json().get("response", "{}")
d = json.loads(resp)
action = d.get("action", "hold").lower()
action = str(d.get("action", "hold")).lower()
if action not in {"buy", "sell", "hold"}:
action = "hold"
confidence = float(d.get("confidence", 0.5))
confidence = max(0.0, min(1.0, 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,
@@ -73,16 +93,6 @@ def alpaca_headers():
}
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
@@ -107,3 +117,23 @@ def account_snapshot():
return r.json()
except Exception:
return {}
def positions_snapshot():
try:
r = requests.get(f"{settings.alpaca_base}/v2/positions", headers=alpaca_headers(), timeout=20)
if r.ok:
return r.json()
except Exception:
pass
return []
def market_open():
try:
r = requests.get(f"{settings.alpaca_base}/v2/clock", headers=alpaca_headers(), timeout=20)
if r.ok:
return bool(r.json().get("is_open", False))
except Exception:
pass
return False