feat(live): 引擎启动期行情腿自检quote_preflight——09-03行情事故定位件:momentum 9:30六笔卖单fail-closed忽略+18/22探针零单,引擎进程内取价失败但独立探针.SH格式全通(dev反证,我先前的「服务死」误判收回:get_full_tick须.SH后缀+subscribe须xtdata.run(),两个坑全踩);需进程内直接证据区分「引擎会话坏vs服务坏」:boot时broker交易腿连接后一笔get_full_tick(510500.SH)留痕OK/EMPTY/EXC,永不抛不阻断;今晚23:05轮换后新进程即出证据,明早9:30三灯(momentum重试/small_cap调仓/9:35探针)前置诊断;3新测+591绿 [vps]
CI/CD / test (push) Successful in 1s
CI/CD / nas-deploy (push) Successful in 9s
CI/CD / nas-verify (push) Successful in 14s

This commit is contained in:
2026-09-03 13:51:26 +08:00
parent 37d8f85d21
commit 029ea34aec
2 changed files with 56 additions and 0 deletions
+28
View File
@@ -42,6 +42,32 @@ CONNECT_RETRY_ATTEMPTS = 5
CONNECT_RETRY_WAIT_SEC = 10.0
PREFLIGHT_QUOTE_CODE = "510500.SH" # QMT 后缀格式;聚宽 .XSHG 传入返回空(09-03 踩坑)
def quote_preflight(_fetch: Any = None) -> str:
"""启动期行情腿自检:进程内 get_full_tick 一笔,返回可读结论字符串。
2026-09-03 行情腿事故定位用:引擎进程内取价失败(momentum 6 笔卖单
fail-closed 忽略)而独立探针 .SH 格式全通——需要**进程内**直接证据区分
「引擎会话坏」vs「数据服务坏」。boot 时留痕一笔,轮换后即知新进程
行情腿通不通;只记日志不阻断启动(自检失败不挡交易,留证据即可)。
"""
try:
if _fetch is None:
from xtquant import xtdata
ticks = xtdata.get_full_tick([PREFLIGHT_QUOTE_CODE])
else:
ticks = _fetch()
d = ticks.get(PREFLIGHT_QUOTE_CODE) if isinstance(ticks, dict) else None
if d:
price = d.get("lastPrice", d.get("last_price"))
return "OK price=%s" % price
return "EMPTY ticks=%s" % (str(ticks)[:60] if ticks else "{}")
except Exception as e: # noqa: BLE001 - 自检不阻断引擎启动
return "EXC: %s" % e
def with_connect_retry(broker: Any,
attempts: int = CONNECT_RETRY_ATTEMPTS,
wait_sec: float = CONNECT_RETRY_WAIT_SEC,
@@ -274,6 +300,8 @@ def run_live(provider_config: Dict[str, Any] | None = None) -> None:
logger.info("QmtBroker 装配 account=%s data_path=%s(connect 失败退避重试 %d×%.0fs)",
cfg["account"], cfg["mini_path"],
CONNECT_RETRY_ATTEMPTS, CONNECT_RETRY_WAIT_SEC)
logger.info("[quote-preflight] 行情腿自检 %s: %s",
PREFLIGHT_QUOTE_CODE, quote_preflight())
# 实例虚拟账本(共享 QMT 账户的切片视图,2026-08-19 互卖/对账/收益率三问题同根):
# 先建+恢复再起 engine——适配层 _setup 经 get_active() 注入 facade 通道
@@ -66,3 +66,31 @@ def test_live_config_carries_instance_strategy_name():
from bullet_trade.core.live_engine import LiveConfig
cfg = LiveConfig.load({"strategy_name": "live_17"})
assert cfg.strategy_name == "live_17"
# ---------------- quote_preflight:启动期行情腿自检(2026-09-03 事故定位) ----------------
def test_quote_preflight_ok_with_price():
"""行情腿通:返回 OK+价格(进程内直接证据,boot 时一笔留痕)。"""
from sanguo_portfolio.runner_live import quote_preflight
out = quote_preflight(_fetch=lambda: {"510500.SH": {"lastPrice": 7.88}})
assert out.startswith("OK")
assert "7.88" in out
def test_quote_preflight_empty_is_reported_not_raised():
"""取到空(格式错/会话坏/盘后无数据):返回 EMPTY 摘要,绝不抛。"""
from sanguo_portfolio.runner_live import quote_preflight
assert quote_preflight(_fetch=lambda: {}).startswith("EMPTY")
assert quote_preflight(_fetch=lambda: None).startswith("EMPTY")
def test_quote_preflight_exception_is_swallowed():
"""fetch 抛异常:返回 EXC 摘要——自检不阻断引擎启动。"""
from sanguo_portfolio.runner_live import quote_preflight
def _boom():
raise RuntimeError("xtdata down")
out = quote_preflight(_fetch=_boom)
assert out.startswith("EXC")
assert "xtdata down" in out