From 233f0c5d10ea4f937066e51cd6506a0784f85d3c Mon Sep 17 00:00:00 2001 From: crypto_predictor Date: Sat, 7 Feb 2026 21:48:06 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20Windows=20?= =?UTF-8?q?=E5=85=BC=E5=AE=B9=E6=80=A7=E6=94=AF=E6=8C=81=EF=BC=8C=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E8=AE=BE=E5=A4=87=E9=80=89=E6=8B=A9=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - start_system.py: 跨平台进程管理 - Windows 使用 CREATE_NEW_PROCESS_GROUP 和 terminate() - Unix 使用 setsid 和 killpg - 自动检测并启用 Windows ANSI 颜色支持 - 设备选择逻辑优化: - macOS: 优先使用 MPS,回退到 CPU - Windows/Linux: 强制使用 CPU(无 CUDA 依赖) - 影响文件: - api/prediction_service.py - auto_trade_polymarket.py - training/config.py --- api/prediction_service.py | 10 ++++- auto_trade_polymarket.py | 10 +++-- start_system.py | 88 ++++++++++++++++++++++++++++++--------- training/config.py | 17 +++++--- 4 files changed, 95 insertions(+), 30 deletions(-) diff --git a/api/prediction_service.py b/api/prediction_service.py index 560d1aa..77b63ba 100644 --- a/api/prediction_service.py +++ b/api/prediction_service.py @@ -87,12 +87,18 @@ class PredictionService: def __init__(self): import threading + import platform # 初始化锁 if PredictionService._prediction_lock is None: PredictionService._prediction_lock = threading.Lock() - self.device = torch.device('mps' if torch.backends.mps.is_available() else - 'cuda' if torch.cuda.is_available() else 'cpu') + # 设备选择:macOS 用 MPS,Windows/Linux 用 CPU + if platform.system() == 'Darwin': # macOS + self.device = torch.device('mps' if torch.backends.mps.is_available() else 'cpu') + else: # Windows / Linux + self.device = torch.device('cpu') + print(f"使用设备: {self.device}") + self.et_tz = ZoneInfo('America/New_York') self.fetcher = BinanceDataFetcher() diff --git a/auto_trade_polymarket.py b/auto_trade_polymarket.py index df67bd9..709d2c3 100644 --- a/auto_trade_polymarket.py +++ b/auto_trade_polymarket.py @@ -175,9 +175,13 @@ class PolymarketBTCTrader: headers={'Accept-Encoding': 'identity'} ) - # 设备 - self.device = torch.device('mps' if torch.backends.mps.is_available() else - 'cuda' if torch.cuda.is_available() else 'cpu') + # 设备选择:macOS 用 MPS,Windows/Linux 用 CPU + import platform + if platform.system() == 'Darwin': # macOS + self.device = torch.device('mps' if torch.backends.mps.is_available() else 'cpu') + else: # Windows / Linux + self.device = torch.device('cpu') + print(f"使用设备: {self.device}") # 加载模型 self.model = None diff --git a/start_system.py b/start_system.py index 558bca3..88c8bd6 100755 --- a/start_system.py +++ b/start_system.py @@ -4,41 +4,93 @@ 同时启动: 1. FastAPI 后端服务 (提供前端看板 API) 2. Polymarket 自动交易程序 + +支持平台:macOS, Linux, Windows """ import subprocess import sys import time import signal import os +import platform from datetime import datetime -# 颜色代码 -GREEN = '\033[92m' -YELLOW = '\033[93m' -RED = '\033[91m' -RESET = '\033[0m' +# 检测操作系统 +IS_WINDOWS = platform.system() == 'Windows' + +# 颜色代码 (Windows CMD 默认不支持 ANSI,但现代 Windows Terminal 支持) +if IS_WINDOWS: + # 尝试启用 Windows ANSI 支持 + try: + import ctypes + kernel32 = ctypes.windll.kernel32 + kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7) + ANSI_SUPPORTED = True + except Exception: + ANSI_SUPPORTED = False +else: + ANSI_SUPPORTED = True + +if ANSI_SUPPORTED: + GREEN = '\033[92m' + YELLOW = '\033[93m' + RED = '\033[91m' + RESET = '\033[0m' +else: + GREEN = YELLOW = RED = RESET = '' def log(message, color=RESET): timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S') print(f"{color}[{timestamp}] {message}{RESET}") def start_process(command, name): - """启动子进程""" + """启动子进程(跨平台兼容)""" log(f"正在启动 {name}...", YELLOW) try: - process = subprocess.Popen( - command, - shell=True, - stdout=sys.stdout, - stderr=sys.stderr, - preexec_fn=os.setsid # 创建新的进程组,方便统一杀掉 - ) + if IS_WINDOWS: + # Windows: 不使用 preexec_fn,使用 CREATE_NEW_PROCESS_GROUP + process = subprocess.Popen( + command, + shell=True, + stdout=sys.stdout, + stderr=sys.stderr, + creationflags=subprocess.CREATE_NEW_PROCESS_GROUP + ) + else: + # macOS / Linux: 使用 setsid 创建新进程组 + process = subprocess.Popen( + command, + shell=True, + stdout=sys.stdout, + stderr=sys.stderr, + preexec_fn=os.setsid + ) log(f"{name} 已启动 (PID: {process.pid})", GREEN) return process except Exception as e: log(f"启动 {name} 失败: {e}", RED) return None +def terminate_process(process, name): + """终止进程(跨平台兼容)""" + if process is None: + return + try: + if IS_WINDOWS: + # Windows: 使用 terminate() 或发送 CTRL_BREAK_EVENT + process.terminate() + else: + # macOS / Linux: 终止整个进程组 + os.killpg(os.getpgid(process.pid), signal.SIGTERM) + log(f"{name} 已停止", GREEN) + except Exception as e: + log(f"停止 {name} 时出错: {e}", RED) + # 强制杀死 + try: + process.kill() + except Exception: + pass + def main(): # 激活 Conda 环境 # 注意:在子进程中直接使用当前已激活的 python 解释器路径,避免 conda activate 问题 @@ -64,10 +116,10 @@ def main(): try: # 监控子进程状态 while True: - if api_process.poll() is not None: + if api_process and api_process.poll() is not None: log("API 服务意外退出", RED) break - if trade_process.poll() is not None: + if trade_process and trade_process.poll() is not None: log("自动交易程序意外退出", RED) break time.sleep(1) @@ -76,10 +128,8 @@ def main(): log("\n正在停止服务...", YELLOW) finally: # 优雅退出 - if api_process: - os.killpg(os.getpgid(api_process.pid), signal.SIGTERM) - if trade_process: - os.killpg(os.getpgid(trade_process.pid), signal.SIGTERM) + terminate_process(api_process, "API 服务") + terminate_process(trade_process, "自动交易程序") log("所有服务已停止", GREEN) diff --git a/training/config.py b/training/config.py index dbeb49f..914e098 100644 --- a/training/config.py +++ b/training/config.py @@ -38,18 +38,23 @@ class TrainingConfig: Returns: Device string ('mps', 'cuda', or 'cpu') + + Note: + macOS 使用 MPS,Windows/Linux 使用 CPU """ import torch + import platform if self.device != 'auto': return self.device - # Auto-detect best available device - if torch.backends.mps.is_available(): - return 'mps' - elif torch.cuda.is_available(): - return 'cuda' - else: + # 根据操作系统选择设备:macOS 用 MPS,Windows/Linux 用 CPU + if platform.system() == 'Darwin': # macOS + if torch.backends.mps.is_available(): + return 'mps' + else: + return 'cpu' + else: # Windows / Linux return 'cpu'