From c3881af708bf95f4186b1b62da2628596410dfdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=80=BB=E7=AE=A1?= Date: Wed, 9 Sep 2026 17:24:49 +0800 Subject: [PATCH] fix(HEL-356): preserve derived limit streaks Co-authored-by: multica-agent --- backend/data/providers/tushare_daily.py | 11 +++++- backend/data/providers/tushare_dashboard.py | 12 +++++- config/architecture-inventory.json | 14 +++---- tests/test_hel494_regressions.py | 44 +++++++++++++++++++++ 4 files changed, 71 insertions(+), 10 deletions(-) diff --git a/backend/data/providers/tushare_daily.py b/backend/data/providers/tushare_daily.py index 704b2a7..4b9a267 100644 --- a/backend/data/providers/tushare_daily.py +++ b/backend/data/providers/tushare_daily.py @@ -169,7 +169,16 @@ class DailyMarketMixin: current = item.get(key) if incoming in (None, "", "--"): continue - if current in (None, "", "--", 0, 0.0): + # The daily fallback and the board pool can arrive at different + # times. Keep the stronger streak instead of freezing the + # provisional default at one or letting a stale pool lower it. + if key == "limit_times": + item[key] = max( + 1, + int(_number(current, 1)), + int(_number(incoming, 1)), + ) + elif current in (None, "", "--", 0, 0.0): item[key] = incoming merged.append(item) return merged diff --git a/backend/data/providers/tushare_dashboard.py b/backend/data/providers/tushare_dashboard.py index 19ffebb..32257f4 100644 --- a/backend/data/providers/tushare_dashboard.py +++ b/backend/data/providers/tushare_dashboard.py @@ -55,16 +55,24 @@ class DashboardMixin: if not limit_rows: limit_data_source = "derived" notices.append("涨跌停高级接口当日数据尚未更新,已使用日线数据推算。") - limit_rows = self._derive_limits(trade_date, daily) + limit_rows = self._derive_limits( + trade_date, + daily, + previous_limit_rows=previous_limit_rows, + ) except TushareError as exc: limit_data_source = "derived" notices.append(f"涨跌停高级接口不可用,已使用日线数据推算:{exc}") - limit_rows = self._derive_limits(trade_date, daily) previous_daily = self._load_daily(previous_trade_date) previous_limit_rows = [ row for row in self._derive_limits(previous_trade_date, previous_daily) if row.get("limit_type") == "U" ] + limit_rows = self._derive_limits( + trade_date, + daily, + previous_limit_rows=previous_limit_rows, + ) up_rows = [row for row in limit_rows if row.get("limit_type") == "U"] down_rows = [row for row in limit_rows if row.get("limit_type") == "D"] diff --git a/config/architecture-inventory.json b/config/architecture-inventory.json index 4a2b663..129b7ca 100644 --- a/config/architecture-inventory.json +++ b/config/architecture-inventory.json @@ -498,8 +498,8 @@ }, { "path": "backend/data/providers/tushare_dashboard.py", - "bytes": 34777, - "lines": 797 + "bytes": 35001, + "lines": 805 }, { "path": "database.py", @@ -601,6 +601,11 @@ "bytes": 9876, "lines": 224 }, + { + "path": "backend/data/providers/tushare_daily.py", + "bytes": 9539, + "lines": 241 + }, { "path": "backend/features/market/insights_themes.py", "bytes": 9348, @@ -611,11 +616,6 @@ "bytes": 9139, "lines": 199 }, - { - "path": "backend/data/providers/tushare_daily.py", - "bytes": 9076, - "lines": 232 - }, { "path": "backend/data/providers/tushare_dragon_tiger.py", "bytes": 9059, diff --git a/tests/test_hel494_regressions.py b/tests/test_hel494_regressions.py index d9035b6..185ade1 100644 --- a/tests/test_hel494_regressions.py +++ b/tests/test_hel494_regressions.py @@ -80,6 +80,50 @@ class LimitOverlayTests(unittest.TestCase): self.assertEqual(rows[0]["fd_amount"], 82000000) self.assertEqual(rows[0]["turnover_ratio"], 18.4) + def test_overlay_replaces_provisional_first_board_with_observed_streak(self) -> None: + mixin = DailyMarketMixin() + mixin._load_limit_lists = lambda trade_date: [] + mixin.try_limit_pool = lambda trade_date: [ + {"ts_code": "000737.SZ", "limit_times": 3} + ] + + rows = mixin._overlay_board_fields( + [{"ts_code": "000737.SZ", "limit_times": 1, "limit_type": "U"}], + "20260909", + ) + + self.assertEqual(rows[0]["limit_times"], 3) + + def test_daily_fallback_extends_yesterday_streak(self) -> None: + mixin = DailyMarketMixin() + mixin.query = lambda *args, **kwargs: [] + mixin._load_limit_lists = lambda trade_date: [] + + rows = mixin._derive_limits( + "20260909", + [ + { + "ts_code": "000737.SZ", + "trade_date": "20260909", + "close": 11.0, + "high": 11.0, + "pct_chg": 10.0, + "amount": 100000, + } + ], + price_limits=[ + {"ts_code": "000737.SZ", "up_limit": 11.0, "down_limit": 9.0} + ], + basic_rows=[ + {"ts_code": "000737.SZ", "name": "北方铜业", "industry": "有色"} + ], + previous_limit_rows=[ + {"ts_code": "000737.SZ", "limit_times": 2} + ], + ) + + self.assertEqual(rows[0]["limit_times"], 3) + class ShenwanRealtimeSourceTests(unittest.TestCase): def test_transport_refuses_rt_sw_k(self) -> None: