feat: 添加 Windows 兼容性支持,优化设备选择逻辑

- 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
This commit is contained in:
crypto_predictor committed 2026-02-07 21:48:06 +08:00
1 parent 8cdfb2a8e2
commit 233f0c5d10
4 files changed
+86 -21

No files matched your search

+8 -2
View File
@@ -87,12 +87,18 @@ class PredictionService:
def __init__(self): def __init__(self):
import threading import threading
import platform
# 初始化锁 # 初始化锁
if PredictionService._prediction_lock is None: if PredictionService._prediction_lock is None:
PredictionService._prediction_lock = threading.Lock() PredictionService._prediction_lock = threading.Lock()
self.device = torch.device('mps' if torch.backends.mps.is_available() else # 设备选择:macOS 用 MPS,Windows/Linux 用 CPU
'cuda' if torch.cuda.is_available() else '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.et_tz = ZoneInfo('America/New_York')
self.fetcher = BinanceDataFetcher() self.fetcher = BinanceDataFetcher()
+7 -3
View File
@@ -175,9 +175,13 @@ class PolymarketBTCTrader:
headers={'Accept-Encoding': 'identity'} headers={'Accept-Encoding': 'identity'}
) )
# 设备 # 设备选择:macOS 用 MPS,Windows/Linux 用 CPU
self.device = torch.device('mps' if torch.backends.mps.is_available() else import platform
'cuda' if torch.cuda.is_available() else '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.model = None self.model = None
+63 -13
View File
@@ -4,34 +4,66 @@
同时启动: 同时启动:
1. FastAPI 后端服务 (提供前端看板 API) 1. FastAPI 后端服务 (提供前端看板 API)
2. Polymarket 自动交易程序 2. Polymarket 自动交易程序
支持平台:macOS, Linux, Windows
""" """
import subprocess import subprocess
import sys import sys
import time import time
import signal import signal
import os import os
import platform
from datetime import datetime from datetime import datetime
# 颜色代码 # 检测操作系统
GREEN = '\033[92m' IS_WINDOWS = platform.system() == 'Windows'
YELLOW = '\033[93m'
RED = '\033[91m' # 颜色代码 (Windows CMD 默认不支持 ANSI,但现代 Windows Terminal 支持)
RESET = '\033[0m' 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): def log(message, color=RESET):
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S') timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
print(f"{color}[{timestamp}] {message}{RESET}") print(f"{color}[{timestamp}] {message}{RESET}")
def start_process(command, name): def start_process(command, name):
"""启动子进程""" """启动子进程(跨平台兼容)"""
log(f"正在启动 {name}...", YELLOW) log(f"正在启动 {name}...", YELLOW)
try: try:
if IS_WINDOWS:
# Windows: 不使用 preexec_fn,使用 CREATE_NEW_PROCESS_GROUP
process = subprocess.Popen( process = subprocess.Popen(
command, command,
shell=True, shell=True,
stdout=sys.stdout, stdout=sys.stdout,
stderr=sys.stderr, stderr=sys.stderr,
preexec_fn=os.setsid # 创建新的进程组,方便统一杀掉 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) log(f"{name} 已启动 (PID: {process.pid})", GREEN)
return process return process
@@ -39,6 +71,26 @@ def start_process(command, name):
log(f"启动 {name} 失败: {e}", RED) log(f"启动 {name} 失败: {e}", RED)
return None 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(): def main():
# 激活 Conda 环境 # 激活 Conda 环境
# 注意:在子进程中直接使用当前已激活的 python 解释器路径,避免 conda activate 问题 # 注意:在子进程中直接使用当前已激活的 python 解释器路径,避免 conda activate 问题
@@ -64,10 +116,10 @@ def main():
try: try:
# 监控子进程状态 # 监控子进程状态
while True: while True:
if api_process.poll() is not None: if api_process and api_process.poll() is not None:
log("API 服务意外退出", RED) log("API 服务意外退出", RED)
break break
if trade_process.poll() is not None: if trade_process and trade_process.poll() is not None:
log("自动交易程序意外退出", RED) log("自动交易程序意外退出", RED)
break break
time.sleep(1) time.sleep(1)
@@ -76,10 +128,8 @@ def main():
log("\n正在停止服务...", YELLOW) log("\n正在停止服务...", YELLOW)
finally: finally:
# 优雅退出 # 优雅退出
if api_process: terminate_process(api_process, "API 服务")
os.killpg(os.getpgid(api_process.pid), signal.SIGTERM) terminate_process(trade_process, "自动交易程序")
if trade_process:
os.killpg(os.getpgid(trade_process.pid), signal.SIGTERM)
log("所有服务已停止", GREEN) log("所有服务已停止", GREEN)
+8 -3
View File
@@ -38,19 +38,24 @@ class TrainingConfig:
Returns: Returns:
Device string ('mps', 'cuda', or 'cpu') Device string ('mps', 'cuda', or 'cpu')
Note:
macOS 使用 MPS,Windows/Linux 使用 CPU
""" """
import torch import torch
import platform
if self.device != 'auto': if self.device != 'auto':
return self.device return self.device
# Auto-detect best available device # 根据操作系统选择设备:macOS 用 MPS,Windows/Linux 用 CPU
if platform.system() == 'Darwin': # macOS
if torch.backends.mps.is_available(): if torch.backends.mps.is_available():
return 'mps' return 'mps'
elif torch.cuda.is_available():
return 'cuda'
else: else:
return 'cpu' return 'cpu'
else: # Windows / Linux
return 'cpu'
if __name__ == "__main__": if __name__ == "__main__":