84 lines
2.6 KiB
Python
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())
|