diff --git a/lotto/app/weight_evolver.py b/lotto/app/weight_evolver.py index c5bf308..2ff91fb 100644 --- a/lotto/app/weight_evolver.py +++ b/lotto/app/weight_evolver.py @@ -294,6 +294,35 @@ def evaluate_weekly() -> Dict[str, Any]: 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() new_base, reason = decide_base_update( winner_max_correct=winner["max_correct"], diff --git a/lotto/tests/test_weight_evolver.py b/lotto/tests/test_weight_evolver.py index 3ac3f0e..2d642f5 100644 --- a/lotto/tests/test_weight_evolver.py +++ b/lotto/tests/test_weight_evolver.py @@ -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) assert winner2["gated"] is False 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등 다수보다 큼