Skip to main content
Glama
JingxuanC

Factor Miner MCP

Factor Miner MCP

A 股量化因子挖掘工具集的独立 MCP(Model Context Protocol)服务。从 Athena 的 py-sidecar 中抽取 factor 域,让任何 MCP 客户端(Claude Desktop、Kimi Code、Cursor、自研 Agent)都能直接驱动完整的「因子挖掘 → 评估 → 回测 → 上线巡检」流水线。

工具清单(17 个)

因子挖掘

工具

说明

负载

factor_execute

沙箱执行 factor.py(import 白名单 + rlimit + 120s 超时),跑评估电池

重(异步)

factor_backtest

全量 qlib 回测:SOTA 因子 + 新因子对齐 Alpha20 baseline,qrun 出指标(可交给独立执行器跑,见下文)

重(异步)

factor_oos_check

生产准入 OOS 检查:挖掘窗口 vs 纯样本外窗口,报告 IC/年化/回撤 + 衰减(同上,可外置)

重(异步)

factor_daily_compute

每日收盘后计算在线因子,写 Redis dfactor:{symbol}(TTL 48h)

重(异步)

update_data

qlib cn_data 每日增量更新(三源熔断 + 原子切换)+ 重建 h5 数据集

重(异步)

factor_evaluate

一段因子代码 → 专业评估(一条链):沙箱跑代码 + 同窗口切片对齐的 alphalens 口径 tearsheet,附单调性 / IC 半衰期 / IC t 值 / 假设缺失标记。补上 factor_execute(只有契约检查)与 factor_tearsheet(要自带 factor_values+klines)之间的断链

重(异步)

factor_recent_ic

衰减巡检:近 N 交易日截面 IC(纯 pandas,无需 qlib)

compute_factors

从 OHLCV K线计算 Alpha158 风格因子(纯 pandas)

predict

因子值 → ML 信号预测

ML

工具

说明

负载

ml_train_rolling

滚动训练:K线 → Alpha158 因子 + 次日收益标签 → 扩张窗训练,输出 IC/RankIC/Sharpe。model="lgbm"(默认)走 LGBM;model="master" 走 MASTER 深度学习后端

重(异步)

ml_predict

用滚动模型出次日收益预测(lgbm 优先 Redis 因子快照;master 用最近 seq_len 天特征序列)

ml_metrics

训练器状态:最近训练日、逐日指标、模型类型(lgbm/master)、特征清单

