add semi-win scoring (0.5 pts for 2nd pick) and whale/public bet recommendations

This commit is contained in:
2026-02-26 00:03:59 +05:00
parent 1eed8786db
commit 949d0c2a57
2 changed files with 111 additions and 30 deletions

View File

@@ -1123,7 +1123,8 @@ def _backtest_theories(winners):
return {"error": "Not enough data for backtesting"}
theories = ["base_rate", "markov_1", "markov_2", "recent_20", "streak", "combined"]
correct = {t: 0 for t in theories}
full_hits = {t: 0 for t in theories}
semi_hits = {t: 0 for t in theories}
total_tested = 0
rolling = {t: [] for t in theories} # rolling accuracy over last 200
@@ -1135,24 +1136,24 @@ def _backtest_theories(winners):
total_h = len(history)
# Base rate
base = {c: history.count(c) / total_h for c in CHAIR_LABELS}
base_pick = max(CHAIR_LABELS, key=lambda c: base[c])
base_ranked = sorted(CHAIR_LABELS, key=lambda c: base[c], reverse=True)
# Markov-1
m1, _ = _markov_matrix_1(history)
last = history[-1]
m1_probs = m1.get(last, {c: 1 / 3 for c in CHAIR_LABELS})
m1_pick = max(CHAIR_LABELS, key=lambda c: m1_probs.get(c, 0))
m1_ranked = sorted(CHAIR_LABELS, key=lambda c: m1_probs.get(c, 0), reverse=True)
# Markov-2
m2, _ = _markov_matrix_2(history)
key2 = f"{history[-2]}{history[-1]}"
m2_probs = m2.get(key2, {c: 1 / 3 for c in CHAIR_LABELS})
m2_pick = max(CHAIR_LABELS, key=lambda c: m2_probs.get(c, 0))
m2_ranked = sorted(CHAIR_LABELS, key=lambda c: m2_probs.get(c, 0), reverse=True)
# Recent-20
recent = history[-20:] if len(history) >= 20 else history
rec = {c: recent.count(c) / len(recent) for c in CHAIR_LABELS}
rec_pick = max(CHAIR_LABELS, key=lambda c: rec[c])
rec_ranked = sorted(CHAIR_LABELS, key=lambda c: rec[c], reverse=True)
# Streak
streak_chair = history[-1]
@@ -1173,7 +1174,7 @@ def _backtest_theories(winners):
streak_probs = {c: streak_probs[c] / s_total for c in CHAIR_LABELS}
else:
streak_probs = {c: 1 / 3 for c in CHAIR_LABELS}
streak_pick = max(CHAIR_LABELS, key=lambda c: streak_probs[c])
streak_ranked = sorted(CHAIR_LABELS, key=lambda c: streak_probs[c], reverse=True)
# Combined Bayesian
combined = {c: 0 for c in CHAIR_LABELS}
@@ -1182,19 +1183,28 @@ def _backtest_theories(winners):
for sig_name, weight in weights.items():
for c in CHAIR_LABELS:
combined[c] += weight * signals[sig_name].get(c, 1 / 3)
combined_pick = max(CHAIR_LABELS, key=lambda c: combined[c])
combined_ranked = sorted(CHAIR_LABELS, key=lambda c: combined[c], reverse=True)
picks = {
"base_rate": base_pick, "markov_1": m1_pick, "markov_2": m2_pick,
"recent_20": rec_pick, "streak": streak_pick, "combined": combined_pick,
ranked = {
"base_rate": base_ranked, "markov_1": m1_ranked, "markov_2": m2_ranked,
"recent_20": rec_ranked, "streak": streak_ranked, "combined": combined_ranked,
}
for t in theories:
hit = 1 if picks[t] == actual else 0
if picks[t] == actual:
correct[t] += 1
rolling[t].append(hit)
pick = ranked[t][0]
second = ranked[t][1]
if pick == actual:
full_hits[t] += 1
rolling[t].append(1.0)
elif second == actual:
semi_hits[t] += 1
rolling[t].append(0.5)
else:
rolling[t].append(0.0)
accuracy = {t: round(correct[t] / total_tested * 100, 2) if total_tested else 0 for t in theories}
accuracy = {
t: round((full_hits[t] + semi_hits[t] * 0.5) / total_tested * 100, 2) if total_tested else 0
for t in theories
}
# Rolling accuracy over last 200 games
window = 200
@@ -1212,6 +1222,8 @@ def _backtest_theories(winners):
return {
"total_tested": total_tested,
"accuracy": accuracy,
"full_hits": {t: full_hits[t] for t in theories},
"semi_hits": {t: semi_hits[t] for t in theories},
"rolling_accuracy": rolling_accuracy,
"random_baseline": 33.33,
}
@@ -1230,12 +1242,16 @@ def _last_n_predictions(winners, n=20):
m1, _ = _markov_matrix_1(history)
m2, _ = _markov_matrix_2(history)
pred, _ = _bayesian_prediction(history, m1, m2)
predicted = max(CHAIR_LABELS, key=lambda c: pred[c])
ranked = sorted(CHAIR_LABELS, key=lambda c: pred[c], reverse=True)
predicted = ranked[0]
second_predicted = ranked[1]
results.append({
"index": i,
"predicted": predicted,
"second_predicted": second_predicted,
"actual": actual,
"correct": predicted == actual,
"semi_correct": predicted != actual and second_predicted == actual,
"probs": {c: round(pred[c], 4) for c in CHAIR_LABELS},
})
return results