feat(lotto): evaluate_weekly 학습 신호를 forward lift로 승격
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -294,6 +294,35 @@ def evaluate_weekly() -> Dict[str, Any]:
|
|||||||
|
|
||||||
winner = max(per_day, key=lambda d: d["avg_score"])
|
winner = max(per_day, key=lambda d: d["avg_score"])
|
||||||
|
|
||||||
|
# 자가학습 강화: backtest forward 등수점수 lift로 winner 재선정
|
||||||
|
latest_no = latest["drw_no"]
|
||||||
|
runs = db.get_backtest_runs(draw_no=latest_no)
|
||||||
|
engine_runs = [r for r in runs if r["strategy"] == "engine_w"]
|
||||||
|
null_runs = [r for r in runs if r["strategy"] == "random_null"]
|
||||||
|
if engine_runs and null_runs:
|
||||||
|
random_score = prize_score_from_hist(null_runs[0])
|
||||||
|
per_w = []
|
||||||
|
for r in engine_runs:
|
||||||
|
per_w.append({
|
||||||
|
"trial_id": r["trial_id"],
|
||||||
|
"day_of_week": int(r["weight_label"][1:]) if r["weight_label"].startswith("w") else 0,
|
||||||
|
"weight": json.loads(r["weight_json"]) if r["weight_json"] else DEFAULT_UNIFORM[:],
|
||||||
|
"prize_score": prize_score_from_hist(r),
|
||||||
|
})
|
||||||
|
lift_winner = select_winner_by_lift(per_w, random_score=random_score)
|
||||||
|
if not lift_winner["gated"]:
|
||||||
|
winner = {
|
||||||
|
"trial_id": lift_winner["trial_id"],
|
||||||
|
"day_of_week": lift_winner["day_of_week"],
|
||||||
|
"weight": lift_winner["weight"],
|
||||||
|
"avg_score": winner["avg_score"],
|
||||||
|
"max_correct": winner["max_correct"],
|
||||||
|
"lift": lift_winner["lift"],
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
# 노이즈 → base 유지 강제 (max_correct를 0으로 낮춰 unchanged 유도)
|
||||||
|
winner = {**winner, "max_correct": min(winner["max_correct"], 2), "lift": lift_winner["lift"]}
|
||||||
|
|
||||||
current_base = db.get_current_base()
|
current_base = db.get_current_base()
|
||||||
new_base, reason = decide_base_update(
|
new_base, reason = decide_base_update(
|
||||||
winner_max_correct=winner["max_correct"],
|
winner_max_correct=winner["max_correct"],
|
||||||
|
|||||||
@@ -135,3 +135,10 @@ def test_select_winner_by_lift_gating():
|
|||||||
winner2 = we.select_winner_by_lift(per_w, random_score=3.0, epsilon=2.0)
|
winner2 = we.select_winner_by_lift(per_w, random_score=3.0, epsilon=2.0)
|
||||||
assert winner2["gated"] is False
|
assert winner2["gated"] is False
|
||||||
assert winner2["trial_id"] == 2 # prize 9 → lift +6
|
assert winner2["trial_id"] == 2 # prize 9 → lift +6
|
||||||
|
|
||||||
|
|
||||||
|
def test_prize_score_from_hist():
|
||||||
|
# 등수 가중치: 1등 매우 큼, 하위는 작게
|
||||||
|
s = we.prize_score_from_hist({"m3": 10, "m4": 2, "m5": 0, "m6": 0, "bonus_hits": 0})
|
||||||
|
s_big = we.prize_score_from_hist({"m3": 0, "m4": 0, "m5": 0, "m6": 1, "bonus_hits": 0})
|
||||||
|
assert s_big > s # 1등 1장이 5등 다수보다 큼
|
||||||
|
|||||||
Reference in New Issue
Block a user