From c2e5a73aba187f920dd17b31ca997a8d28e05607 Mon Sep 17 00:00:00 2001
From: aaron <>
Date: Mon, 18 May 2026 12:44:53 +0800
Subject: [PATCH] 1
---
app/db/analytics.py | 33 ++++----
static/app.html | 22 +++---
static/auth.html | 31 +++++---
static/base.html | 12 +--
static/index.html | 2 +-
static/subscription.html | 4 +-
tests/test_recommendation_archive_filter.py | 85 +++++++++++++++++++++
7 files changed, 143 insertions(+), 46 deletions(-)
diff --git a/app/db/analytics.py b/app/db/analytics.py
index 5b70a87..9a5e479 100644
--- a/app/db/analytics.py
+++ b/app/db/analytics.py
@@ -186,19 +186,24 @@ def get_observation_candidates(limit=50):
}
-def _archive_filter_where(archive_filter):
+def _decision_archive_where(archive_filter):
archive_filter = str(archive_filter or "").strip()
+ executed_where = "EXISTS (SELECT 1 FROM paper_trades ptf WHERE ptf.recommendation_id = recommendation.id)"
+ invalid_where = """
+ NOT EXISTS (SELECT 1 FROM paper_trades ptf WHERE ptf.recommendation_id = recommendation.id)
+ AND (
+ status IN ('expired','invalid','archived','stopped_out')
+ OR COALESCE(execution_status, '') = 'invalid'
+ )
+ """
if archive_filter == "executed":
- return " AND EXISTS (SELECT 1 FROM paper_trades ptf WHERE ptf.recommendation_id = recommendation.id)"
+ # 已执行口径以 paper trading 账本为准。正在持仓中的模拟交易,
+ # 其 recommendation 仍可能是 active/watch_pool,不能被归档条件挡掉。
+ return executed_where
if archive_filter == "invalid":
- return """
- AND NOT EXISTS (SELECT 1 FROM paper_trades ptf WHERE ptf.recommendation_id = recommendation.id)
- AND (
- status IN ('expired','invalid','archived','stopped_out')
- OR COALESCE(execution_status, '') = 'invalid'
- )
- """
- return ""
+ return f"({invalid_where})"
+ # “全部”只展示归档口径:已执行 + 失效。
+ return f"(({executed_where}) OR ({invalid_where}))"
def _attach_paper_trade(item):
@@ -251,9 +256,7 @@ def get_all_recommendations(limit=50, decision_only=False, version="", offset=0,
except Exception:
offset = 0
- archive_where = "(status != 'active' OR COALESCE(display_bucket, '') = 'history' OR COALESCE(execution_status, '') IN ('invalid','completed'))"
- archive_filter_where = _archive_filter_where(archive_filter)
- filtered_archive_where = archive_where + archive_filter_where
+ filtered_archive_where = _decision_archive_where(archive_filter)
version_where = " AND strategy_version=%s" if version else ""
params = [version] if version else []
@@ -309,7 +312,7 @@ def get_all_recommendations(limit=50, decision_only=False, version="", offset=0,
SELECT symbol, MAX(id) AS max_id
FROM recommendation
WHERE """
- + archive_where
+ + filtered_archive_where
+ version_where
+ """
GROUP BY symbol
@@ -342,7 +345,7 @@ def get_all_recommendations(limit=50, decision_only=False, version="", offset=0,
SELECT symbol, MAX(id) AS max_id
FROM recommendation
WHERE """
- + archive_where
+ + filtered_archive_where
+ """
GROUP BY symbol
) latest ON latest.max_id = r.id
diff --git a/static/app.html b/static/app.html
index e496e47..46f74c0 100644
--- a/static/app.html
+++ b/static/app.html
@@ -1,5 +1,5 @@
{% extends "base.html" %}
-{% block title %}AlphaX Agent — 看板{% endblock %}
+{% block title %}AlphaX Agent — 机会总览{% endblock %}
{% block extra_head_css %}
@@ -285,11 +285,11 @@
{% block content %}
-
+
-
-
+
+
@@ -589,7 +589,7 @@ function renderLiveCards(data, weakCount) {
var items = Array.isArray(data) ? data : [];
if (!items.length) {
var weakOnly = weakCount ? '
'; return; }
var cardsHtml = historyItems.map(function(r,idx) {
var base = (r.symbol||'').replace('/USDT',''), outcome = historyOutcome(r);
var paper = r.paper_trade || null;
@@ -1000,7 +1000,7 @@ async function loadHistoryRecommendations(reset) {
var outcomeDetail = outcome.detail;
return '