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:
1 parent
8cdfb2a8e2
commit
233f0c5d10
4 files changed
+82
-17
No files matched your search
@@ -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()
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
+59
-9
@@ -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
|
||||||
|
|
||||||
# 颜色代码
|
# 检测操作系统
|
||||||
|
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'
|
GREEN = '\033[92m'
|
||||||
YELLOW = '\033[93m'
|
YELLOW = '\033[93m'
|
||||||
RED = '\033[91m'
|
RED = '\033[91m'
|
||||||
RESET = '\033[0m'
|
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
@@ -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__":
|
||||||
|
|||||||
Reference in new issue
Block a user