fix(HEL-490): 真实装配接通中枢并收编估值晚间复核
把 query/行情钩子绑到内层 TushareClient,图表接受不完整日K窗口;收编现网 HEL-423 未提交的估值复核,避免换版丢掉。 Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
co-authored by
Cursor
multica-agent
parent
1c2f2ac057
commit
c8a9376adb
@@ -511,6 +511,95 @@ class DatahubBridgeTests(unittest.TestCase):
|
||||
self.assertEqual(chart[-1]["close"], 10.4)
|
||||
self.assertAlmostEqual(chart[-1]["amount_billion"], 0.024)
|
||||
|
||||
def test_try_daily_chart_keeps_usable_bars_when_coverage_incomplete(self) -> None:
|
||||
rows = [
|
||||
{
|
||||
"ts_code": "000001.SZ",
|
||||
"trade_date": "20240901",
|
||||
"open": 10.0,
|
||||
"high": 10.4,
|
||||
"low": 9.9,
|
||||
"close": 10.2,
|
||||
"volume": 100000,
|
||||
"amount": 2000000,
|
||||
},
|
||||
{
|
||||
"ts_code": "000001.SZ",
|
||||
"trade_date": "20240902",
|
||||
"open": 10.2,
|
||||
"high": 10.5,
|
||||
"low": 10.1,
|
||||
"close": 10.4,
|
||||
"volume": 120000,
|
||||
"amount": 2400000,
|
||||
},
|
||||
]
|
||||
hub = DatahubBridge(
|
||||
flags(daily=(True, False)),
|
||||
FakeClient(
|
||||
response=DatahubResponse(
|
||||
data=rows,
|
||||
meta={
|
||||
"stale": False,
|
||||
"staleness_seconds": 0,
|
||||
"incomplete": True,
|
||||
"coverage": {"complete": False, "missing_count": 127},
|
||||
"source": "tushare:daily",
|
||||
},
|
||||
)
|
||||
),
|
||||
)
|
||||
chart = hub.try_daily_chart("000001.SZ", "20240902", 90, "daily")
|
||||
self.assertIsNotNone(chart)
|
||||
self.assertEqual(chart[-1]["trade_date"], "2024-09-02")
|
||||
self.assertEqual(chart[-1]["close"], 10.4)
|
||||
|
||||
def test_gateway_tushare_assembly_binds_hooks_on_inner_client(self) -> None:
|
||||
quotes = [
|
||||
{
|
||||
"ts_code": f"{index:06d}.SZ",
|
||||
"name": f"S{index}",
|
||||
"pre_close": 10.0,
|
||||
"open": 10.0,
|
||||
"high": 10.5,
|
||||
"low": 9.8,
|
||||
"close": 10.2,
|
||||
"vol": 100.0,
|
||||
"amount": 1000.0,
|
||||
"quote_date": "20240902",
|
||||
}
|
||||
for index in range(1, 221)
|
||||
]
|
||||
hub_client = FakeClient(
|
||||
response=DatahubResponse(
|
||||
data=quotes,
|
||||
meta={"stale": False, "staleness_seconds": 0, "source": "eastmoney_clist"},
|
||||
)
|
||||
)
|
||||
gateway = build_data_gateway(
|
||||
{"tushare_token": "tok"},
|
||||
datahub_settings=flags(quotes=(True, False), daily=(True, False)),
|
||||
)
|
||||
gateway.datahub.client = hub_client
|
||||
wrapped = gateway.tushare()
|
||||
inner = wrapped._legacy
|
||||
self.assertTrue(callable(getattr(inner, "try_market_quotes", None)))
|
||||
self.assertTrue(callable(getattr(inner, "try_index_quotes", None)))
|
||||
self.assertTrue(callable(getattr(inner, "record_datahub_legacy", None)))
|
||||
self.assertIs(inner.query.__self__, wrapped)
|
||||
self.assertEqual(inner.query.__func__, wrapped.query.__func__)
|
||||
self.assertFalse(hasattr(type(inner), "try_market_quotes"))
|
||||
rows = inner.try_market_quotes("20240902")
|
||||
self.assertGreaterEqual(len(rows or []), 200)
|
||||
self.assertIn("/v1/quotes/latest", hub_client.paths)
|
||||
hub_client.response = DatahubResponse(
|
||||
data=[dict(HUB_DAILY)],
|
||||
meta={"stale": False, "staleness_seconds": 0, "source": "tushare:daily"},
|
||||
)
|
||||
daily = inner.query("daily", {"trade_date": "20240902"}, "ts_code,amount")
|
||||
self.assertEqual(daily[0]["amount"], 2000.0)
|
||||
self.assertIn("/v1/bars/daily", hub_client.paths)
|
||||
|
||||
def test_features_do_not_import_datahub_client(self) -> None:
|
||||
violations = []
|
||||
for path in (ROOT / "backend" / "features").rglob("*.py"):
|
||||
|
||||
@@ -381,6 +381,88 @@ class RealtimeDashboardTests(unittest.TestCase):
|
||||
self.assertEqual(dashboard["meta"]["quote_count"], 3)
|
||||
self.assertIn("数据中枢", dashboard["meta"]["notice"])
|
||||
|
||||
def test_gateway_dashboard_uses_bound_market_quotes(self) -> None:
|
||||
from backend.data import build_data_gateway
|
||||
from backend.data.datahub.client import DatahubResponse
|
||||
from backend.data.datahub.settings import DATASETS, DatahubSettings, DatasetFlags
|
||||
from backend.data.gateway import DataGateway
|
||||
from backend.data.providers.tushare import TushareProvider
|
||||
|
||||
quotes = [
|
||||
{
|
||||
"ts_code": item["ts_code"],
|
||||
"name": item["name"],
|
||||
"pre_close": item["pre_close"],
|
||||
"open": item["open"],
|
||||
"high": item["high"],
|
||||
"low": item["low"],
|
||||
"close": item["close"],
|
||||
"vol": item["vol"],
|
||||
"amount": item["amount"],
|
||||
"quote_date": "20260720",
|
||||
}
|
||||
for item in FREE_QUOTES
|
||||
]
|
||||
extras = [
|
||||
{
|
||||
"ts_code": f"{index:06d}.SZ",
|
||||
"name": f"X{index}",
|
||||
"pre_close": 10.0,
|
||||
"open": 10.0,
|
||||
"high": 10.2,
|
||||
"low": 9.8,
|
||||
"close": 10.1,
|
||||
"vol": 100.0,
|
||||
"amount": 1000.0,
|
||||
"quote_date": "20260720",
|
||||
}
|
||||
for index in range(10, 230)
|
||||
]
|
||||
|
||||
class QuoteHub:
|
||||
def __init__(self):
|
||||
self.calls = []
|
||||
|
||||
def quotes_latest(self, **params):
|
||||
return self.get("/v1/quotes/latest", params)
|
||||
|
||||
def get(self, path, params=None):
|
||||
self.calls.append(path)
|
||||
if path == "/v1/quotes/latest":
|
||||
return DatahubResponse(
|
||||
data=quotes + extras,
|
||||
meta={"stale": False, "staleness_seconds": 0, "source": "eastmoney_clist"},
|
||||
)
|
||||
raise AssertionError(path)
|
||||
|
||||
datasets = {name: DatasetFlags(name) for name in DATASETS}
|
||||
datasets["quotes"] = DatasetFlags("quotes", read=True, shadow=False)
|
||||
settings = DatahubSettings(base_url="http://127.0.0.1:9", token="tok", datasets=datasets)
|
||||
base = build_data_gateway({"tushare_token": "tok"}, datahub_settings=settings)
|
||||
gateway = DataGateway(
|
||||
policy=base.policy,
|
||||
quality=base.quality,
|
||||
tushare_provider=TushareProvider(
|
||||
lambda: "tok",
|
||||
client_factory=lambda token: FakeRealtimeClient(token),
|
||||
),
|
||||
ifind_provider=base.ifind_provider,
|
||||
chart_data=base.chart_data,
|
||||
realtime_observer=base.realtime_observer,
|
||||
datahub=base.datahub,
|
||||
)
|
||||
gateway.datahub.client = QuoteHub()
|
||||
wrapped = gateway.tushare()
|
||||
inner = wrapped._legacy
|
||||
inner.clock = lambda: datetime(2026, 7, 20, 10, 30, tzinfo=timezone(timedelta(hours=8)))
|
||||
inner.realtime_aggregator = FakeFreeAggregator(fail=True)
|
||||
TushareClient._realtime_reference_cache.clear()
|
||||
dashboard = wrapped.dashboard("20260720")
|
||||
self.assertEqual(dashboard["meta"]["quote_source"], "datahub")
|
||||
self.assertIn("/v1/quotes/latest", gateway.datahub.client.calls)
|
||||
self.assertTrue(callable(getattr(inner, "try_market_quotes", None)))
|
||||
self.assertFalse(hasattr(type(inner), "try_market_quotes"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user