2197feac33
2026-08-21 实锤(live_19/shadow_60 同卡):momentum 9:30:06 buy_sign=True 后 47min 零输出被重启;small_cap 同型(fundamentals 超时120s后沉默)。channel_test 正常(不走走filters重取数)。value_selection/all_weather 月度未到期=零成交正常。 根因(VPS离场时段探针27s跑完全路径反推):filters.filter_st/filter_new 逐只 get_security_info,每只2次xtdata RPC(_detect_instrument_type+get_instrument_detail) 且无超时——momentum 10指数×30只×2过滤=600次RPC,开盘时段QMT忙时47min+不归; provider无get_security_info_batch→filters._batch_security_info回退逐只(风暴路径)。 修法: - SanguoMiniQmtProvider.get_security_info_batch:委托内部LocalUnifiedProvider (2条本地SQL替N×2次RPC,与78d35ba get_closes_panel/get_constituent_ex同款 委托模式);失败/非dict返None→filters自动回退逐只(不崩策略);全部策略 (aw/mom/sc/vs含_ex)经共享filters即时受益,影子/回测同速 - momentum/_ex+small_cap/_ex 选股段加阶段耗时日志(池预取/宽表/过滤3段, 下个9:30若仍有卡点日志直接钉死位置) - 遗留已报issue:SanguoMiniQmtProvider无get_limit_status_batch→实盘涨跌停/ 停牌filter静默失效(涨停股照买),归数据session按本地公式+单次批量tick根治 测试:+5(委托/失败回退None/非dict回退/filters消费批量零逐只RPC×2,__new__ 绕过Mac无bullet_trade的构造raise);portfolio 380绿(13失败全=Mac缺bullet_trade 环境性,NAS CI过) [vps]
80 lines
3.6 KiB
Python
80 lines
3.6 KiB
Python
"""SanguoMiniQmtProvider.get_security_info_batch 委托测试(2026-08-21 挂死根治)。
|
|
|
|
背景:实盘 momentum/small_cap 9:30 选股挂死——filters.filter_st/filter_new 逐只
|
|
get_security_info,每只 2 次 xtdata RPC 无超时,开盘时段 600 次 RPC 47min+ 不归。
|
|
本方法委托内部 LocalUnifiedProvider 批量(2 条本地 SQL),filters 探测到即走批量。
|
|
|
|
Mac 无 bullet_trade:类定义处 MiniQMTProvider=object 兜底,可直接实例化;
|
|
_unified 用 MagicMock 替换,只测委托语义(不连本地库)。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from unittest.mock import MagicMock
|
|
|
|
from sanguo_portfolio import SanguoMiniQmtProvider
|
|
from sanguo_portfolio import filters
|
|
|
|
|
|
def _provider(unified: MagicMock) -> SanguoMiniQmtProvider:
|
|
# __new__ 绕过 __init__:Mac 无 bullet_trade 时构造直接 raise
|
|
# (RuntimeError);被测方法只用 self._unified,委托语义不依赖基类初始化。
|
|
p = object.__new__(SanguoMiniQmtProvider)
|
|
p._unified = unified
|
|
return p
|
|
|
|
|
|
class TestGetSecurityInfoBatch:
|
|
def test_delegates_to_unified(self):
|
|
unified = MagicMock(name="unified")
|
|
expected = {
|
|
"600519.XSHG": {"display_name": "贵州茅台", "start_date": "2001-08-27"},
|
|
"600036.XSHG": {"display_name": "招商银行", "start_date": "2002-04-09"},
|
|
}
|
|
unified.get_security_info_batch.return_value = expected
|
|
p = _provider(unified)
|
|
out = p.get_security_info_batch(["600519.XSHG", "600036.XSHG"])
|
|
assert out == expected
|
|
unified.get_security_info_batch.assert_called_once_with(
|
|
["600519.XSHG", "600036.XSHG"], None)
|
|
|
|
def test_unified_failure_returns_none_not_raise(self):
|
|
"""委托失败 → None(不崩策略),filters 自动回退逐只旧路径。"""
|
|
unified = MagicMock(name="unified")
|
|
unified.get_security_info_batch.side_effect = RuntimeError("db locked")
|
|
p = _provider(unified)
|
|
assert p.get_security_info_batch(["600519.XSHG"]) is None
|
|
|
|
def test_non_dict_result_returns_none(self):
|
|
unified = MagicMock(name="unified")
|
|
unified.get_security_info_batch.return_value = ["not", "a", "dict"]
|
|
p = _provider(unified)
|
|
assert p.get_security_info_batch(["600519.XSHG"]) is None
|
|
|
|
|
|
class TestFiltersConsumeBatch:
|
|
def test_filter_st_uses_batch_and_skips_per_stock_rpc(self):
|
|
"""filters 探测到批量方法 → 一次预取,绝不再逐只 get_security_info
|
|
(即挂死根因的 RPC 风暴路径)。"""
|
|
provider = MagicMock(name="provider")
|
|
provider.get_security_info_batch.return_value = {
|
|
"600519.XSHG": {"display_name": "贵州茅台", "start_date": "2001-08-27"},
|
|
"600000.XSHG": {"display_name": "ST浦发", "start_date": "1999-11-10"},
|
|
}
|
|
out = filters.filter_st_stock(["600519.XSHG", "600000.XSHG"], provider)
|
|
assert out == ["600519.XSHG"] # ST 被滤掉
|
|
provider.get_security_info_batch.assert_called_once()
|
|
provider.get_security_info.assert_not_called() # 零逐只 RPC
|
|
|
|
def test_filter_new_stock_uses_batch(self):
|
|
provider = MagicMock(name="provider")
|
|
provider.get_security_info_batch.return_value = {
|
|
"600519.XSHG": {"display_name": "贵州茅台",
|
|
"start_date": "2001-08-27"},
|
|
"301999.XSHE": {"display_name": "次新股",
|
|
"start_date": "2026-08-01"},
|
|
}
|
|
out = filters.filter_new_stock(
|
|
["600519.XSHG", "301999.XSHE"], provider, "2026-08-21", days=375)
|
|
assert out == ["600519.XSHG"] # 上市 20 天的次新被滤掉
|
|
provider.get_security_info.assert_not_called()
|