MASTER 深度学习后端(model="master"

MASTER(AAAI 2024,微软 qlib benchmark 收录)是股票专用 Transformer:日内时序注意力(TAttention)+ 截面股票间注意力(SAttention)+ market-guided gating(用市场上下文门控 个股特征)。本实现复用与 LGBM 相同的 point-in-time Alpha158 因子(无未来 函数),batch 为「同一交易日的全部股票截面」,市场上下文取当日截面特征的 mean/std。网络结构 vendor 自官方仓库(factor_miner/master_nn.py,MIT)。

// 训练(异步 job,返回 job_id 后用 job_status 轮询)
{"name": "ml_train_rolling", "arguments": {
  "model": "master", "klines_list": [...], "seq_len": 8, "epochs": 3}}
// 预测 / 状态
{"name": "ml_predict", "arguments": {"model": "master", "klines_list": [...]}}
{"name": "ml_metrics", "arguments": {}}

注意事项:

  • 截面模型:至少 4 只有效股票才训练(建议 ≥10 只);max_symbols 默认 50 上限校验(CPU 保护)。

  • CPU 默认值偏小seq_len=8 / epochs=3 / d_model=64torch.set_num_threads(2)。2 核 / 3G 内存服务器上,20 股 × 2 年 量级约几分钟(瓶颈在逐日因子重算,与 LGBM 路径相同);加大 epochs 或股票数会线性变慢,训练全程走异步 job 不占 HTTP 连接。

  • 特征归一化统计量只用训练段,模型 + 归一化器一起存 FACTOR_MINER_MODEL_DIR/master.pt

  • 需要 torch(CPU 版,Docker 镜像已内置;本地 pip install torch)。

因子评估与组合

工具

说明

负载

factor_tearsheet

Alphalens 式因子完整评估:分位数组收益、多空价差、IC 序列(均值/IR/衰减)、换手率(手写 pandas)

portfolio_optimize

组合优化:HRP / 等权 / 最小方差内置;装 pypfopt 后支持 mean_variance(max Sharpe)

regime_detect

牛/熊/震荡识别:规则状态机(动量 + 已实现波动率阈值)内置;装 hmmlearn 走 GaussianHMM

change_point

结构突变检测:CUSUM + 二分分割(水平 + 漂移两路)内置;装 ruptures 走 PELT

vol_forecast

波动率预测:EWMA(λ=0.94)+ Parkinson 高低价参考内置;装 arch 走 GARCH(1,1)

重负载工具提交即入队返回 job_id,轮询 GET /jobs/<id> 拿结果, 不占 HTTP 连接。

factor_evaluate:从「一段代码」到「专业指标」的一条链

factor_execute 只回契约检查(eval_ok / eval_detail),不回因子值factor_tearsheet 要调用方自带 factor_values + klines。两者之间本来是断的 ——任何 agent 都拿不到「代码 → 分层收益 / IC / 换手」。factor_evaluate 接上它:

factor_evaluate(code, hypothesis?) 
  = 沙箱跑 code(_run_factor_window:白名单 + rlimit + 120s)
  + 同一次窗口切片给的 close(因子值与价格天然对齐,不做事后 intersect)
  + analytics.factor_tearsheet(alphalens 口径,原样透传)
  + 专业摘要:严格单调性(+rho) / IC 半衰期(决定持有期) / IC t 值 / 假设缺失标记

实测(真实 daily_pv_all.h5,333MB,4809 标的,window_days=120):

阶段

耗时

stage_h5_for_sandbox(Fixed 格式只能整读,截窗 + 写盘)

117.7s

沙箱跑因子代码

27.2s

读回 result.h5 + 窗口 close

2.9s

拼面板

12.3s

tearsheet

18.6s

所以它必须走异步队列server.pyASYNC_TOOLS)——一次评估是分钟级, 不是秒级。整读全量的开销与 factor_execute / factor_recent_ic 同源, 不是本工具引入的。

实现说明与算法出处

  • factor_tearsheet 手写 pandas 而非依赖 alphalens 本体:alphalens 已半停维护(上游多年无实质更新),其依赖链与 pandas>=2 冲突频发; 分位数收益 / IC / 换手率逻辑本身很短,手写可控、可测、零额外依赖。

  • HRP — López de Prado (2016), Building Diversified Portfolios that Outperform Out-of-Sample(相关距离 → 层次聚类 → 拟对角化 → 递归二分)。

  • EWMA — RiskMetrics (1996), J.P. Morgan Technical Document,λ=0.94, 多期预测平坦外推。

  • PELT — Killick et al. (2012), Optimal Detection of Changepoints With a Linear Computational Cost, JASA(可选增强,内置为 CUSUM(Page 1954)

    • 二分分割)。

  • HMM — GaussianHMM(收益率 + 滚动波动率两特征,可选增强,内置为规则 状态机)。

  • 全部 5 个工具:纯 numpy/pandas/scipy 路径开箱可用,重库 (pypfopt / hmmlearn / ruptures / arch)惰性导入做可选增强,输出 method 字段标注实际实现。

Related MCP server: Vintage

快速开始

pip install -r requirements.txt
python3 server.py --port 50053

验证:

curl http://127.0.0.1:50053/health
curl http://127.0.0.1:50053/tools

接入 MCP 客户端(以 Claude Desktop / Kimi Code 为例):

# mcp 配置
factor:
  url: http://127.0.0.1:50053/mcp

Docker 部署

无需本地 Python 环境,一条命令起服务:

docker compose up -d        # 构建镜像 + 启动容器(首次构建约 3-5 分钟)
docker compose ps           # 查看状态
docker compose logs -f      # 跟踪日志

验证:

curl http://127.0.0.1:50053/health
curl http://127.0.0.1:50053/tools   # 应返回 16 个工具

