fix: GLM思考泄漏修复+前端工作流显示优化+持仓迁移至数据看板
- llm.py: 移除 reasoning_content 回退(泄漏英文思考到前端),max_tokens 2000→4000(GLM thinking.enable=false 仍有少量思考,加大确保产出正式答案)
- AI页: Charles新闻摘要加滚动+自动换行;新增研报摘要展示区(LLM生成); Ethan执行结果改全宽+灰底+滚动(400px)+pre-wrap(解决截断和格式混乱)
- 持仓管理从 Monitor 迁至 Dashboard(数据看板:统计卡→ENSO→趋势图→持仓 管理全宽);Monitor 回归纯监控(保留持仓数统计,移除增删改查/QMT导入)
- 测试: 55项LLM测试通过, tsc/build 0错误
Co-Authored-By: Claude Code noreply@anthropic.com
版权所有:中国计算机学会技术支持:开源发展技术委员会
京ICP备13000930号-9
京公网安备 11010802047560号
AITrade - AI量化交易系统
基于ClassRoom课程的插件化、可扩展量化交易平台。
项目状态
Phase 4 完成内容(部分)
LSTM股价预测模型 - ✅ 已完成
Transformer时间序列预测 - ✅ 已完成
Phase 3 完成内容
LangGraph交易团队工作流 - ✅ 已完成
Phase 2 完成内容
Phase 3 完成内容(部分)
Kris风控体系(8条核心规则)- ✅ 已完成
事前风控(Pre-Trade):
事中风控(Intra-Trade):
事后风控(Post-Trade):
✅ CircuitBreakerRule - 熔断机制(连续亏损/冷却期)
✅ MacroGateRule - 宏观门控(VIX/失业率/通胀率)
✅ 单元测试:23个测试用例全部通过
强化学习交易策略 - ✅ 已完成
生产化路线(M0–M3)
在 Phase 1–4 功能骨架之上,按”先安全、再数据、再撮合、最后实盘”的顺序分四个里程碑推进生产化:
JWT_SECRET_KEY>SECRET_KEY> 仓库默认值,仍为默认值时启动打 error 日志)、compose 敏感值统一${VAR:-默认值}口径、MySQL 备份/恢复脚本scripts/backup_mysql.ps1(gzip 导出 + 保留 N 份 + 计划任务)、全业务端点 JWT 鉴权与 CORS 收敛(豁免清单见「已知限制」)。操作手册见本文「安全与备份」小节。get_daily的adjust参数契约)、行情/新闻/回测结果落库(wp-datawrite)、交易动作审计旁路(wp-audit,monitor/audit.py落盘留痕)、日度数据校验与下载任务状态机(Redis 后端 + 孤儿任务回收)。同期接入 ENSO 气候风险管道:NOAA CPC ONI →macro_daily表 →EnsoGateRule风控门控与 LangGraph 工作流气候上下文,详见 doc/17-ENSO气候风险集成.md。aitrade/execution/matching.py(T+1、一字涨跌停、停牌、量约束、手数取整、板块分档),回测接入事前风控,并以”同一数据、同一策略、两侧引擎”的一致性验收(scripts/consistency_check.py)锁住语义,详见 doc/18-M2撮合统一.md。TRADING_MODE三档开关(dry_run 默认 / paper / live)+ 双条件安全红线(live 需AITRADE_CONFIRM_LIVE=YES口令,缺一降级 paper)、只读预检脚本scripts/live_readiness_check.py、急停端点POST /api/live/halt、每日对账(券商持仓 ↔positions表)与工作流人工审批;灰度上线 checklist 与回滚步骤详见 doc/19-M3实盘装配.md。positions表有了交易写入方(execution/position_store.py成交旁路落库:paper 档写入,dry_run 档不写(内存账户重启即重建,防漂移),live 档成交回报通道待接入);两个 WebSocket(/ws/market、/api/download/ws/download)握手鉴权(查询参数?token=<JWT>,失败先 accept 再close(4401)使关闭码真实到达浏览器,前端对 4401 停止自动重连);DB 驱动交易日历(data/trading_calendar.py,调度守卫按stock_daily指数 bar 判历史节假日,当日/未来回退 weekday——当日 bar 收盘后才落库;增量同步已并入指数代码保证 bar 有写入方,见 doc/20 §8.1);dragon/grid 策略前视偏差修复(逐 bar 因果计算,滚动窗口);策略组合同标的双腿去重与权重真正生效;急停响应新增成功撤单计数cancelled_count;qmt-agent 同步任务异步 job 化(POST /sync/*立即返回job_id,GET /sync/jobs/{job_id}轮询取结果,容器不再挂一小时长连接,见 doc/20);日线清洗成交量缺失行剔除、下载任务 Redis 断连指数退避等杂项。aitrade/signal_engine.py纯 DB 计算(不经 QMT,容器内可跑):近 120 根 qfq 日线的 MACD(0.35)/RSI14(0.2)/MA5×MA20(0.25)/量能(0.2) 四因子加权打分([-100,100]),叠加近 7 日新闻情绪(±20 封顶,SentimentAnalyzer固定中文词典模式)与 ENSO 宏观门控(敏感行业 ONI≥warn 减半、≥block 剔除 buy,与EnsoGateRule共用enso_*配置);另产大盘择时三态(strong/neutral/weak,双指数 MA20/MA60 + 全市场宽度)与仓位帽(1.0/0.8/0.5)。信号落daily_signals表(只存 buy/sell 行,UNIQUE(trade_date,symbol),幂等覆盖),交易日 15:40 调度任务daily_signal_scan自动扫描 + 告警摘要(strong/weak 才推送)+ 日报reports/signals_YYYYMMDD.md;API 挂/api/signal(POST /signal/scan同步全量 /GET /signal/latest/GET /signal/history),前端/signal页新增「每日信号扫描」页签(大盘状态横幅 + 买卖榜单 + 重新扫描)。信号仅供研究参考,不构成投资建议;详见 doc/22-每日信号引擎.md。GET /api/monitor/positions增强——每行附中文名(stock_info)与最新前复权收盘现算的市值/盈亏(last_close/market_value/pnl/pnl_pct,无行情或成本未知为 null,库内估值字段以current_price/market_value_db/pnl_db兼容保留);POST /api/monitor/positions手工按 symbol upsert(未知代码/非正数量 422)、DELETE /api/monitor/positions/{symbol}(404 语义)、POST /api/monitor/positions/import-qmt从 miniQMT 券商持仓导入(agent/direct 双模式,只写数量/可用、成本不动,桥不可达/未配账户/空持仓 502+中文提示);/api/signal的 scan/latest 买/卖信号项新增held持仓标注、latest 顶层新增held_symbols。项目结构
快速开始
1. 环境准备
2. 配置环境变量
3. 启动服务(两种部署模式
QMT_MODE)direct 模式(默认,单机直跑 = 现状):基础设施进 Docker,后端留在宿主机本地跑, xtquant 直连 miniQMT(本地 IPC +
userdata_mini,无法进容器):agent 模式(方案A,业务容器化):前后端 + 数据库全进
docker-compose.yml, 宿主机只跑一个 qmt-agent 桥(aitrade/qmt_agent,默认0.0.0.0:8001), 容器内 api 经QMTGateway→http://host.docker.internal:8001访问 QMT 能力, agent 直连localhost:3306写库:安全默认:下单端点默认禁用(
QMT_AGENT_ALLOW_ORDERS默认 false,需容器与 agent 双侧同时开启才可用,并叠加实盘双条件,见 doc/19);agent 除GET /health外全部 要求X-Agent-Token鉴权。启动顺序、防火墙收紧、模式切换与故障排查详见 doc/20-QMT桥接与容器部署.md。4. 数据库迁移
核心数据表
stock_infoindustry行业列,Phase14 新增,NULL=未回填)aitrade industry-backfill回填(qmt 优先、东财兜底,见 doc/21)stock_dailyadj_factor)stock_minutesymbol + trade_date(bar时间) + period唯一,volume单位手;默认保留 90 天(minute_retention_days)POST /sync/minute管道 / 调度任务minute_sync(交易日 16:00),宇宙=指数+当前持仓或minute_sync_symbols固定清单stock_delisteddelisted_refresh)aitrade/data/delist.py::refresh_delisted_listmacro_dailyEnsoService(NOAA CPC,见 doc/17)positionsexecution/position_store.pydaily_signalstrade_date+symbol唯一,action(buy/sell/hold) + score + factors/reasons(JSON) + 大盘状态快照market_regimesignal_engine.persist_signals(15:40 任务daily_signal_scan/POST /api/signal/scan,见 doc/22)使用示例
数据采集
策略回测
风控管理
开发
运行测试
代码检查
代码格式化
已知限制
以下是当前版本的已知问题与边界,使用前请知悉(不构成投资建议):
stock_delisted表已落地 akshare 沪深退市名单(月度刷新delisted_refresh,见 doc/21),选股/下载可据其排除退市标的;但退市标的的历史行情(尤其本系统开始下载数据前已退市、从未入库的部分)QMT/akshare 无稳定回补渠道——量化幸存者偏差或做含退市股的全市场回测需外部采购(tushare 退市日线积分、商用数据)。stock_info仍只增不删、存量stock_daily中退市股行保留(历史事实数据)。web/app.py启动时按TRADING_MODE三档开关装配实盘引擎并注入set_live_engine()(默认dry_run,/api/live/*正常可用;仅装配异常时才 503”实盘引擎未初始化”)。装配挂的是AssemblyPlaceholderStrategy/DataSource(无信号、无行情),POST /api/live/start后引擎空转,绑定真实策略/数据源属后续工作。live 档必须同时满足TRADING_MODE=live与环境变量AITRADE_CONFIRM_LIVE=YES(严格等于 YES),缺一即拒绝并降级 paper;急停POST /api/live/halt,档位核对GET /api/live/mode,完整操作手册见 doc/19-M3实盘装配.md。web/api/__init__.py对 data/strategy/backtest/ai/portfolio/analysis/monitor/live/settings/workflow/macro/download(HTTP)等业务路由组统一挂了require_authenticated——缺失/伪造/过期 token 一律 401(/api/auth/me自带get_current_user,仍受保护)。有意豁免(保持匿名):/api/health*、/api/auth/login|register、根/health。两个 WebSocket/ws/market与/api/download/ws/download因浏览器 WS 无法携带 Authorization 头,改为握手阶段校验查询参数?token=<JWT>(web/websocket.py::authenticate_websocket,与 HTTP 共用同一解码校验,失败close(4401, unauthorized),前端getWebSocketUrl已自动附带 token);本地调试可用WS_AUTH=false关闭该握手鉴权(默认开)。另注意 JWT 签名密钥仍是仓库公开默认值时任何读过源码的人可伪造 token,生产必须在.env配置SECRET_KEY(启动时检测到默认值会打 error 日志,见「安全与备份」)。MacroGateRule所需的vix/unemployment_rate/inflation_rate目前没有任何管道写入,规则实际处于”无数据即放行”的空转状态;ENSO 管道(NOAA CPC ONI →macro_daily表)是第一条真正落地的宏观指标数据链路,后续补齐其余宏观指标时可参考其模式(详见 doc/17-ENSO气候风险集成.md)。qmt.py/qmt_real.py已弃用,实盘统一走xtquant_trader**:这两份早期 miniQMT 封装是”幻觉客户端”——下单调用的是 xtquantXtTrader上并不存在的buy/sellkwargs 便捷 API,connect()也只是裸构造即置已连接(qmt_real还缺AITRADE_CONFIRM_LIVE实盘双条件护栏),接真实 miniQMT 必挂。现已整体弃用:保留仅为兼容既有 import(实例化会抛DeprecationWarning并打醒目日志),观察期后删除;实盘下单/撤单/持仓/账户查询统一使用aitrade/execution/xtquant_trader.py(唯一真实客户端),编排与安全护栏走live.py的LiveTradingEngine。安全与备份
默认凭据清单(开发默认值,生产必须覆盖)
两个 compose 文件(
docker-compose.yml/docker-compose-infra.yml)中的敏感值均以${VAR:-默认值}形式从环境变量或根目录.env读取,变量名与默认值两边完全一致(口径统一)。默认值与已初始化的数据卷保持一致,仅供本地开发:SECRET_KEY/JWT_SECRET_KEYchange-me-in-production(Settings 默认)/ 仓库公开常量JWT_SECRET_KEY环境变量,缺省回落SECRET_KEY(.env 即可);仍是已知默认值时启动打 error 日志,任何读过源码的人可伪造 admin tokenMYSQL_ROOT_PASSWORDaitrade123aitrade_mysql-data首次初始化时生效,之后以库内实际密码为准REDIS_PASSWORDaitrade123--requirepass启动INFLUXDB_USERNAME/INFLUXDB_PASSWORDadmin/aitrade123aitrade_influxdb-data首次初始化时生效INFLUXDB_BUCKET/INFLUXDB_ORGmarket_data/aitradeGRAFANA_ADMIN_PASSWORDadmingrafana-data首次初始化时生效LANGFUSE_DB_PASSWORDlangfuseLANGFUSE_NEXTAUTH_SECRET/LANGFUSE_SALT生成强密钥(生产覆盖默认值时使用):
其中
SECRET_KEY(及可选的JWT_SECRET_KEY,优先级更高)是后端 JWT 签名密钥:api/migrate 容器已做透传,写入根目录.env即生效;轮换后所有存量 token 立即失效(30 分钟有效期窗口,影响可忽略)。将生成的值写入根目录
.env(参考.env.example的「Docker 基础设施密钥」小节),docker compose 会自动读取。密码轮换步骤
注意:直接改
.env里的MYSQL_ROOT_PASSWORD不会改变已初始化 MySQL 卷里的真实密码(该变量只在数据卷首次初始化时生效)。正确顺序是先改库内密码,再同步.env:其他服务的轮换:
.env中REDIS_PASSWORD(同时更新REDIS_URL/CELERY_BROKER_URL/CELERY_RESULT_BACKEND中的密码),docker compose -f docker-compose-infra.yml up -d redis即生效(密码不涉及数据卷)。docker exec -it aitrade-influxdb-1 influx user password --name admin(同时更新.env的INFLUXDB_PASSWORD);API Token 建议在 Web UI(http://localhost:8086 → Load Data → API Tokens)生成后更新.env的INFLUXDB_TOKEN。LANGFUSE_NEXTAUTH_SECRET轮换会使已登录会话失效(可接受);LANGFUSE_SALT轮换会导致历史加密数据无法解密,如必须轮换请先备份aitrade_langfuse-db-data卷并评估影响。备份与恢复
备份(Windows,导出 gzip 到
./backups/,默认保留最近 7 份):脚本要点:
docker exec在容器内执行mysqldump | gzip,再docker cp拷回,避免 PowerShell 重定向损坏二进制流;密码默认读容器内MYSQL_ROOT_PASSWORD,不落在命令行参数里;输出目录会自动生成.gitignore防止误提交。可注册 Windows 计划任务每日执行:恢复(一条龙,
<文件>换成实际备份文件名):文档
详细设计文档请参考
doc/目录。License
MIT