127 lines
4.7 KiB
Python
127 lines
4.7 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.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/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 ?", (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
|