From 7a6e9c8c52bd6fd8aaa6b884482c34a1dbe576b3 Mon Sep 17 00:00:00 2001 From: claude_dev Date: Mon, 24 Aug 2026 13:49:21 +0800 Subject: [PATCH] =?UTF-8?q?test(api):=20=E4=BF=AEchannel=5Ftest=E9=9A=8F?= =?UTF-8?q?=E5=90=AF=E9=9A=8F=E9=AA=8C=E8=BF=87=E6=97=B6=E6=96=AD=E8=A8=80?= =?UTF-8?q?=E2=80=94=E2=80=94=E5=AE=9E=E7=9B=98=3D=E5=8D=95probe=5Fall?= =?UTF-8?q?=E5=AE=9A=E6=97=B6(=E5=90=AF=E5=8A=A8+90s=E9=9A=8F=E5=A2=99?= =?UTF-8?q?=E9=92=9F),=E5=9B=9B=E6=97=B6=E7=82=B9=E6=94=B9=E7=94=B1?= =?UTF-8?q?=E6=97=A0env=E7=9B=B4=E6=B5=8B=E9=92=89=E6=AD=BB=20[nas]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 策略session 08-24移交项#2: test_facade_injects_run_daily_for_channel_test 在HEAD恒红,非flaky——原断言的9:35/10:45/13:45/14:30四时点是回测/无env路径;实盘(SANGUO_LIVE_STRATEGY置位)自随启随验(channel_test.py:78-84)只注册一个「启动+90s」的probe_all,时:分随墙钟(13:33/13:34/13:35三跑三变之源)。 修=断言随启随验设计:单probe_all+时刻落在[注册前+90s,注册后+90s]取分窗口(纯时刻比较防1900年日期陷阱,容忍跨午夜);新增无env直测(ChannelTestConfig(on_demand=False))钉死四时点路径,原断言价值不丢。 3跑稳定;tests/api 165全绿。 --- tests/api/test_portfolio_live.py | 46 +++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/tests/api/test_portfolio_live.py b/tests/api/test_portfolio_live.py index fdae217..7585c1a 100644 --- a/tests/api/test_portfolio_live.py +++ b/tests/api/test_portfolio_live.py @@ -233,23 +233,61 @@ def test_setup_idempotent_across_both_hooks(monkeypatch): def test_facade_injects_run_daily_for_channel_test(monkeypatch): - """channel_test 自注册依赖 facade.run_daily(缺注入时静默 no-op 永不开仓): - process_initialize 后 9:35/10:45/13:45/14:30 四时点应经注入的 run_daily 挂上。""" + """channel_test 自注册依赖 facade.run_daily(缺注入时静默 no-op 永不开仓)。 + 实盘(env 置位)走随启随验:只注册一个「启动后约90秒」的 probe_all 定时 + (时间随墙钟=原断言四时点恒红的原因);四时点行为由下方无 env 直测钉死。""" import bullet_trade.core as bt_core + from datetime import datetime, timedelta from sanguo_portfolio import live_strategy registered = _patch_live_wiring(monkeypatch) monkeypatch.setenv("SANGUO_LIVE_STRATEGY", "channel_test") _reset_live_state() + before = datetime.now() live_strategy.process_initialize(object()) + after = datetime.now() - daily_times = sorted(r[2] for r in registered if r[0] == "daily") - assert daily_times == ["10:45", "13:45", "14:30", "9:35"] + daily = [r for r in registered if r[0] == "daily"] + assert [r[1] for r in daily] == ["probe_all"] + # 注册时刻在 before~after 之间 → 探针时:分必落在 [floor(before+90s), floor(after+90s)] + # (纯时刻比较;strptime 是 1900 年日期不能与 now 直接比。跨午夜时两者恰为首尾) + probe_t = datetime.strptime(daily[0][2], "%H:%M").time() + lo_t = (before + timedelta(seconds=90)).replace(second=0, microsecond=0).time() + hi_t = (after + timedelta(seconds=90)).replace(second=0, microsecond=0).time() + assert lo_t <= probe_t <= hi_t or probe_t in (lo_t, hi_t) # facade 拿到的是 bullet_trade 顶层 run_daily 本尊,不是默认空 lambda assert live_strategy._STATE["strategy"].broker.run_daily is bt_core.run_daily +def test_channel_test_backtest_mode_registers_four_fixed_times(): + """回测/无 env 路径:9:35/10:45/13:45/14:30 四时点保留(随启随验只影响实盘)。""" + from sanguo_portfolio.strategies.channel_test import ( + ChannelTestConfig, ChannelTestStrategy, + ) + + class _FakeBroker: + def __init__(self): + self.calls = [] + + def set_benchmark(self, *a): + pass + + def set_option(self, *a): + pass + + def run_daily(self, f, t): + self.calls.append((getattr(f, "__name__", str(f)), t)) + + b = _FakeBroker() + s = ChannelTestStrategy( + provider=object(), broker=b, config=ChannelTestConfig(on_demand=False)) + s.initialize(object()) + + times = sorted(t for _, t in b.calls) + assert times == ["10:45", "13:45", "14:30", "9:35"] + + def test_normalize_vt_symbol_bare_code_gets_exchange_suffix(): """CTA 实盘标的:裸 6 位码自动补交易所后缀(6→SSE,0/3→SZSE)。""" from sanguo_api.routes_live import _normalize_vt_symbol