diff --git a/sanguo_trader/shadow/__main__.py b/sanguo_trader/shadow/__main__.py index 8c4a139..11d0d96 100644 --- a/sanguo_trader/shadow/__main__.py +++ b/sanguo_trader/shadow/__main__.py @@ -60,6 +60,15 @@ def main() -> int: run_auto_supervisor() return 0 + if "--account" in sys.argv: + # 多账户 worker 不抢全局 solo 锁:全局锁全机只放行一个 worker,supervisor + # 多账户并存时其余全部"锁占用"优雅退出(VPS 2026-08-16 实录 40/41 反复 + # rc=0,单账户时代从未暴露)。同账户防重复由 bullet_trade + # runtime/shadow_{aid} 实例锁兜底,supervisor children 表保证一账户一进程。 + from .runner import run_shadow + + run_shadow() + return 0 lock = _acquire_lock() if lock is None: print("影子柜台已在运行(锁占用),本次启动退出。", flush=True) diff --git a/tests/trader/test_shadow_lock_routing.py b/tests/trader/test_shadow_lock_routing.py new file mode 100644 index 0000000..861298a --- /dev/null +++ b/tests/trader/test_shadow_lock_routing.py @@ -0,0 +1,56 @@ +"""影子柜台 CLI 路由:--account worker 不抢全局 solo 锁(多账户并存的前提)。""" +import sys + +import pytest + + +def _run_main(monkeypatch, argv): + from sanguo_trader.shadow import __main__ as m + + calls = {"lock": 0, "shadow": 0, "auto": 0} + + def _no_lock(): + calls["lock"] += 1 + return object() + + def _shadow(*a, **k): + calls["shadow"] += 1 + + def _auto(*a, **k): + calls["auto"] += 1 + + import sanguo_trader.shadow.runner as runner_mod + import sanguo_trader.shadow.supervisor as sup + + monkeypatch.setattr(m, "_acquire_lock", _no_lock) + # main() 内 lazy import:patch 源模块而非 __main__ + monkeypatch.setattr(runner_mod, "run_shadow", _shadow) + monkeypatch.setattr(sup, "run_auto_supervisor", _auto) + monkeypatch.setattr(sys, "argv", ["prog", *argv]) + rc = m.main() + return rc, calls + + +def test_account_worker_skips_global_lock(monkeypatch): + """--account N:多账户 worker 不碰全局锁(VPS 实录 40/41「锁占用」循环退出)。""" + rc, calls = _run_main(monkeypatch, ["--account", "41"]) + assert rc == 0 + assert calls["shadow"] == 1 + assert calls["lock"] == 0 # 关键:不抢全局 solo 锁 + + +def test_solo_mode_still_locks(monkeypatch): + """无 --account 的 solo 模式维持全局锁(防双开)。""" + rc, calls = _run_main(monkeypatch, []) + assert rc == 0 + assert calls["shadow"] == 1 + assert calls["lock"] == 1 + + +def test_auto_mode_no_lock_no_shadow(monkeypatch): + """--auto 主管:既不抢锁也不起柜台。""" + rc, calls = _run_main(monkeypatch, ["--auto"]) + assert rc == 0 + assert calls["auto"] == 1 + assert calls["lock"] == 0 + assert calls["shadow"] == 0