Files
sanguo_vnpy_v2/sanguo_trader/shadow/__main__.py
T

84 lines
2.6 KiB
Python

"""影子柜台 CLI 入口。
用法:
python -m sanguo_trader.shadow # 读 env(见 runner.py docstring)
python -m sanguo_trader.shadow --auto # 主管模式:轮询 paper 库自动拉起影子账户
"""
from __future__ import annotations
import logging
import os
import sys
from pathlib import Path
LOCK_FILE = Path(
os.environ.get("SANGUO_SHADOW_LOCK")
or Path.home() / ".sanguo_shadow_desk.lock"
)
def _acquire_lock() -> "object | None":
"""单实例锁(Windows/msvcrt 与 POSIX/fcntl 双兼容)。"""
LOCK_FILE.parent.mkdir(parents=True, exist_ok=True)
try:
import fcntl # POSIX
fh = open(LOCK_FILE, "w")
fcntl.flock(fh, fcntl.LOCK_EX | fcntl.LOCK_NB)
return fh
except ImportError:
pass
except OSError:
return None # 已有实例在跑
try:
import msvcrt # Windows
fh = open(LOCK_FILE, "w")
msvcrt.locking(fh.fileno(), msvcrt.LK_NBLCK, 1)
return fh
except (ImportError, OSError):
return None
def main() -> int:
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s %(name)s: %(message)s",
)
if "--auto" in sys.argv:
# supervisor 自身日志落文件(schtasks 控制台输出是黑洞,拉起/重启决策无处可查)
try:
_log_dir = Path(__file__).resolve().parents[2] / "logs"
_log_dir.mkdir(parents=True, exist_ok=True)
_fh = logging.FileHandler(_log_dir / "shadow_supervisor.log")
_fh.setFormatter(logging.Formatter(
"%(asctime)s %(levelname)s %(name)s: %(message)s"))
logging.getLogger().addHandler(_fh)
except Exception:
pass # 日志失败不挡主管
from .supervisor import run_auto_supervisor
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)
return 0
from .runner import run_shadow
run_shadow()
return 0
if __name__ == "__main__":
sys.exit(main())