说明与限制:

  • pyqlib 按构建架构自动处理(Dockerfile WITH_QLIB build-arg,默认 auto):

    • amd64(云服务器常见架构):自动安装官方 manylinux wheel, factor_backtest / gen_data / update_data 开箱即用;

    • aarch64(Apple Silicon / ARM 服务器):PyPI 无 linux/aarch64 wheel, 默认跳过,回测/数据工具返回明确错误,其余 12 个工具不受影响。 需要时用源码编译构建:docker build --build-arg WITH_QLIB=1 (慢,约 10-20 分钟,构建期需能访问 GitHub;拉不动可换镜像: --build-arg QLIB_GIT_URL=https://gitee.com/mirrors/qlib.git); 或直接用 docker run --platform linux/amd64 跑 amd64 镜像 (QEMU 模拟,慢但能用)。

    • 完全禁用:--build-arg WITH_QLIB=0。 国内构建加速:--build-arg PIP_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple

  • h5 数据集可通过 volume 挂载: - ./data:/app/data 并设 FACTOR_MINER_DATA_DIR=/app/data/factor_mining

  • lightgbm 已随镜像安装(镜像内含 libgomp1 OpenMP 运行时), ml_train_rolling / ml_predict 可用。训练出的模型默认落在容器 /tmp/athena_models/(重建即丢),生产部署请挂卷并设 FACTOR_MINER_MODEL_DIR=/app/models

  • torch(CPU 版)已随镜像安装(MASTER 深度学习后端):离线/代理环境 可把预下载的 torch CPU wheel 放进 wheels/(文件名 torch-*.whl) 构建期离线安装;否则按 PIP_INDEX_URL / 官方 CPU 源在线装。 注意镜像体积因此增加约 300-800MB(视 torch 版本),3G 内存服务器 训练 MASTER 时建议股票数 ≤50、epochs ≤5,避免与 qlib 回测类重任务 并发(队列 worker 数可用 MCP_WORKERS=1 压低)。

  • Redis 缓存(dfactor:* 写入)可选:设置 REDIS_URL 指向可达的 Redis,缺失时自动降级跳过缓存写入。

license 鉴权(可选):在 docker-compose.yml 中取消注释,把宿主机 licenses.json 挂进容器并设置 MCP_LICENSE_FILE

environment:
  MCP_LICENSE_FILE: /app/licenses/licenses.json
volumes:
  - ./licenses.json:/app/licenses/licenses.json:ro

数据准备(回测类工具需要)

factor_execute / factor_evaluate / factor_backtest / factor_oos_check 依赖 qlib cn_data 导出的日频量价 h5 数据集:

pip install pyqlib  # arm64 Linux 需从 GitHub 源码编译,见 requirements.txt 注释
python3 -m factor_miner.update_data          # qlib cn_data 增量更新
python3 -m factor_miner.gen_data --debug     # 调试集(100 股 × 2 年)
python3 -m factor_miner.gen_data --full      # 全量(回测用)

factor_recent_ic / compute_factors / predict 纯 pandas 实现, 不依赖 qlib,开箱即用。

把 qrun 拆出去跑(factor_executor/,可选)

qrun 是全市场 LGBM 训练 + 组合回测,内存是 GB 级;而 miner 容器通常被限制在 1.5 GiB 左右。两者塞在同一个 cgroup 里的后果实测过:

  • qrun 撞顶 → 内核 memcg 把整个 miner 容器杀掉(不是只杀那个任务)→ unless-stopped 重启 → 客户端看到 RemoteProtocolError

  • 失败的 qrun 会变成孤儿进程继续占内存,把容器卡在 1522/1536 MiB, 此后任何请求都必然 OOM,只能重启清场

factor_executor/只有第 4 步(执行 + 解析)搬到一个独立进程/容器:

miner 容器(轻)                          executor 容器(重)
  Step 1 沙箱跑因子                          qrun(自有 cgroup 上限)
  Step 2 去重闸门(日频 IC)        ──►      LGBM 训练 + 组合回测
  Step 3 拼 combined_factors_df.h5          解析 mlruns → metrics
  Step 4 交给执行器(若已配置)

Step 1–3 留在本地,因为它们本来就轻、且依赖面板归一化与 exec_cache。

开启方式:miner 侧设 FACTOR_EXECUTOR_URL,空 = 继续本地执行(默认,行为不变)。

共享文件系统是硬前提:请求里传的是路径不是文件内容(几十 MB 的面板不适合 走 HTTP 请求体),所以 miner 与执行器必须把宿主机上的 backtest 根目录挂到同一个 容器路径(默认 /work)。不一致时提交会被明确拒绝,而不是等 qrun 报一个看不懂的错。

qlib 数据必须挂祖父目录:日更的 atomic_swap 会整体 rename 替换 cn_data, 挂更深的子目录会在一次日更后指向已删除的 inode。所以挂 qlib_data 这一层:

# compose 片段(执行器)
factor-executor:
  build: {context: ./factor-miner-mcp}
  command: ["python3", "-m", "factor_executor.server"]
  environment:
    EXECUTOR_JOB_ROOT: /work
    EXECUTOR_QLIB_ROOT: /qlib_data
    EXECUTOR_PANEL_H5: /data/factor_mining/daily_pv_all.h5
    EXECUTOR_MEM_LIMIT_MB: "7168"    # qrun 子进程的 RLIMIT_AS(见下方实测)
    EXECUTOR_TIMEOUT: "1800"
  volumes:
    - ./backtests:/work                       # 与 miner 同挂同一宿主目录
    - /opt/athena-mcp/qlib_data:/qlib_data:ro # ⚠️ 祖父目录
    - /opt/athena-mcp/data/factor_mining:/data/factor_mining:ro

miner 侧对应加 FACTOR_EXECUTOR_URL=http://factor-executor:50054,并把 <backtests>:/work 也挂上。

数据版本会校验:请求带 miner 侧的面板版本(manifest 的 h5_sha256 优先), 执行器比对不一致就拒绝执行 —— 面板与执行器 qlib 数据不同源时回测会静默偏掉, 那比失败糟糕得多。

失败隔离:每个 job 一个独立会话/进程组,超时或失败都按收割(killpg), 不会再留孤儿;-9 会被翻译成"很可能是内存不足"的提示。执行器崩了不影响 miner 的其它工具。

qrun 要多少内存(生产实测,别再按猜的值配)

factor_oos_check 走 full profile = market: csi300 + start_time 2008 + test_end null, 也就是全量 csi300 跨十余年的 Alpha158 特征 + LGBM 训练 + 组合回测。在这台机器 (7.4G 总内存)上实测:

配置

结果

RLIMIT_AS = 5120 MiB

失败 —— qrun 90s 后 MemoryError: Unable to allocate 80.0 MiB;申请 80MiB 都失败说明是地址空间耗尽,不是机器没内存

RLIMIT_AS 不限,容器 10 GiB

成功,cgroup 峰值 3783 MiB

RLIMIT_AS = 7168 MiB + 容器 mem_limit: 8192m

成功,cgroup 峰值 3581 MiB(留约 2× 余量)

要注意的是虚拟地址空间,不是 RSS:5120 MiB 的 RAS 都过不去,而实际 RSS 只有 ~3.6 GiB —— qlib/pandas 的 mmap 与中间对象让 VA 明显高于常驻。所以这个上限不能照着 "RSS × 1.2" 去设。

mem_limitRLIMIT_AS 刻意都设、且 RLIMIT_AS < mem_limit:前者是容器护栏, 后者让 qrun 先于容器被自己杀掉(不会拖累执行器进程本身)。

相关测试:test_factor_executor.py(用假 qrun 驱动,不需要 pyqlib,本机可跑)。

每日数据同步(update_data)

update_data 已封装为 MCP 工具(异步 job):交易日历对齐 → 三源熔断抓取 (mootdx → 腾讯 → 东财)→ raw+qfq 复权对齐 → staging 校验 → 原子切换 → 自动重建 daily_pv_all.h5。任一环节失败保留旧数据,退出码非 0。

部署要点:

  • 持久化:qlib cn_data 必须挂卷(compose 里 ./qlib_data:/app/qlib_data + QLIB_PROVIDER_URI=/app/qlib_data/cn_data),否则容器重建数据就没了; h5 数据集挂 ./data:/app/data

  • 冷启动:增量更新从既有 cn_data 日历尾部续抓,首次需要一份基础数据 (从现有 Athena 部署拷贝 ~/.qlib/qlib_data/cn_data,或 qlib 社区 dump), 之后每日只增量。

  • 定时调度:宿主机 crontab 每个交易日收盘后调一次(非交易日自动空转):

# 周一到周五 15:40 / 18:10 各跑一轮(收盘后数据落定有延迟,双轮兜底)
40 15 * * 1-5 curl -s -X POST http://127.0.0.1:50053/mcp -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"update_data","arguments":{}}}'
10 18 * * 1-5 curl -s -X POST http://127.0.0.1:50053/mcp -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"update_data","arguments":{}}}'

返回 job_id,轮询 GET /jobs/<id> 拿结果;也可用 CLI: docker exec factor-miner-mcp python3 -m factor_miner.update_data --limit 10(冒烟)。

鉴权与额度(可选)

默认开放模式(本地/内网)。设置环境变量后强制 license key 鉴权:

export MCP_LICENSE_FILE=/path/to/licenses.json
python3 server.py --port 50053
# 客户端请求头:X-License-Key: <key>

license JSON 格式与额度语义见 mcp_gateway.py docstring。GET /quota 查余量,GET /queue-stats 看队列。

端点一览

GET  /health        健康检查
GET  /tools         工具 JSON schema 列表
POST /mcp           MCP JSON-RPC(initialize / tools/list / tools/call)
GET  /jobs/<id>     异步任务状态/结果
GET  /quota         license 额度余量(鉴权模式)
GET  /queue-stats   队列概况
GET  /metrics       Prometheus 指标(无需鉴权)

可观察性 / Observability

GET /metrics 输出 Prometheus text exposition 格式(无需鉴权,仅工具名级聚合):

  • mcp_tool_calls_total{tool,status} — 调用计数,status ∈ ok/error/rejected_license/rejected_quota/queued

  • mcp_tool_latency_seconds_sum{tool} / mcp_tool_latency_seconds_count{tool} — 延迟累计/次数(异步任务从入队到完成)

  • mcp_license_check_total{result} — license 校验计数(ok/invalid)

  • mcp_uptime_seconds — 进程启动至今秒数

  • mcp_queue_depth — 当前排队任务数(gauge)

  • mcp_queue_jobs_total{status} — 异步任务完成计数(done/error)

Prometheus 抓取配置示例:

scrape_configs:
  - job_name: factor-miner-mcp
    metrics_path: /metrics
    static_configs:
      - targets: ["127.0.0.1:50053"]

沙箱安全模型

factor_execute / factor_evaluate 等执行用户提交的 factor.py 时在子进程沙箱中运行: AST import 白名单(仅 pandas/numpy 等)、rlimit 资源限制、120s 超时、 隔离工作目录。实现见 factor_miner/sandbox.py

致谢

  • factor_miner/gen_data.py 移植自 microsoft/RD-Agent(MIT)

  • factor_miner/qlib_dump_bin.py 裁剪自 microsoft/qlib v0.9.6(MIT)

  • factor_miner/master_nn.py vendor 自 SJTU-DMTai/MASTER(AAAI 2024,MIT)的 qlib 提交版(qlib 0.9.7 的 contrib 未合入 MASTER,故本地内置)

  • 本项目主体来自 Athena

License

MIT

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    MCP server that wraps SFC financial data API into 32 tools for comprehensive A-share market data, including real-time quotes, rankings, limit-up statistics, news, themes, financials, charts, research reports, and watchlists.
    -
  • A
    license
    A
    quality
    A
    maintenance
    Provides point-in-time financial data access and an honest backtesting engine via MCP, enabling users to research restated fundamentals, run backtests with deflated Sharpe metrics, and benchmark returns against published factors.
    8
    1
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Provides A-share causal analysis capabilities including event studies, counterfactual validation, causal graph learning, and econometric inference through MCP, enabling clients to run complete causal inference workflows.
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI agents to run a full quant research workflow over MCP: pulling data, authoring and backtesting strategies, running statistical validation and risk checks, and recording findings for future sessions.
    1
    MIT