diff --git a/app/db.py b/app/db.py index 82a0e4bab..49871418a 100644 --- a/app/db.py +++ b/app/db.py @@ -1260,7 +1260,8 @@ def _compute_whale_public_picks(client, game_nos): for gno in game_nos: users = game_data.get(gno, {}) if not users: - picks[gno] = {"whale_pick": None, "public_pick": None, + picks[gno] = {"whale_pick": None, "whale_second_pick": None, + "public_pick": None, "public_second_pick": None, "whale_count": 0, "public_count": 0, "bettor_counts": {"A": 0, "B": 0, "C": 0}} continue @@ -1280,7 +1281,13 @@ def _compute_whale_public_picks(client, game_nos): for c in CHAIR_LABELS: whale_money[c] += users[uid].get(c, 0) whale_total = sum(whale_money.values()) - whale_pick = max(CHAIR_LABELS, key=lambda c: whale_money[c]) if whale_total > 0 else None + if whale_total > 0: + whale_ranked = sorted(CHAIR_LABELS, key=lambda c: whale_money[c], reverse=True) + whale_pick = whale_ranked[0] + whale_second_pick = whale_ranked[1] + else: + whale_pick = None + whale_second_pick = None # Sum public money per chair pub_money = {"A": 0, "B": 0, "C": 0} @@ -1289,7 +1296,13 @@ def _compute_whale_public_picks(client, game_nos): pub_money[c] += users[uid].get(c, 0) pub_total = sum(pub_money.values()) total_bettors = len(user_totals) - public_pick = max(CHAIR_LABELS, key=lambda c: pub_money[c]) if pub_total > 0 and total_bettors > 5 else None + if pub_total > 0 and total_bettors > 5: + pub_ranked = sorted(CHAIR_LABELS, key=lambda c: pub_money[c], reverse=True) + public_pick = pub_ranked[0] + public_second_pick = pub_ranked[1] + else: + public_pick = None + public_second_pick = None # Count bettors per chair bettor_counts = {"A": 0, "B": 0, "C": 0} @@ -1300,7 +1313,9 @@ def _compute_whale_public_picks(client, game_nos): picks[gno] = { "whale_pick": whale_pick, + "whale_second_pick": whale_second_pick, "public_pick": public_pick, + "public_second_pick": public_second_pick, "whale_count": len(whale_uids), "public_count": len(pub_uids), "bettor_counts": bettor_counts, @@ -1372,25 +1387,29 @@ def get_prediction_analysis() -> dict: # Backtesting backtest = _backtest_theories(winners) - # Last 20 prediction vs actual - last_20_raw = _last_n_predictions(winners, 20) - # Attach game_nos to last_20 - for entry in last_20_raw: + # Last 50 prediction vs actual + last_50_raw = _last_n_predictions(winners, 50) + # Attach game_nos to last_50 + for entry in last_50_raw: idx = entry["index"] entry["game_no"] = game_nos[idx] if idx < len(game_nos) else 0 - # Merge whale/public picks into last_20 - last_20_game_nos = [e["game_no"] for e in last_20_raw if e.get("game_no")] - wp_data = _compute_whale_public_picks(client, last_20_game_nos) - for entry in last_20_raw: + # Merge whale/public picks into last_50 + last_50_game_nos = [e["game_no"] for e in last_50_raw if e.get("game_no")] + wp_data = _compute_whale_public_picks(client, last_50_game_nos) + for entry in last_50_raw: gno = entry.get("game_no", 0) wp = wp_data.get(gno, {}) entry["whale_pick"] = wp.get("whale_pick") + entry["whale_second_pick"] = wp.get("whale_second_pick") entry["public_pick"] = wp.get("public_pick") + entry["public_second_pick"] = wp.get("public_second_pick") entry["bettor_counts"] = wp.get("bettor_counts", {"A": 0, "B": 0, "C": 0}) actual = entry["actual"] entry["whale_hit"] = (wp.get("whale_pick") == actual) if wp.get("whale_pick") else None + entry["whale_semi"] = (not entry["whale_hit"] and wp.get("whale_second_pick") == actual) if wp.get("whale_pick") else None entry["public_hit"] = (wp.get("public_pick") == actual) if wp.get("public_pick") else None + entry["public_semi"] = (not entry["public_hit"] and wp.get("public_second_pick") == actual) if wp.get("public_pick") else None # Card analysis card_values = _card_value_distribution(cards_data) @@ -1419,7 +1438,7 @@ def get_prediction_analysis() -> dict: return { "total_games": len(winners), "last_winners": winners[-10:] if len(winners) >= 10 else winners, - "last_20_predictions": last_20_raw, + "last_20_predictions": last_50_raw, "prediction": prediction, "signals": signals, "markov1": {"matrix": markov1, "counts": {k: dict(v) for k, v in markov1_counts.items()}}, @@ -1461,8 +1480,10 @@ def get_prediction_history(limit: int = 100) -> dict: # Merge and compute accuracy whale_hits = 0 + whale_semi_hits = 0 whale_total = 0 public_hits = 0 + public_semi_hits = 0 public_total = 0 model_full_hits = 0 model_semi_hits = 0 @@ -1471,11 +1492,15 @@ def get_prediction_history(limit: int = 100) -> dict: gno = entry.get("game_no", 0) wp = wp_data.get(gno, {}) entry["whale_pick"] = wp.get("whale_pick") + entry["whale_second_pick"] = wp.get("whale_second_pick") entry["public_pick"] = wp.get("public_pick") + entry["public_second_pick"] = wp.get("public_second_pick") entry["bettor_counts"] = wp.get("bettor_counts", {"A": 0, "B": 0, "C": 0}) actual = entry["actual"] entry["whale_hit"] = (wp.get("whale_pick") == actual) if wp.get("whale_pick") else None + entry["whale_semi"] = (not entry["whale_hit"] and wp.get("whale_second_pick") == actual) if wp.get("whale_pick") else None entry["public_hit"] = (wp.get("public_pick") == actual) if wp.get("public_pick") else None + entry["public_semi"] = (not entry["public_hit"] and wp.get("public_second_pick") == actual) if wp.get("public_pick") else None # Remove internal index from output del entry["index"] @@ -1489,13 +1514,19 @@ def get_prediction_history(limit: int = 100) -> dict: whale_total += 1 if entry["whale_hit"]: whale_hits += 1 + elif entry.get("whale_semi"): + whale_semi_hits += 1 if entry["public_hit"] is not None: public_total += 1 if entry["public_hit"]: public_hits += 1 + elif entry.get("public_semi"): + public_semi_hits += 1 total_pred = len(predictions) model_score = model_full_hits + model_semi_hits * 0.5 + whale_score = whale_hits + whale_semi_hits * 0.5 + public_score = public_hits + public_semi_hits * 0.5 return { "total_games": len(winners), @@ -1510,13 +1541,15 @@ def get_prediction_history(limit: int = 100) -> dict: }, "whale": { "hits": whale_hits, + "semi": whale_semi_hits, "total": whale_total, - "pct": round(whale_hits / whale_total * 100, 1) if whale_total else 0, + "pct": round(whale_score / whale_total * 100, 1) if whale_total else 0, }, "public": { "hits": public_hits, + "semi": public_semi_hits, "total": public_total, - "pct": round(public_hits / public_total * 100, 1) if public_total else 0, + "pct": round(public_score / public_total * 100, 1) if public_total else 0, }, }, } diff --git a/static/predictions.html b/static/predictions.html index e2bc18dec..c8292b806 100644 --- a/static/predictions.html +++ b/static/predictions.html @@ -257,10 +257,13 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-
- +| Game | Predicted | 2nd Pick | P(A) | P(B) | P(C) | Whale | Public | Actual | Result | ||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Not enough data | ${p.whale_pick} ${whLabel} | `; } else { whaleCell = '-- | '; } - // Public cell + // Public cell with semi-win let pubCell; if (p.public_pick) { - const puCls = p.public_hit ? 'correct' : 'wrong'; - const puLabel = p.public_hit ? 'HIT' : 'MISS'; + const puCls = p.public_hit ? 'correct' : (p.public_semi ? 'semi' : 'wrong'); + const puLabel = p.public_hit ? 'HIT' : (p.public_semi ? 'SEMI' : 'MISS'); pubCell = `${p.public_pick} ${puLabel} | `; } else { pubCell = '-- | '; @@ -719,17 +728,114 @@ function renderLast20(predictions) {${p.actual} | ${resultLabel} | `; - }).join('') + - `|||||||
| Not enough data | |||||||||||||
| Accuracy (last ${predictions.length}) | -${whalePct !== '--' ? whalePct + '%' : '--'} | -${publicPct !== '--' ? publicPct + '%' : '--'} | -- Model: ${(score/predictions.length*100).toFixed(1)}% + | ${acc.whalePct !== '--' ? acc.whalePct + '%' : '--'} | +${acc.publicPct !== '--' ? acc.publicPct + '%' : '--'} | ++ Model: ${acc.modelPct}% | |||||||
| Loading... | |||||||||||||
| Failed: ${err.message} | |||||||||||||
| From \\ To | ' + CHAIRS.map(c => `\u2192${c} | `).join('') + '||||||||||||