feat(lotto): backtest API 라우터 + main 등록
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -47,6 +47,7 @@ from .weight_evolver import (
|
|||||||
from .routers import curator as curator_router
|
from .routers import curator as curator_router
|
||||||
from .routers import briefing as briefing_router
|
from .routers import briefing as briefing_router
|
||||||
from .routers import review as review_router
|
from .routers import review as review_router
|
||||||
|
from .routers import backtest as backtest_router
|
||||||
from .jobs.grade_weekly_review import run_for_latest as grade_run_for_latest
|
from .jobs.grade_weekly_review import run_for_latest as grade_run_for_latest
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
@@ -54,6 +55,7 @@ install_access_log(app)
|
|||||||
app.include_router(curator_router.router)
|
app.include_router(curator_router.router)
|
||||||
app.include_router(briefing_router.router)
|
app.include_router(briefing_router.router)
|
||||||
app.include_router(review_router.router)
|
app.include_router(review_router.router)
|
||||||
|
app.include_router(backtest_router.router)
|
||||||
scheduler = BackgroundScheduler(timezone=os.getenv("TZ", "Asia/Seoul"))
|
scheduler = BackgroundScheduler(timezone=os.getenv("TZ", "Asia/Seoul"))
|
||||||
|
|
||||||
ALL_URL = os.getenv("LOTTO_ALL_URL", "https://smok95.github.io/lotto/results/all.json")
|
ALL_URL = os.getenv("LOTTO_ALL_URL", "https://smok95.github.io/lotto/results/all.json")
|
||||||
|
|||||||
30
lotto/app/routers/backtest.py
Normal file
30
lotto/app/routers/backtest.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
from fastapi import APIRouter, BackgroundTasks, Query
|
||||||
|
from .. import backtest, db
|
||||||
|
|
||||||
|
router = APIRouter(prefix="/api/lotto/backtest", tags=["backtest"])
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/track-record")
|
||||||
|
def track_record():
|
||||||
|
return backtest.track_record()
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/calibration")
|
||||||
|
def calibration(weeks: int = Query(52, ge=1, le=520)):
|
||||||
|
return {"history": db.get_calibration_history(limit=weeks)}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/review/{draw_no}")
|
||||||
|
def review(draw_no: int):
|
||||||
|
return backtest.build_review_payload(draw_no)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/run-forward")
|
||||||
|
def run_forward(draw_no: int = Query(...), k: int = 5000, pool_n: int = 20000):
|
||||||
|
return backtest.run_forward_purchase(draw_no=draw_no, k=k, pool_n=pool_n)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/backfill")
|
||||||
|
def backfill(background_tasks: BackgroundTasks, batch: int = 50, sample_m: int = 2000):
|
||||||
|
background_tasks.add_task(backtest.backfill_calibration, batch, sample_m)
|
||||||
|
return {"ok": True, "message": f"backfill 시작 (batch={batch})"}
|
||||||
23
lotto/tests/test_backtest_api.py
Normal file
23
lotto/tests/test_backtest_api.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import os, sys, tempfile
|
||||||
|
|
||||||
|
# _shared lives in web-backend/_shared; add the parent dir so it can be found
|
||||||
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", ".."))
|
||||||
|
|
||||||
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
|
def _client(monkeypatch):
|
||||||
|
tmp = tempfile.mkdtemp()
|
||||||
|
from app import db
|
||||||
|
monkeypatch.setattr(db, "DB_PATH", os.path.join(tmp, "lotto.db"))
|
||||||
|
db.init_db()
|
||||||
|
from app.main import app
|
||||||
|
return TestClient(app), db
|
||||||
|
|
||||||
|
def test_backtest_endpoints(monkeypatch):
|
||||||
|
client, db = _client(monkeypatch)
|
||||||
|
r = client.get("/api/lotto/backtest/track-record")
|
||||||
|
assert r.status_code == 200
|
||||||
|
assert "by_strategy" in r.json()
|
||||||
|
r2 = client.get("/api/lotto/backtest/calibration?weeks=4")
|
||||||
|
assert r2.status_code == 200
|
||||||
|
assert isinstance(r2.json().get("history"), list)
|
||||||
Reference in New Issue
Block a user