diff --git a/backend/app/main.py b/backend/app/main.py index 7894554..ce8aca0 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -27,6 +27,8 @@ from .db import ( save_weekly_report, get_weekly_report_list, get_weekly_report, # Phase 2: 개인 패턴 분석 get_all_recommendation_numbers, + # Phase 3: 전략 관련 + get_strategy_performance as db_get_strategy_performance, ) from .recommender import recommend_numbers, recommend_with_heatmap from .collector import sync_latest, sync_ensure_all @@ -34,6 +36,11 @@ from .generator import run_simulation, generate_smart_recommendations from .checker import check_results_for_draw from .utils import calc_metrics, calc_recent_overlap from .analyzer import get_statistical_report, generate_weekly_report, analyze_personal_patterns, generate_combined_recommendation +from .purchase_manager import check_purchases_for_draw +from .strategy_evolver import ( + get_weights_with_trend, recalculate_weights, + generate_smart_recommendation, +) app = FastAPI() scheduler = BackgroundScheduler(timezone=os.getenv("TZ", "Asia/Seoul")) @@ -257,6 +264,10 @@ class PurchaseCreate(BaseModel): sets: int = 1 prize: int = 0 note: str = "" + numbers: List[List[int]] = [] + is_real: bool = True + source_strategy: str = "manual" + source_detail: dict = {} class PurchaseUpdate(BaseModel): @@ -265,6 +276,9 @@ class PurchaseUpdate(BaseModel): sets: Optional[int] = None prize: Optional[int] = None note: Optional[str] = None + numbers: Optional[List[List[int]]] = None + is_real: Optional[bool] = None + source_strategy: Optional[str] = None @app.get("/api/lotto/purchase/stats") @@ -274,15 +288,28 @@ def api_purchase_stats(): @app.get("/api/lotto/purchase") -def api_purchase_list(draw_no: Optional[int] = None, days: Optional[int] = None): - """구매 이력 조회 (draw_no, days 필터 선택)""" - return {"records": get_purchases(draw_no=draw_no, days=days)} +def api_purchase_list(draw_no: Optional[int] = None, days: Optional[int] = None, + is_real: Optional[bool] = None, strategy: Optional[str] = None): + """구매 이력 조회 (필터: draw_no, days, is_real, strategy)""" + return {"records": get_purchases(draw_no=draw_no, days=days, is_real=is_real, strategy=strategy)} @app.post("/api/lotto/purchase", status_code=201) def api_purchase_create(body: PurchaseCreate): - """구매 이력 추가""" - return add_purchase(body.draw_no, body.amount, body.sets, body.prize, body.note) + """구매 이력 추가 (실제/가상)""" + sets = body.sets if body.sets > 0 else max(len(body.numbers), 1) + amount = body.amount if body.amount > 0 else sets * 1000 + return add_purchase( + draw_no=body.draw_no, + amount=amount, + sets=sets, + prize=body.prize, + note=body.note, + numbers=body.numbers, + is_real=body.is_real, + source_strategy=body.source_strategy, + source_detail=body.source_detail, + ) @app.put("/api/lotto/purchase/{purchase_id}") @@ -302,6 +329,40 @@ def api_purchase_delete(purchase_id: int): return {"ok": True} +# ── 전략 진화 API ────────────────────────────────────────────────────────── + +@app.get("/api/lotto/strategy/weights") +def api_strategy_weights(): + """현재 전략별 가중치 + 성과 요약 + trend""" + return get_weights_with_trend() + + +@app.get("/api/lotto/strategy/performance") +def api_strategy_performance(strategy: Optional[str] = None, days: Optional[int] = None): + """전략별 회차 성과 이력 (차트용)""" + rows = db_get_strategy_performance(strategy=strategy, days=days) + return {"records": rows} + + +@app.post("/api/lotto/strategy/evolve") +def api_strategy_evolve(): + """수동 가중치 재계산 트리거""" + new_weights = recalculate_weights() + return {"ok": True, "weights": new_weights} + + +# ── 스마트 추천 API ──────────────────────────────────────────────────────── + +@app.get("/api/lotto/recommend/smart") +def api_recommend_smart(sets: int = 5): + """전략 가중치 기반 메타 전략 추천""" + sets = max(1, min(sets, 10)) + result = generate_smart_recommendation(sets=sets) + if "error" in result: + raise HTTPException(status_code=500, detail=result["error"]) + return result + + # ── 통계 분석 리포트 ──────────────────────────────────────────────────────── @app.get("/api/lotto/analysis") def api_analysis():