- 新增 daily_report_sender.py 定时任务程序 - 北京时间每天 8 点推送昨天 24 小时预测统计 - 包含各模型准确率、逐小时预测详情 - 失败交易订单汇总 - start_system.py 集成启动报告推送服务
179 lines
5.6 KiB
Python
Executable File
179 lines
5.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
系统启动脚本
|
|
同时启动:
|
|
1. FastAPI 后端服务 (提供前端看板 API)
|
|
2. Polymarket 自动交易程序
|
|
3. 每日数据报告推送程序 (北京时间每天 8 点)
|
|
|
|
支持平台:macOS, Linux, Windows
|
|
"""
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
import signal
|
|
import os
|
|
import platform
|
|
from datetime import datetime
|
|
|
|
try:
|
|
from daily_report_sender import run_scheduler_background
|
|
except ImportError:
|
|
# 如果模块导入失败,定义一个空函数避免启动失败
|
|
def run_scheduler_background():
|
|
return None
|
|
|
|
# 检测操作系统
|
|
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:
|
|
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 send_alert(content):
|
|
"""发送告警消息 (使用 curl)"""
|
|
try:
|
|
url = "https://zhiyinzhe.xyz/sendMsg/api/send"
|
|
cmd = [
|
|
"curl", "-X", "POST", url,
|
|
"-F", "bot_name=weibo",
|
|
"-F", "receive_id=on_f972f5f8c4de6ac967574b97dc8c1d82",
|
|
"-F", "receive_id_type=union_id",
|
|
"-F", f"content={content}"
|
|
]
|
|
# 使用 run 等待请求完成,确保告警发出
|
|
subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=10)
|
|
log(f"已发送告警: {content}", GREEN)
|
|
except Exception as e:
|
|
log(f"发送告警失败: {e}", RED)
|
|
|
|
def main():
|
|
# 激活 Conda 环境
|
|
# 注意:在子进程中直接使用当前已激活的 python 解释器路径,避免 conda activate 问题
|
|
python_exec = sys.executable
|
|
|
|
# 1. 启动 API 服务 (Uvicorn)
|
|
# 使用 --host 0.0.0.0 允许局域网访问
|
|
api_cmd = f"{python_exec} -m uvicorn api.main:app --host 0.0.0.0 --port 8000"
|
|
api_process = start_process(api_cmd, "API 服务 & 前端看板")
|
|
|
|
# 等待几秒确保 API 启动
|
|
time.sleep(3)
|
|
|
|
# 2. 启动自动交易程序
|
|
# 使用 -u 参数禁用输出缓冲
|
|
trade_cmd = f"{python_exec} -u trader/auto_trade_polymarket.py"
|
|
trade_process = start_process(trade_cmd, "Polymarket 自动交易程序")
|
|
|
|
# 3. 启动每日数据报告推送程序(后台线程)
|
|
# 北京时间每天 8 点推送昨天的数据统计
|
|
try:
|
|
report_thread = run_scheduler_background()
|
|
if report_thread:
|
|
log("每日数据报告推送程序已启动", GREEN)
|
|
else:
|
|
log("⚠️ 每日数据报告推送程序启动失败", YELLOW)
|
|
except Exception as e:
|
|
log(f"⚠️ 启动每日报告程序异常: {e}", YELLOW)
|
|
report_thread = None
|
|
|
|
log("系统启动完成!", GREEN)
|
|
log("前端访问地址: http://localhost:8000", GREEN)
|
|
log("按 Ctrl+C 停止所有服务...", YELLOW)
|
|
|
|
try:
|
|
# 监控子进程状态
|
|
while True:
|
|
if api_process and api_process.poll() is not None:
|
|
msg = "API 服务意外退出"
|
|
log(msg, RED)
|
|
send_alert(msg)
|
|
break
|
|
if trade_process and trade_process.poll() is not None:
|
|
msg = "自动交易程序意外退出"
|
|
log(msg, RED)
|
|
send_alert(msg)
|
|
break
|
|
time.sleep(1)
|
|
|
|
except KeyboardInterrupt:
|
|
log("\n正在停止服务...", YELLOW)
|
|
finally:
|
|
# 优雅退出
|
|
terminate_process(api_process, "API 服务")
|
|
terminate_process(trade_process, "自动交易程序")
|
|
|
|
log("所有服务已停止", GREEN)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|