Add robust Ollama output normalizer with fallback parse path for decision reliability
This commit is contained in:
72
services.py
72
services.py
@@ -89,11 +89,13 @@ def extra_research(symbol: str, weak_points: list, limit: int = 6):
|
||||
|
||||
def summarize_news_with_ollama(symbol: str, context_items: list):
|
||||
prompt = {"task": "Summarize market-moving info into a concise brief", "symbol": symbol, "news": context_items}
|
||||
fallback = " | ".join([(x.get("title") or "")[:90] for x in context_items[:3] if x.get("title")]) or f"No strong headlines for {symbol}"
|
||||
try:
|
||||
parsed = _ollama_generate(settings.ollama_curator_model, prompt)
|
||||
return parsed.get("summary", "no-summary")
|
||||
s = parsed.get("summary")
|
||||
return s if s else fallback
|
||||
except Exception:
|
||||
return "summary-unavailable"
|
||||
return fallback
|
||||
|
||||
|
||||
def strategy_signals(symbol: str, context_items: list):
|
||||
@@ -106,6 +108,30 @@ def strategy_signals(symbol: str, context_items: list):
|
||||
return {"strategy": "event-momentum-v1", "score": score, "action": action, "confidence": conf, "signals": {"bullish": bullish, "bearish": bearish}}
|
||||
|
||||
|
||||
def _normalize_decision(d: dict, strategy: dict):
|
||||
action = str(d.get("action", strategy.get("action", "hold"))).lower()
|
||||
if action not in {"buy", "sell", "hold"}:
|
||||
action = strategy.get("action", "hold")
|
||||
try:
|
||||
confidence = max(0.0, min(1.0, float(d.get("confidence", strategy.get("confidence", 0.5)))))
|
||||
except Exception:
|
||||
confidence = min(strategy.get("confidence", 0.5), 0.55)
|
||||
try:
|
||||
order_usd = float(d.get("order_usd", settings.max_order_usd))
|
||||
except Exception:
|
||||
order_usd = settings.max_order_usd
|
||||
order_usd = max(1.0, min(order_usd, settings.max_order_usd))
|
||||
reason = str(d.get("reason", "normalized-decision"))[:500]
|
||||
return {
|
||||
"action": action,
|
||||
"confidence": confidence,
|
||||
"order_usd": order_usd,
|
||||
"reason": reason,
|
||||
"needs_more_research": bool(d.get("needs_more_research", False)),
|
||||
"research_topics": d.get("research_topics", []) or [],
|
||||
}
|
||||
|
||||
|
||||
def llm_final_decision(symbol: str, context_items: list, strategy: dict, memory_hits: list):
|
||||
prompt = {
|
||||
"task": "Final trading decision. Return strict JSON.",
|
||||
@@ -116,21 +142,39 @@ def llm_final_decision(symbol: str, context_items: list, strategy: dict, memory_
|
||||
"news": context_items,
|
||||
"output_schema": {"action": "buy|sell|hold", "confidence": "0-1", "order_usd": f"<= {settings.max_order_usd}", "reason": "short rationale", "needs_more_research": True, "research_topics": ["..."]},
|
||||
}
|
||||
# pass 1: strict json mode
|
||||
try:
|
||||
d = _ollama_generate(settings.ollama_decision_model, prompt, timeout=60)
|
||||
action = str(d.get("action", "hold")).lower()
|
||||
if action not in {"buy", "sell", "hold"}:
|
||||
action = "hold"
|
||||
return {
|
||||
"action": action,
|
||||
"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"),
|
||||
"needs_more_research": bool(d.get("needs_more_research", False)),
|
||||
"research_topics": d.get("research_topics", []) or [],
|
||||
}
|
||||
return _normalize_decision(d, strategy)
|
||||
except Exception:
|
||||
return {"action": strategy.get("action", "hold"), "confidence": min(strategy.get("confidence", 0.5), 0.55), "order_usd": min(1.0, settings.max_order_usd), "reason": "decision-fallback-strategy", "needs_more_research": False, "research_topics": []}
|
||||
pass
|
||||
|
||||
# pass 2: non-json constrained output, then parse heuristically
|
||||
try:
|
||||
text_prompt = (
|
||||
f"Symbol: {symbol}\n"
|
||||
f"Strategy prior: {strategy}\n"
|
||||
f"Return 4 lines only:\n"
|
||||
f"action: buy|sell|hold\nconfidence: 0-1\norder_usd: <= {settings.max_order_usd}\nreason: <short>\n"
|
||||
)
|
||||
r = requests.post(f"{settings.ollama_url}/api/generate", json={"model": settings.ollama_decision_model, "prompt": text_prompt, "stream": False}, timeout=45)
|
||||
if r.ok:
|
||||
raw = (r.json().get("response", "") or "").lower()
|
||||
action = "buy" if "buy" in raw else ("sell" if "sell" in raw else "hold")
|
||||
conf = 0.6 if "confidence" not in raw else strategy.get("confidence", 0.55)
|
||||
parsed = {"action": action, "confidence": conf, "order_usd": settings.max_order_usd, "reason": raw[:300]}
|
||||
return _normalize_decision(parsed, strategy)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return {
|
||||
"action": strategy.get("action", "hold"),
|
||||
"confidence": min(strategy.get("confidence", 0.5), 0.55),
|
||||
"order_usd": min(5.0, settings.max_order_usd),
|
||||
"reason": "decision-fallback-strategy",
|
||||
"needs_more_research": False,
|
||||
"research_topics": [],
|
||||
}
|
||||
|
||||
|
||||
def alpaca_headers():
|
||||
|
||||
Reference in New Issue
Block a user