alphax/app/web/routes_strategy.py
2026-05-29 10:09:30 +08:00

134 lines
5.0 KiB
Python

from fastapi import APIRouter, Cookie
from app.config.config_loader import get_meta
from app.db import auth_db
from app.db.review_queries import (
backfill_strategy_failure_patterns,
dry_run_strategy_candidate_performance,
generate_candidates_from_review_history,
get_strategy_failure_patterns,
get_strategy_insights,
get_strategy_iteration_dashboard,
get_strategy_rule_candidates,
refresh_strategy_candidate_performance,
)
from app.db.strategy_insights import get_strategy_evaluation
from app.services.llm_insights import get_latest_review_memo
from app.db.schema import get_conn
from app.db.altcoin_db import _derive_execution_fields
from app.web.shared import require_api_user_with_subscription
router = APIRouter()
@router.get("/api/versions")
async def api_versions(view: str = "active", altcoin_session: str = Cookie(default="")):
require_api_user_with_subscription(altcoin_session)
conn = get_conn()
rows = conn.execute(
"""
SELECT r.* FROM recommendation r
JOIN (
SELECT symbol, strategy_version, MAX(id) AS max_id
FROM recommendation
WHERE status='active' AND strategy_version IS NOT NULL AND strategy_version != ''
GROUP BY symbol, strategy_version
) latest ON latest.max_id = r.id
ORDER BY r.strategy_version DESC, r.rec_time DESC
"""
).fetchall()
conn.close()
counts = {}
for row in rows:
item = dict(row)
_derive_execution_fields(item)
version = str(item.get("strategy_version") or "").strip()
if not version:
continue
status = item.get("execution_status") or "observe"
if view == "active" and status not in ("buy_now", "wait_pullback"):
continue
if view == "watch" and status != "observe":
continue
counts[version] = counts.get(version, 0) + 1
versions = [{"version": version, "count": count} for version, count in counts.items()]
current_version = str(get_meta().get("strategy_version") or "").strip()
if current_version and current_version not in counts:
versions.append({"version": current_version, "count": 0})
def _version_key(v):
try:
return tuple(int(p) for p in v["version"].lstrip("v").split("."))
except Exception:
return (0,)
versions.sort(key=_version_key, reverse=True)
return versions
@router.get("/api/strategy/insights")
async def api_strategy_insights(altcoin_session: str = Cookie(default="")):
require_api_user_with_subscription(altcoin_session)
return get_strategy_insights()
@router.get("/api/strategy/evaluation")
async def api_strategy_evaluation(days: int = 30, altcoin_session: str = Cookie(default="")):
require_api_user_with_subscription(altcoin_session)
return get_strategy_evaluation(days=days)
@router.get("/api/strategy/lifecycle")
async def api_strategy_lifecycle(days: int = 30, altcoin_session: str = Cookie(default="")):
require_api_user_with_subscription(altcoin_session)
data = get_strategy_iteration_dashboard(days=days)
data["llm_review_memo"] = get_latest_review_memo()
return data
@router.get("/api/iterations")
async def api_iterations(limit: int = 30, altcoin_session: str = Cookie(default="")):
require_api_user_with_subscription(altcoin_session)
conn = get_conn()
rows = conn.execute("SELECT * FROM strategy_iteration_log ORDER BY id DESC LIMIT %s", (limit,)).fetchall()
conn.close()
return [dict(r) for r in rows]
@router.get("/api/strategy/candidates")
async def api_strategy_candidates(limit: int = 50, status: str = "", altcoin_session: str = Cookie(default="")):
require_api_user_with_subscription(altcoin_session)
return get_strategy_rule_candidates(limit=limit, status=status or None)
@router.get("/api/strategy/failures")
async def api_strategy_failures(limit: int = 50, altcoin_session: str = Cookie(default="")):
require_api_user_with_subscription(altcoin_session)
return get_strategy_failure_patterns(limit=limit)
@router.post("/api/strategy/candidates/refresh")
async def api_strategy_candidates_refresh(altcoin_session: str = Cookie(default="")):
require_api_user_with_subscription(altcoin_session)
return {"updated": refresh_strategy_candidate_performance()}
@router.get("/api/strategy/candidates/dry-run")
async def api_strategy_candidates_dry_run(altcoin_session: str = Cookie(default="")):
require_api_user_with_subscription(altcoin_session)
return dry_run_strategy_candidate_performance()
@router.post("/api/strategy/failures/backfill")
async def api_strategy_failures_backfill(dry_run: bool = False):
return backfill_strategy_failure_patterns(dry_run=dry_run)
@router.post("/api/strategy/candidates/generate-history")
async def api_strategy_candidates_generate_history(dry_run: bool = False):
result = generate_candidates_from_review_history(dry_run=dry_run)
if not dry_run:
result["refreshed"] = refresh_strategy_candidate_performance()
return result