更新qmt代码,支持多端qmt登录
This commit is contained in:
@@ -8,37 +8,46 @@ from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import FileResponse
|
||||
from pydantic import BaseModel
|
||||
|
||||
from qmt_engine import QMTEngine, QMTStatus
|
||||
# 导入新的管理器类
|
||||
from qmt_engine import MultiEngineManager, TerminalStatus
|
||||
|
||||
|
||||
# ================= Pydantic模型 =================
|
||||
class StatusResponse(BaseModel):
|
||||
"""状态响应模型"""
|
||||
running: bool
|
||||
qmt_connected: bool
|
||||
start_time: str
|
||||
last_loop_update: str
|
||||
account_id: str
|
||||
|
||||
class TerminalStatusModel(BaseModel):
|
||||
"""单个终端状态模型"""
|
||||
qmt_id: str
|
||||
alias: str
|
||||
account_id: str
|
||||
is_connected: bool
|
||||
last_heartbeat: str
|
||||
|
||||
class StatusResponse(BaseModel):
|
||||
"""全局状态响应模型"""
|
||||
running: bool
|
||||
start_time: str
|
||||
terminals: List[TerminalStatusModel]
|
||||
|
||||
class PositionsResponse(BaseModel):
|
||||
"""持仓响应模型"""
|
||||
real_positions: List[Dict[str, Any]]
|
||||
# 按 qmt_id 分组的实盘持仓
|
||||
real_positions: Dict[str, List[Dict[str, Any]]]
|
||||
# 按策略名分组的虚拟持仓
|
||||
virtual_positions: Dict[str, Dict[str, str]]
|
||||
|
||||
|
||||
class LogsResponse(BaseModel):
|
||||
"""日志响应模型"""
|
||||
logs: List[str]
|
||||
|
||||
|
||||
# ================= FastAPI应用 =================
|
||||
|
||||
class QMTAPIServer:
|
||||
"""QMT API服务器"""
|
||||
"""多终端 QMT API服务器"""
|
||||
|
||||
def __init__(self, qmt_engine: QMTEngine):
|
||||
self.app = FastAPI(title="QMT Monitor")
|
||||
self.qmt_engine = qmt_engine
|
||||
def __init__(self, manager: MultiEngineManager):
|
||||
self.app = FastAPI(title="QMT Multi-Terminal Monitor")
|
||||
self.manager = manager
|
||||
self._setup_middleware()
|
||||
self._setup_routes()
|
||||
|
||||
@@ -61,41 +70,78 @@ class QMTAPIServer:
|
||||
return FileResponse("dashboard.html")
|
||||
return {"error": "Dashboard not found"}
|
||||
|
||||
@self.app.get("/api/status", response_model=StatusResponse, summary="获取系统状态")
|
||||
@self.app.get("/api/status", response_model=StatusResponse, summary="获取所有终端状态")
|
||||
def get_status():
|
||||
"""获取QMT连接状态和系统信息"""
|
||||
status = self.qmt_engine.get_status()
|
||||
"""获取所有 QMT 终端的连接状态"""
|
||||
terminal_data = self.manager.get_all_status()
|
||||
|
||||
terminals = [
|
||||
TerminalStatusModel(
|
||||
qmt_id=t.qmt_id,
|
||||
alias=t.alias,
|
||||
account_id=t.account_id,
|
||||
is_connected=t.is_connected,
|
||||
last_heartbeat=t.last_heartbeat
|
||||
) for t in terminal_data
|
||||
]
|
||||
|
||||
return StatusResponse(
|
||||
running=status.is_running,
|
||||
qmt_connected=status.is_connected,
|
||||
start_time=status.start_time,
|
||||
last_loop_update=status.last_heartbeat,
|
||||
account_id=status.account_id
|
||||
running=self.manager.is_running,
|
||||
start_time=self.manager.start_time,
|
||||
terminals=terminals
|
||||
)
|
||||
|
||||
@self.app.get("/api/positions", response_model=PositionsResponse, summary="获取持仓信息")
|
||||
def get_positions():
|
||||
"""获取实盘和虚拟持仓信息"""
|
||||
positions = self.qmt_engine.get_positions()
|
||||
"""汇总所有终端的实盘持仓和所有策略的虚拟持仓"""
|
||||
real_pos_data = {}
|
||||
virtual_pos_data = {}
|
||||
|
||||
# 1. 遍历所有终端单元获取实盘持仓
|
||||
for qmt_id, unit in self.manager.units.items():
|
||||
positions = []
|
||||
if unit.callback and unit.callback.is_connected:
|
||||
try:
|
||||
xt_pos = unit.xt_trader.query_stock_positions(unit.acc_obj)
|
||||
if xt_pos:
|
||||
positions = [
|
||||
{
|
||||
"code": p.stock_code,
|
||||
"volume": p.volume,
|
||||
"can_use": p.can_use_volume,
|
||||
"market_value": round(p.market_value, 2)
|
||||
} for p in xt_pos if p.volume > 0
|
||||
]
|
||||
except:
|
||||
pass
|
||||
real_pos_data[qmt_id] = positions
|
||||
|
||||
# 2. 遍历所有策略获取虚拟持仓
|
||||
for s_name in self.manager.config.get('strategies', {}).keys():
|
||||
v_data = self.manager.pos_manager.get_all_virtual_positions(s_name)
|
||||
virtual_pos_data[s_name] = v_data
|
||||
|
||||
return PositionsResponse(
|
||||
real_positions=positions["real_positions"],
|
||||
virtual_positions=positions["virtual_positions"]
|
||||
real_positions=real_pos_data,
|
||||
virtual_positions=virtual_pos_data
|
||||
)
|
||||
|
||||
@self.app.get("/api/logs", response_model=LogsResponse, summary="获取日志")
|
||||
def get_logs(lines: int = Query(50, ge=1, le=1000, description="返回日志行数")):
|
||||
"""获取最近的交易日志"""
|
||||
logs = self.qmt_engine.get_logs(lines)
|
||||
@self.app.get("/api/logs", response_model=LogsResponse, summary="获取系统日志")
|
||||
def get_logs(lines: int = Query(50, ge=1, le=1000)):
|
||||
"""获取最近的系统运行日志"""
|
||||
logs = self.manager.get_logs(lines)
|
||||
return LogsResponse(logs=logs)
|
||||
|
||||
@self.app.get("/api/health", summary="健康检查")
|
||||
def health_check():
|
||||
"""健康检查接口"""
|
||||
status = self.qmt_engine.get_status()
|
||||
if status.is_running and status.is_connected:
|
||||
"""健康检查:只要有一个终端在线即视为正常"""
|
||||
terminal_data = self.manager.get_all_status()
|
||||
any_connected = any(t.is_connected for t in terminal_data)
|
||||
|
||||
if self.manager.is_running and any_connected:
|
||||
return {"status": "healthy", "timestamp": datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
||||
else:
|
||||
return {"status": "unhealthy", "timestamp": datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
||||
return {"status": "unhealthy", "reason": "No terminals connected", "timestamp": datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
||||
|
||||
def get_app(self) -> FastAPI:
|
||||
"""获取FastAPI应用实例"""
|
||||
@@ -103,7 +149,8 @@ class QMTAPIServer:
|
||||
|
||||
|
||||
# ================= 辅助函数 =================
|
||||
def create_api_server(qmt_engine: QMTEngine) -> FastAPI:
|
||||
"""创建API服务器"""
|
||||
server = QMTAPIServer(qmt_engine)
|
||||
return server.get_app()
|
||||
|
||||
def create_api_server(manager: MultiEngineManager) -> FastAPI:
|
||||
"""创建API服务器入口"""
|
||||
server = QMTAPIServer(manager)
|
||||
return server.get_app()
|
||||
Reference in New Issue
Block a user