add semi-win scoring (0.5 pts for 2nd pick) and whale/public bet recommendations
This commit is contained in:
48
app/db.py
48
app/db.py
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user