diff --git a/backend/src/lhbfx/queries.py b/backend/src/lhbfx/queries.py
index a793fe2..d52efb6 100644
--- a/backend/src/lhbfx/queries.py
+++ b/backend/src/lhbfx/queries.py
@@ -354,8 +354,7 @@ def fetch_trader_detail(trader_id: int) -> dict[str, Any]:
)
seats = [_normalize_row(row) for row in cursor.fetchall()]
- cursor.execute(
- """
+ stock_query = """
SELECT
d.stock_code,
MAX(COALESCE(o.stock_name, d.stock_name)) AS stock_name,
@@ -365,22 +364,120 @@ def fetch_trader_detail(trader_id: int) -> dict[str, Any]:
MAX(d.trade_date) AS last_trade_date,
SUM(CASE WHEN CAST(COALESCE(NULLIF(d.buy_amount_wan, ''), '0') AS DECIMAL(18,2)) > 0 THEN 1 ELSE 0 END) AS buy_action_count,
SUM(CASE WHEN CAST(COALESCE(NULLIF(d.sell_amount_wan, ''), '0') AS DECIMAL(18,2)) > 0 THEN 1 ELSE 0 END) AS sell_action_count,
+ SUM(CAST(COALESCE(NULLIF(d.net_amount_wan, ''), '0') AS DECIMAL(18,2))) AS total_net_amount_wan,
MAX(CASE WHEN w.warning_type = 'sell_alert' THEN 1 ELSE 0 END) AS has_sell_alert,
- MAX(CASE WHEN w.warning_type = 'slow_exit_watch' THEN 1 ELSE 0 END) AS has_slow_exit
+ MAX(CASE WHEN w.warning_type = 'slow_exit_watch' THEN 1 ELSE 0 END) AS has_slow_exit,
+ MAX(s.industry) AS industry,
+ MAX(s.market) AS market,
+ MAX(s.total_market_value) AS total_market_value,
+ MAX(s.circulating_market_value) AS circulating_market_value
FROM lhb_detail_seats d
LEFT JOIN lhb_overview o
ON o.stock_code = d.stock_code AND o.trade_date = d.trade_date
LEFT JOIN warning_events w
ON w.stock_code = d.stock_code AND w.trader_name = d.matched_trader_name
+ LEFT JOIN stocks s
+ ON s.stock_code = d.stock_code
WHERE d.matched_trader_name = %s
GROUP BY d.stock_code
ORDER BY last_trade_date DESC, action_count DESC
LIMIT 100
- """,
- (trader_name,),
- )
+ """
+
+ cursor.execute(stock_query, (trader_name,))
stocks = [_normalize_row(row) for row in cursor.fetchall()]
+ stock_codes = [row["stock_code"] for row in stocks if row.get("stock_code")]
+ increasing_by_stock: dict[str, bool] = {}
+ if stock_codes:
+ placeholders = ", ".join(["%s"] * len(stock_codes))
+ cursor.execute(
+ f"""
+ SELECT
+ stock_code,
+ trade_date,
+ SUM(CAST(COALESCE(NULLIF(net_amount_wan, ''), '0') AS DECIMAL(18,2))) AS daily_net_amount_wan
+ FROM lhb_detail_seats
+ WHERE matched_trader_name = %s
+ AND stock_code IN ({placeholders})
+ AND trade_date IS NOT NULL
+ GROUP BY stock_code, trade_date
+ ORDER BY stock_code, trade_date
+ """,
+ (trader_name, *stock_codes),
+ )
+ net_history_by_stock: dict[str, list[float]] = {}
+ for row in cursor.fetchall():
+ stock_code = row["stock_code"]
+ net_history_by_stock.setdefault(stock_code, []).append(float(row["daily_net_amount_wan"] or 0))
+
+ for stock_code, net_history in net_history_by_stock.items():
+ increasing_by_stock[stock_code] = len(net_history) >= 2 and all(
+ current > previous for previous, current in zip(net_history, net_history[1:])
+ )
+
+ for stock in stocks:
+ stock["is_net_amount_increasing"] = increasing_by_stock.get(stock["stock_code"], False)
+
+ missing_codes = [
+ row["stock_code"]
+ for row in stocks[:30]
+ if not row.get("industry") or not row.get("market") or row.get("total_market_value") is None
+ ]
+ if missing_codes:
+ eastmoney = EastMoneyClient()
+ seen_codes: set[str] = set()
+ for stock_code in missing_codes:
+ if stock_code in seen_codes:
+ continue
+ seen_codes.add(stock_code)
+ profile: dict[str, Any] = {}
+ snapshot: dict[str, Any] = {}
+ try:
+ profile = eastmoney.fetch_company_profile(stock_code)
+ except Exception:
+ profile = {}
+ try:
+ snapshot = eastmoney.fetch_quote_snapshot(stock_code)
+ except Exception:
+ snapshot = {}
+
+ cursor.execute(
+ """
+ INSERT INTO stocks (
+ stock_code,
+ stock_name,
+ market,
+ industry,
+ concept_tags,
+ total_market_value,
+ circulating_market_value
+ )
+ VALUES (%s, %s, %s, %s, %s, %s, %s)
+ ON DUPLICATE KEY UPDATE
+ stock_name = COALESCE(NULLIF(VALUES(stock_name), ''), stock_name),
+ market = COALESCE(NULLIF(VALUES(market), ''), market),
+ industry = COALESCE(NULLIF(VALUES(industry), ''), industry),
+ concept_tags = COALESCE(VALUES(concept_tags), concept_tags),
+ total_market_value = COALESCE(VALUES(total_market_value), total_market_value),
+ circulating_market_value = COALESCE(VALUES(circulating_market_value), circulating_market_value)
+ """,
+ (
+ stock_code,
+ profile.get("stock_name") or (snapshot.get("stock_name") if snapshot else None) or stock_code,
+ profile.get("market"),
+ profile.get("industry") or snapshot.get("industry"),
+ json.dumps(profile.get("concept_tags") or [], ensure_ascii=False) if profile.get("concept_tags") else None,
+ snapshot.get("total_market_value"),
+ snapshot.get("circulating_market_value"),
+ ),
+ )
+
+ cursor.execute(stock_query, (trader_name,))
+ stocks = [_normalize_row(row) for row in cursor.fetchall()]
+ for stock in stocks:
+ stock["is_net_amount_increasing"] = increasing_by_stock.get(stock["stock_code"], False)
+
cursor.execute(
"""
SELECT trade_date, stock_code, stock_name, warning_type, warning_level, trigger_reason
diff --git a/docs/技术文档.md b/docs/技术文档.md
index c8ce7b7..b82e34b 100644
--- a/docs/技术文档.md
+++ b/docs/技术文档.md
@@ -140,7 +140,7 @@ longhubang/
### 4.3 页面组件
- `HomeControlScreen.vue`:首页总控台
-- `TraderDetailScreen.vue`:游资详情
+- `TraderDetailScreen.vue`:游资详情,采用单列全宽股票列表,支持时间、名称、净额连续增大筛选,并展示行业、板块、总市值、净额和预警标签
- `StockDetailScreen.vue`:个股详情
- `WarningCenterScreen.vue`:预警中心
diff --git a/docs/需求文档.md b/docs/需求文档.md
index bf3111a..5b67870 100644
--- a/docs/需求文档.md
+++ b/docs/需求文档.md
@@ -64,9 +64,11 @@
游资详情页支持:
- 游资档案
-- 核心席位
-- 近期参与股票
- 风格标签
+- 股票列表全宽展示
+- 时间、名称、净额连续增大筛选
+- 按时间、净额、动作数排序
+- 行业、上市板块、总市值、净额和预警标签展示
### 2.6 预警中心
diff --git a/docs/需求梳理-今日游资操作优先.md b/docs/需求梳理-今日游资操作优先.md
index 593232f..f203425 100644
--- a/docs/需求梳理-今日游资操作优先.md
+++ b/docs/需求梳理-今日游资操作优先.md
@@ -144,9 +144,11 @@
游资详情页保留,但不再作为首页首要信息。
-游资详情页用于查看某个游资的长期参与情况、席位信息、风格标签和近期参与股票。
+游资详情页用于查看某个游资的长期参与情况、风格标签和近期参与股票。
已移除“动作时间线”模块。
+已移除“净额重点”和“席位概览”侧栏,股票列表改为单列全宽展示。
+股票列表支持按时间、名称、净额连续增大筛选,并支持按时间、净额、动作数排序。
原因:
@@ -206,3 +208,4 @@
6. 首页顶部指标与关注列表联动,取消关注后自动剔除统计。
7. 所有股票入口都能跳转到股票详情。
8. 游资详情页去掉动作时间线。
+9. 游资详情页去掉净额重点和席位概览侧栏,保留单列股票列表和净额趋势筛选。
diff --git a/frontend/src/components/TraderDetailScreen.vue b/frontend/src/components/TraderDetailScreen.vue
index 842036d..e494702 100644
--- a/frontend/src/components/TraderDetailScreen.vue
+++ b/frontend/src/components/TraderDetailScreen.vue
@@ -1,8 +1,8 @@
@@ -48,7 +95,7 @@ const summaryCards = computed(() => {
02 Trader Detail
- 游资详情页
+ 游资详情
· {{ traderDetail.trader.name }}
@@ -67,76 +114,113 @@ const summaryCards = computed(() => {
-