diff --git a/backend/data/datahub/realtime_proxy.py b/backend/data/datahub/realtime_proxy.py index c5aa85e..5c758a2 100644 --- a/backend/data/datahub/realtime_proxy.py +++ b/backend/data/datahub/realtime_proxy.py @@ -22,8 +22,7 @@ class HubRealtimeProxy: except RealtimeAggregateError as exc: error = str(exc) epochs = [int(item.get("quote_time_epoch") or 0) for item in indices] - now = datetime.now().astimezone() - max_skew = 120 if now.hour >= 15 else 15 + max_skew = 120 index_consistent = bool(epochs) and max(epochs) - min(epochs) <= max_skew ready = len(indices) == 3 and index_consistent return { diff --git a/backend/data/providers/tushare_dashboard.py b/backend/data/providers/tushare_dashboard.py index b55ca7a..19ffebb 100644 --- a/backend/data/providers/tushare_dashboard.py +++ b/backend/data/providers/tushare_dashboard.py @@ -368,6 +368,11 @@ class DashboardMixin: basic = basics[0] if basics else {} capital = self._latest_capital(ts_code, reference_date) float_share = _number(capital.get("float_share")) + if not float_share and previous_close: + # The hub's canonical valuation table stores market values but not + # share counts. circ_mv (10k CNY) / price (CNY) is float shares in + # 10k shares, the same unit used by the turnover formula below. + float_share = _number(capital.get("circ_mv")) / previous_close # rt_k volume is shares; daily_basic float_share is reported in 10k shares. turnover_rate = _number(row.get("vol")) / float_share / 100 if float_share else 0 market_date = reference_date or datetime.now().astimezone().strftime("%Y%m%d") @@ -389,6 +394,13 @@ class DashboardMixin: for item in market_rows: item_capital = capital_map.get(str(item.get("ts_code") or ""), {}) item_float_share = _number(item_capital.get("float_share")) + item_previous = _number( + item.get("pre_close") + if item.get("pre_close") not in (None, "") + else item.get("previous_close") + ) + if not item_float_share and item_previous: + item_float_share = _number(item_capital.get("circ_mv")) / item_previous if item_float_share: market_turnovers.append(_number(item.get("vol")) / item_float_share / 100) market_turnover = ( diff --git a/backend/data/providers/tushare_industries.py b/backend/data/providers/tushare_industries.py index 1dca38c..bbecaeb 100644 --- a/backend/data/providers/tushare_industries.py +++ b/backend/data/providers/tushare_industries.py @@ -541,6 +541,13 @@ class ShenwanIndustryMixin: for item in valid: capital = capital_map.get(str(item.get("ts_code") or ""), {}) float_share = _number(capital.get("float_share")) + previous = _number( + item.get("pre_close") + if item.get("pre_close") not in (None, "") + else item.get("previous_close") + ) + if not float_share and previous: + float_share = _number(capital.get("circ_mv")) / previous volume = _number(item.get("vol")) if float_share and volume: # 免费源成交量为股;daily_basic.float_share 为万股。 @@ -549,6 +556,13 @@ class ShenwanIndustryMixin: for item in market_rows: capital = capital_map.get(str(item.get("ts_code") or ""), {}) float_share = _number(capital.get("float_share")) + previous = _number( + item.get("pre_close") + if item.get("pre_close") not in (None, "") + else item.get("previous_close") + ) + if not float_share and previous: + float_share = _number(capital.get("circ_mv")) / previous volume = _number(item.get("vol")) if float_share and volume: market_turnovers.append(volume / float_share / 100) diff --git a/backend/features/heaven/market_context.py b/backend/features/heaven/market_context.py index 5715e4d..359bb17 100644 --- a/backend/features/heaven/market_context.py +++ b/backend/features/heaven/market_context.py @@ -202,8 +202,7 @@ class HeavenMarketContextMixin: } if len(quotes) != 3 or quote_dates != {trade_date}: raise ValueError("腾讯三大指数日期与目标交易日不一致") - now = datetime.now().astimezone() - max_skew = 120 if now.hour >= 15 else 15 + max_skew = 120 if max(epochs) - min(epochs) > max_skew: raise ValueError(f"腾讯三大指数时间差超过{max_skew}秒") diff --git a/config/architecture-inventory.json b/config/architecture-inventory.json index 751c516..4a2b663 100644 --- a/config/architecture-inventory.json +++ b/config/architecture-inventory.json @@ -483,8 +483,8 @@ }, { "path": "backend/data/providers/tushare_industries.py", - "bytes": 37168, - "lines": 859 + "bytes": 37786, + "lines": 873 }, { "path": "backend/features/screener/catalog.py", @@ -498,8 +498,8 @@ }, { "path": "backend/data/providers/tushare_dashboard.py", - "bytes": 34082, - "lines": 785 + "bytes": 34777, + "lines": 797 }, { "path": "database.py", @@ -568,8 +568,8 @@ }, { "path": "backend/features/heaven/market_context.py", - "bytes": 14535, - "lines": 357 + "bytes": 14467, + "lines": 356 }, { "path": "frontend/shared/session.js",