2026-01-04 22:43:13 +08:00
|
|
|
|
# coding:utf-8
|
|
|
|
|
|
"""
|
2026-01-10 04:06:35 +08:00
|
|
|
|
QMT多终端交易系统启动器
|
|
|
|
|
|
版本:V2.0 (Multi-Terminal Edition)
|
2026-01-04 22:43:13 +08:00
|
|
|
|
"""
|
|
|
|
|
|
import sys
|
|
|
|
|
|
import os
|
2026-01-10 04:06:35 +08:00
|
|
|
|
import threading
|
2026-01-27 01:21:22 +08:00
|
|
|
|
import logging
|
|
|
|
|
|
import datetime
|
2026-01-10 04:06:35 +08:00
|
|
|
|
import uvicorn
|
2026-01-04 22:43:13 +08:00
|
|
|
|
|
2026-01-10 04:06:35 +08:00
|
|
|
|
# 将当前目录添加到Python路径,确保模块导入正常
|
2026-01-04 22:43:13 +08:00
|
|
|
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
|
|
if current_dir not in sys.path:
|
|
|
|
|
|
sys.path.insert(0, current_dir)
|
|
|
|
|
|
|
2026-01-10 04:06:35 +08:00
|
|
|
|
# 导入升级后的多终端管理器
|
|
|
|
|
|
from qmt_engine import MultiEngineManager
|
2026-01-04 22:43:13 +08:00
|
|
|
|
from api_server import create_api_server
|
|
|
|
|
|
|
2026-01-27 01:21:22 +08:00
|
|
|
|
|
|
|
|
|
|
def setup_logger():
|
|
|
|
|
|
"""配置日志系统"""
|
|
|
|
|
|
log_dir = "logs"
|
|
|
|
|
|
if not os.path.exists(log_dir):
|
|
|
|
|
|
os.makedirs(log_dir)
|
|
|
|
|
|
|
|
|
|
|
|
log_file = os.path.join(log_dir, f"{datetime.date.today().strftime('%Y-%m-%d')}.log")
|
|
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger("QMT_Run")
|
|
|
|
|
|
logger.setLevel(logging.INFO)
|
|
|
|
|
|
|
|
|
|
|
|
formatter = logging.Formatter(
|
|
|
|
|
|
'[%(asctime)s] [%(levelname)s] [%(filename)s:%(lineno)d] %(message)s',
|
|
|
|
|
|
datefmt='%Y-%m-%d %H:%M:%S'
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
file_handler = logging.FileHandler(log_file, mode='a', encoding='utf-8')
|
|
|
|
|
|
file_handler.setFormatter(formatter)
|
|
|
|
|
|
|
|
|
|
|
|
stream_handler = logging.StreamHandler(sys.stdout)
|
|
|
|
|
|
stream_handler.setFormatter(formatter)
|
|
|
|
|
|
|
|
|
|
|
|
logger.addHandler(file_handler)
|
|
|
|
|
|
logger.addHandler(stream_handler)
|
|
|
|
|
|
|
|
|
|
|
|
return logger
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-01-04 22:43:13 +08:00
|
|
|
|
def main():
|
2026-01-10 04:06:35 +08:00
|
|
|
|
"""主函数 - 启动多终端QMT交易引擎管理中心和API服务器"""
|
2026-01-27 01:21:22 +08:00
|
|
|
|
logger = setup_logger()
|
|
|
|
|
|
|
2026-01-10 04:06:35 +08:00
|
|
|
|
# 强制设置环境变量,确保Python在Windows控制台输出不因编码崩溃
|
|
|
|
|
|
os.environ["PYTHONUTF8"] = "1"
|
|
|
|
|
|
|
2026-01-27 01:21:22 +08:00
|
|
|
|
logger.info("="*50)
|
|
|
|
|
|
logger.info(">>> QMT Multi-Terminal System Starting...")
|
|
|
|
|
|
logger.info("="*50)
|
2026-01-04 22:43:13 +08:00
|
|
|
|
|
2026-01-10 04:06:35 +08:00
|
|
|
|
# 1. 获取多终端管理器单例
|
|
|
|
|
|
manager = MultiEngineManager()
|
2026-01-27 01:21:22 +08:00
|
|
|
|
logger.info("多终端管理器实例获取成功")
|
2026-01-04 22:43:13 +08:00
|
|
|
|
|
|
|
|
|
|
try:
|
2026-01-10 04:06:35 +08:00
|
|
|
|
# 2. 初始化引擎(加载配置、连接Redis、初始化各终端执行单元)
|
2026-01-27 01:21:22 +08:00
|
|
|
|
logger.info("正在加载配置文件: config.json")
|
2026-01-10 04:06:35 +08:00
|
|
|
|
manager.initialize('config.json')
|
2026-01-27 01:21:22 +08:00
|
|
|
|
logger.info("Done: Multi-Manager initialized successfully.")
|
2026-01-04 22:43:13 +08:00
|
|
|
|
except Exception as e:
|
2026-01-27 01:21:22 +08:00
|
|
|
|
logger.error(f"Error: System initialization failed: {repr(e)}", exc_info=True)
|
2026-01-04 22:43:13 +08:00
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
2026-01-10 04:06:35 +08:00
|
|
|
|
# 3. 启动全局监控与交易路由主循环线程
|
|
|
|
|
|
# 该线程负责:终端健康检查、断线重连、消息路由、收盘结算
|
2026-01-27 01:21:22 +08:00
|
|
|
|
logger.info("正在启动交易主循环线程...")
|
2026-01-10 04:06:35 +08:00
|
|
|
|
trading_thread = threading.Thread(target=manager.run_trading_loop, name="MainTradeLoop", daemon=True)
|
2026-01-04 22:43:13 +08:00
|
|
|
|
trading_thread.start()
|
2026-01-27 01:21:22 +08:00
|
|
|
|
logger.info("Done: Global trading loop thread started.")
|
2026-01-04 22:43:13 +08:00
|
|
|
|
|
2026-01-10 04:06:35 +08:00
|
|
|
|
# 4. 创建适配多终端的API服务器
|
2026-01-27 01:21:22 +08:00
|
|
|
|
logger.info("正在创建API服务器...")
|
2026-01-10 04:06:35 +08:00
|
|
|
|
app = create_api_server(manager)
|
2026-01-27 01:21:22 +08:00
|
|
|
|
logger.info("Done: API server created with multi-terminal support.")
|
2026-01-04 22:43:13 +08:00
|
|
|
|
|
2026-01-10 04:06:35 +08:00
|
|
|
|
# 5. 启动Web服务
|
2026-01-27 01:21:22 +08:00
|
|
|
|
logger.info(">>> Web Dashboard: http://localhost:8001")
|
2026-01-04 22:43:13 +08:00
|
|
|
|
try:
|
2026-01-10 04:06:35 +08:00
|
|
|
|
# 建议关闭 access_log 以减少控制台刷屏
|
2026-01-04 22:43:13 +08:00
|
|
|
|
uvicorn.run(
|
|
|
|
|
|
app,
|
|
|
|
|
|
host="0.0.0.0",
|
|
|
|
|
|
port=8001,
|
|
|
|
|
|
log_level="warning",
|
|
|
|
|
|
access_log=False
|
|
|
|
|
|
)
|
|
|
|
|
|
except KeyboardInterrupt:
|
2026-01-27 01:21:22 +08:00
|
|
|
|
logger.info(">>> Shutdown signal received. Closing terminals...")
|
2026-01-10 04:06:35 +08:00
|
|
|
|
manager.stop()
|
2026-01-27 01:21:22 +08:00
|
|
|
|
logger.info(">>> System safely closed.")
|
2026-01-27 00:52:35 +08:00
|
|
|
|
_write_exit_code(0)
|
|
|
|
|
|
sys.exit(0)
|
|
|
|
|
|
|
|
|
|
|
|
def _write_exit_code(code):
|
|
|
|
|
|
"""将退出码写入临时文件,供 start.bat 读取"""
|
2026-01-27 01:21:22 +08:00
|
|
|
|
logger = logging.getLogger("QMT_Run")
|
2026-01-27 00:52:35 +08:00
|
|
|
|
try:
|
|
|
|
|
|
exit_code_file = os.path.join(os.environ.get('TEMP', ''), 'exit_code.txt')
|
|
|
|
|
|
with open(exit_code_file, 'w') as f:
|
|
|
|
|
|
f.write(str(code))
|
2026-01-27 01:21:22 +08:00
|
|
|
|
logger.info(f">>> 退出码已写入: {code}")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f">>> 退出码写入失败: {str(e)}")
|
2026-01-04 22:43:13 +08:00
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
2026-01-10 04:06:35 +08:00
|
|
|
|
# 最佳实践:使用 python -u run.py 运行以获得实时日志输出
|
|
|
|
|
|
main()
|