fix: ornith leads when confident (>0.55), qwen vetoes borderline only. Was too tight — 2/2 agreement blocked all trades.

This commit is contained in:
drjones
2026-08-03 03:33:44 -07:00
parent b1c871c198
commit fc96c6f070
7 changed files with 146 additions and 16 deletions

34
bot.py
View File

@@ -342,15 +342,13 @@ def llm_vote(cfg, price, mom, mom_score, conn):
except Exception as ex:
fast_v, fast_c, fast_w = "BORDERLINE", 0.50, f"gate: {str(ex)[:40]}"
# ── 2-model ensemble: qwen3.5 (MacBook) + ornith (GamingPC RTX 3070) ──
# Disagreement = SKIP (conservative). No diverifier — avoids VRAM contention.
# ── 2-model: ornith leads when confident, qwen vetoes borderline ──
NO_PROXY = {"proxies": {"http": None, "https": None}}
votes = []
# Model 1: qwen3.5 fast gate (MacBook — already ran)
v1 = fast_v if fast_v in ("UP","DOWN","SKIP") else "SKIP"
votes.append((v1, fast_c if fast_v in ("UP","DOWN","SKIP") else 0.50, fast_w, "qwen3.5"))
c1 = fast_c if fast_v in ("UP","DOWN","SKIP") else 0.50
# Model 2: ornith deep verify (GamingPC RTX 3070 — always hot)
# Model 2: ornith deep verify (GamingPC RTX 3070)
try:
r2 = requests.post("http://10.30.20.186:11434/api/generate",
json={"model": "ornith:latest", "prompt": prompt, "stream": False, "think": False, "keep_alive": "10m",
@@ -360,20 +358,24 @@ def llm_vote(cfg, price, mom, mom_score, conn):
d2 = json.loads(txt2[s2:e2]) if s2 >= 0 else {}
v2 = str(d2.get("vote","SKIP")).upper()
if v2 not in ("UP","DOWN","SKIP"): v2 = "SKIP"
votes.append((v2, float(d2.get("conf",0)), str(d2.get("why",""))[:120], "ornith"))
c2 = float(d2.get("conf",0))
w2 = str(d2.get("why",""))[:120]
except Exception as ex2:
LOG.warning(f"ornith failed: {ex2}")
votes.append(("SKIP", 0.0, "ornith offline", "ornith"))
return v1, c1, f"[qwen-only: {v1} {c1:.2f}] {fast_w[:80]}"
# Majority: only trade if BOTH agree (2/2). Any disagreement = SKIP.
tally = {"UP": 0, "DOWN": 0, "SKIP": 0}
for v, c, w, src in votes:
tally[v] += 1
winner = "UP" if tally["UP"] == 2 else ("DOWN" if tally["DOWN"] == 2 else "SKIP")
avg_conf = sum(v[1] for v in votes if v[0] == winner) / max(1, tally[winner])
reasons = " | ".join(f"{src}={v}" for v, _, _, src in votes)
tag = "ENSEMBLE" if winner != "SKIP" else "SPLIT"
return winner, avg_conf, f"[{tag}: {tally['UP']}U/{tally['DOWN']}D/{tally['SKIP']}S] {reasons}"
# ornith confident (>0.55) → lead, regardless of qwen
if v2 in ("UP","DOWN") and c2 >= 0.55:
return v2, c2, f"[ornith-lead {v2} {c2:.2f} | qwen={v1}] {w2[:60]}"
# both agree → go
if v1 == v2 and v1 in ("UP","DOWN"):
avg = (c1 + c2) / 2
return v1, avg, f"[agree {v1} {avg:.2f}] {w2[:60]}"
# qwen confident + ornith uncertain → use qwen
if v1 in ("UP","DOWN") and c1 >= 0.65 and c2 < 0.55:
return v1, c1, f"[qwen-lead {v1} {c1:.2f} | ornith={v2}] {fast_w[:60]}"
# disagreement or both uncertain → skip
return "SKIP", 0.0, f"[split qwen={v1}({c1:.2f}) ornith={v2}({c2:.2f})] {w2[:50]}"
def decide(cfg, conn, price):
"""Two-tier decision: qwen fast-gate → ornith deep-verify.