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 ? '
当前只有 '+weakCount+' 个弱观察候选,已默认收起,避免干扰主机会流。
' : ''; - $('liveCards').innerHTML = weakOnly || '

暂无实时推荐或观察候选
系统持续扫描中,有机会会实时更新

'; return; + $('liveCards').innerHTML = weakOnly || '

暂无实时机会或观察候选
系统持续扫描中,有机会会实时更新

'; return; } var order = { buy_now: 0, wait_pullback: 1, observe: 2, holding: 3, completed: 4, invalid: 9 }; items.sort(function(a,b){ @@ -748,9 +748,9 @@ function renderRecCard(r) { if (isTradePlan) { entryPlanHtml = '
入场参考'+fmtP(entryRef)+''+cleanDisplayText(entryLabel+' · '+(entryModel || '触发/计划价'))+'
风险边界'+fmtP(riskLine)+''+cleanDisplayText(stopModel)+'
上方空间'+(upsidePct?('+'+upsidePct.toFixed(1)+'%'):'--')+''+cleanDisplayText(tpModel)+' · '+fmtP(spaceRef)+'
持有阶段'+cleanDisplayText(horizon || phase.short)+''+cleanDisplayText(levelLabel)+'
'; } else { - entryPlanHtml = '
当前参考'+fmtP(price)+'不是入场价
观察重点待触发'+cleanDisplayText(entryModel || '需15m/1H当前信号')+'
绩效口径不计入未成交易推荐
观察阶段'+cleanDisplayText(horizon || '观察池候选')+''+cleanDisplayText(levelLabel)+'
'; + entryPlanHtml = '
当前参考'+fmtP(price)+'不是入场价
观察重点待触发'+cleanDisplayText(entryModel || '需15m/1H当前信号')+'
绩效口径不计入未形成交易机会
观察阶段'+cleanDisplayText(horizon || '观察池候选')+''+cleanDisplayText(levelLabel)+'
'; } - return '
'+base.slice(0,2).toUpperCase()+'
'+base+'
'+score+''+st.label+'
$'+priceFmt+''+changeHtml+'
'+decisionHtml+signalLevelHtml+onchainHtml+aiInsightHtml+'
'+(isWeakObserve ? weakNoteHtml : entryPlanHtml)+(sigHtml?'
'+sigHtml+'
':'')+'
'; + return '
'+base.slice(0,2).toUpperCase()+'
'+base+'
'+score+'机会分
$'+priceFmt+''+changeHtml+'
'+decisionHtml+signalLevelHtml+onchainHtml+aiInsightHtml+'
'+(isWeakObserve ? weakNoteHtml : entryPlanHtml)+(sigHtml?'
'+sigHtml+'
':'')+'
'; } catch (e) { console.error('renderRecCard hard fail', r && r.symbol, e); return renderLiveFallbackCard(r); @@ -918,7 +918,7 @@ function historyOutcome(r) { return { resolved: true, type: 'executed_failed', label: '执行后止损', detail: '执行样本触发风险边界' }; } if (status === 'expired' || status === 'invalid' || status === 'archived' || execution === 'invalid' || bucket === 'history') { - return { resolved: true, type: triggered ? 'executed_invalid' : 'not_executed', label: triggered ? '执行后失效' : '未执行失效', detail: triggered ? '曾进入执行态,后续失效' : '推荐/观察后未形成真实交易' }; + return { resolved: true, type: triggered ? 'executed_invalid' : 'not_executed', label: triggered ? '执行后失效' : '未执行失效', detail: triggered ? '曾进入执行态,后续失效' : '机会/观察后未形成真实交易' }; } return { resolved: false, type: 'pending', label: '仍在跟踪', detail: '尚未归档' }; } @@ -948,7 +948,7 @@ async function loadHistoryRecommendations(reset) { var notExecutedCount = Number(summary.not_executed_count || 0); $('histCount').textContent = totalCount ? ' ' + totalCount : ''; $('historyStats').innerHTML = - '
'+totalCount+'
归档信号
推荐/观察历史
'+ + '
'+totalCount+'
归档信号
机会/观察历史
'+ '
'+executedCount+'
进入执行
收益见模拟交易
'+ '
'+notExecutedCount+'
未执行归档
观察/等回踩失效
'+ '
'+invalidCount+'
信号失效
含过期/风控失效
'; @@ -962,7 +962,7 @@ async function loadHistoryRecommendations(reset) { historyOffset += completed.length; } historyHasMore = !!page.has_more; - if(!historyItems.length){ $('historyCards').innerHTML='

暂无归档推荐
推荐过期、失效或完成后会出现在这里

'; return; } + if(!historyItems.length){ $('historyCards').innerHTML='

暂无归档机会
机会过期、失效或完成后会出现在这里

'; 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 '
'+ '
'+base.slice(0,2).toUpperCase()+'
'+base+'
'+outcomeText+'
'+ - '
'+(hasPaper ? '$'+fmtN(entryP)+''+(paper.status === 'closed' ? '$'+fmtN(exitP) : '持有中')+'' : '未执行失效/归档')+'评分 '+score+' · '+st.label+''+duration+'
'+ + '
'+(hasPaper ? '$'+fmtN(entryP)+''+(paper.status === 'closed' ? '$'+fmtN(exitP) : '持有中')+'' : '未执行失效/归档')+'机会分 '+score+''+duration+'
'+ '
交易阶段'+(hasPaper ? (paper.status === 'closed' ? '已完成模拟交易' : '模拟交易持有中') : '未执行归档')+'
结果说明'+outcomeDetail+'
执行状态'+execText+'
'+ '
'+ (sigHtml?'
'+sigHtml+'
':'')+ diff --git a/static/auth.html b/static/auth.html index df7c469..156c00f 100644 --- a/static/auth.html +++ b/static/auth.html @@ -144,11 +144,6 @@ a { color: inherit; text-decoration: none; } AlphaX Agent -
- AlphaX Agent
- 提前发现机会,别在强信号后追高。登录或开启免费体验,创建账号后可前往订阅中心。 -
-
@@ -211,11 +206,10 @@ a { color: inherit; text-decoration: none; }
- +
- 前往订阅中心 diff --git a/static/base.html b/static/base.html index 4f0af83..cbc513d 100644 --- a/static/base.html +++ b/static/base.html @@ -174,24 +174,20 @@ a { color: inherit; text-decoration: none; } AlphaX Agent