Skill: listolany/quant-strategy-assistant (version 1.0.2)

--- name: quant-strategy-assistant description: "量化策略助手：自然语言→策略生成→回测→优化→QMT模拟/实盘。三轮交互闭环。" metadata: requires: bins: ["python3"] openclaw: requires: bins: ["python3"] --- # 量化策略助手 > **兼容说明**：本文档既是 OpenClaw Skill，也可作为任意 AI Agent 的操作手册。只要 Agent 能读取文件、执行命令、生成代码，即可按下方协议使用。YAML 头部的 `metadata.openclaw` 为 OpenClaw 平台专用字段，其他 Agent 忽略即可。 回测三轮交互闭环 → 用户按需选择参数优化 / 模拟实盘。 ## 能力分层 | 能力 | 引擎 | 依赖 | 说明 | |------|------|------|------| | **CTA回测** | vnpy_ctastrategy | python3 + qgdata | 单标的择时策略，任意平台 | | **Portfolio回测** | vnpy_portfoliostrategy | python3 + qgdata | 多标的组合/轮动策略，任意平台 | | **参数优化** | vnpy OptimizationSetting | 同回测 | 回测后由用户触发，穷举/遗传算法 | | **模拟/实盘** | miniQMT + vnpy CTA引擎 | QMT 交易端 | `qmt-check` 检测 → `trade` 启动自动交易，含探针单验证 | 回测是核心能力，不依赖 QMT。用户请求模拟/实盘时才运行 `qmt-check`。 ### CTA vs Portfolio 自动路由（强制） | 条件 | 模式 | 策略基类 | |------|------|----------| | 单一标的 + 无组合关键词 | `cta` | `CtaTemplate` | | 以下任一条件满足 | `portfolio` | `StrategyTemplate`（vnpy_portfoliostrategy） | **portfolio 判定规则（分层，LLM 和 `parse_requirement()` 均遵循）**： 1. **强信号**（任一出现即 portfolio）：`轮动/组合/多标的/全市场/等权/仓位分配/前N名` 2. **弱信号 + 多标的上下文**（二者共现才 portfolio）：弱信号 `排序/筛选/选股/调仓/排列/持仓周期` 需同时存在多标的上下文（`板块/成分股/指数/行业/概念/股票池`） 3. **标的数量 > 1**：`--symbols` 含 2 只以上自动 portfolio 注意："多头排列"中的"排列"、"均线排序"在单标的场景不触发 portfolio。 路由由 `parse_requirement()` 自动判定并写入 `parsed["mode"]`，agent 生成策略代码时必须使用对应基类。 **Portfolio 引擎驱动约束**：Portfolio 回测引擎仅支持 DAILY 和分钟级驱动。周级策略用 `--interval DAILY`，在 `on_bars(bars)` 中按 `list(bars.values())[0].datetime.weekday()==0`（周一）判断调仓日；周线指标直接用 `pro.weekly()` 获取，无需从日线合成。 **股票池所有权契约（强制）**：股票池由 `--symbols` 参数唯一定义 → 引擎加载数据 → 策略通过 `self.vt_symbols` 接收。策略代码 `__init__` 中**禁止覆盖 `vt_symbols`**，必须使用引擎传入的列表。所有标的代码使用 vnpy 格式（`600519.SSE`/`000858.SZSE`），不可使用 qgdata 格式（`.SH/.SZ`）。选股逻辑在 `on_bars(bars)` 中基于 `self.vt_symbols` 遍历和筛选。 **板块/指数成分股由引擎自动解析（强制）**：用户说"人工智能板块"/"沪深300成分股"/"银行行业"等时，引擎会自动解析出完整成分股列表到 `--symbols`。策略代码中**禁止**自行调用 `pro.ths_member()`/`pro.ths_index()`/`pro.dc_member()` 等板块 API。 **回测日期约束（强制）**：用户的回测日期意图必须由你转换为标准 `--start YYYYMMDD --end YYYYMMDD` 参数。例如用户说"最近1年"则根据当天日期计算 today-365 输出对应日期。用户未提及任何日期时，**不传** `--start/--end`（引擎默认最近1年，即 today-365 ~ today）。第1轮确认摘要中日期栏必须写"引擎默认最近1年"而非自行编造"近2年"等。**禁止**编造与用户意图不符的固定日期。 ### 模拟/实盘交易（独立流程，不走三轮协议） 用户提及 `模拟`/`实盘`/`模拟交易`/`开始交易`/`QMT` 时触发。**前提：至少有一次成功的回测（status=completed）。** **第 1 步：环境检测**（自动发现 QMT 路径，无需用户手动配置路径） ```bash "${PYTHON_BIN}" "${QUANTCLAW_ROOT}/backtests/pipeline_orchestrator.py" qmt-check ``` 输出 JSON 含 `ready`（布尔）和 `hint`（状态说明）： - `platform != Windows` → 提示用户在 Windows + QMT 环境运行 - QMT 路径自动扫描失败 → 提示确认 QMT 已安装 - QMT 终端未运行 → 提示"请先打开 QMT 并以极简模式登录" - 缺少资金账号 → 提示通过 `--account-id` 或 `QMT_ACCOUNT_ID` 提供 - `ready=true` → 进入第 2 步 **第 2 步：启动交易**（内含自动探针验证：100股跌停价挂单→确认→撤单，可选单独跑 `probe --symbol {代码} --account-id {账号}`） ```bash "${PYTHON_BIN}" "${QUANTCLAW_ROOT}/backtests/pipeline_orchestrator.py" trade \ --run-id "{最近成功回测的 run_id}" ``` 返回 JSON 分流： - `status=trading_started` → 告知用户：✅ 交易已启动，附带日志路径 - `status=platform_redirect` → 告知用户：需在 Windows + QMT 环境运行，给出命令 - `status=error` → 告知具体错误 **第 3 步：停止交易**（用户说「停止交易」时） ```bash "${PYTHON_BIN}" "${QUANTCLAW_ROOT}/backtests/pipeline_orchestrator.py" trade-stop --run-id "{run_id}" ``` 仅需用户提供 **资金账号**（`--account-id` 或 `QMT_ACCOUNT_ID`）；QMT 路径自动扫描各盘符下 `userdata_mini` 目录（可通过 `QMT_PATH` 覆盖）。 ## 环境 | 项目 | 值 | |------|---| | 项目根目录 | `QUANTCLAW_ROOT`（兼容 `QMT_PROJECT_ROOT`） | | vnpy_qmt源码 | `$QUANTCLAW_ROOT/vnpy_qmt` | | 策略输出 | `$QUANTCLAW_ROOT/strategies/` | | 回测输出 | `$QUANTCLAW_ROOT/backtests/` | | 回测数据源 | qgdata（建议预先配置 `QGDATA_TOKEN`） | | 实盘交易 | miniQMT (xt_gateway.py)，需 QMT 交易端已启动 | | Python解释器 | `PYTHON_BIN`（默认 `python3`） | 回测默认参数：`capital=1000000 / rate=0.0003 / slippage=0.01 / size=1 / pricetick=0.01` 配置指南: https://gitee.com/GuojinQuant/quant-claw#第四步配置环境变量 （仓库自带 `.env.example`） 关键变量（优先从 `.env` / 环境变量自动推导，不需要写死）： - `QUANTCLAW_ROOT`：项目根目录（兼容 `QMT_PROJECT_ROOT`） - `MONITOR_PUBLIC_BASE`：监控公网基址（可留空；OpenClaw 环境下可由 `OPENCLAW_CONTROL_URL` 自动推导） - `ORCH_MONITOR_PORT_CANDIDATES`：白名单端口（默认 `8767`，必须在防火墙放通） - `QGDATA_TOKEN`：数据 Token（可选；未配置时自动使用内置共享试用Token，有每日额度限制） **Token 自动提取规则（强制）**：若用户对话中出现 60~70 位连续字母数字串（如"我的token是 Mj9mN2xP..."），自动提取并通过 `--token` 参数传给 submit 命令。日志和回复中只显示前 6 位+`***`，**绝不回显完整 Token**。提取到个人 Token 后提示用户：`已使用您的个人Token，不消耗共享试用额度`。 --- ## 核心原则（强制） - 回测请求走三轮交互协议（见下方工作流）。 - 编排器路径：`"${QUANTCLAW_ROOT}/backtests/pipeline_orchestrator.py"`。 - 严禁读取或引用历史报告文件（`.html`/`.png`/`.json`），只使用 orchestrator 的 `status` 命令输出获取结果。 - 策略代码由 agent 在第 2 轮使用 LLM 生成，写入 `${QUANTCLAW_ROOT}/strategies/` 目录。 - 禁止在首条回复前做长轮询。 - 监控页是透明主通道，聊天页只给里程碑与结论。 - 失败时必须回复 `status` + `error` + `next_action`，禁止只说"失败了"不给下一步。 - 实盘能力保留，默认不进入实盘；用户请求模拟/实盘时执行 `qmt-check` 检测。 - 触发词分四类： - **回测第1轮**：`回测`、`策略`、`自动编排`、`均线`、`上穿`、`下穿`、`买入`、`卖出` → 进入三轮交互协议 - **回测第2轮**：`开始生成`、`生成策略`、`好`、`开始`、`继续`、`1`（或任何第1轮确认后的用户消息）；若同条消息同时包含完整需求+第2轮触发词，直接视为已确认并进入第2轮 - **参数优化**：`优化`、`调参`、`参数优化`、`网格搜索` → 执行 `optimize`（回测完成后触发） - **模拟/实盘**：`模拟`、`模拟盘`、`模拟交易`、`开始交易`、`实盘`、`实盘交易`、`QMT` → 执行 `qmt-check` + `trade`（独立流程，不走三轮协议） --- ## 第 0 步：环境预检（每次会话首次触发时执行一次） 运行 Skill 内置预检脚本（`scripts/preflight.py`，相对于本 Skill 目录）。脚本内部使用 `sys.executable` 自适应解释器，直接调用即可： Linux/macOS： ```bash python3 "<本Skill目录>/scripts/preflight.py" ``` Windows PowerShell： ```powershell python "<本Skill目录>\scripts\preflight.py" ``` 输出 JSON，按字段消费： - `ready=true` → 取 `engine_root` 作为 `QUANTCLAW_ROOT`，进入第 1 轮 - `ready=false` + `engine_found=true` → 依赖缺失，向用户展示 `blockers` 和 `fix_cmd` - `ready=false` + `engine_found=false` → 引擎未找到，返回 `status=config_missing` + 配置指南链接。**禁止降级为手动脚本。** - `hints` 非空 → 非阻塞提示（如 Token 状态），向用户如实展示 - 预检通过后可选执行 `doctor_cmd` 做深度配置诊断（端口/Token/公网等） 退出码：`0`=就绪 `1`=引擎未找到 `2`=依赖缺失可修复 编排器脚本内置路径回退（`Path(__file__).parents[1]`），即使环境变量未设置，只要找到脚本就能正常运行。 --- ## 三轮交互协议 ### 第 1 轮：需求确认 **目标**：理解用户意图，确认关键参数，引导进入代码生成轮。 1. **解析需求**：提取标的、周期、信号（仓位/风控缺失时使用默认并说明） 2. **做数据能力检查**： ```bash "${PYTHON_BIN}" "${QUANTCLAW_ROOT}/backtests/data_capability_guard.py" \ --requirement "{用户原始需求}" ``` 3. **确认并引导**：回复至少包含： - 已理解的参数摘要（标的/周期/信号/方向） - 若 `data_capability_guard` 输出含 `token_hint`（非空字符串），**必须**在确认摘要之后、引导词之前如实告知用户。这表示检测到 Portfolio 策略 +（未传 Token 将回退共享试用 Token / 正在使用共享试用 Token）的组合，数据调用量大可能触发频率限制。直接呈现 `token_hint` 内容即可，不要包装为广告 - 明确引导用户触发下一轮： ``` 需求已确认：{标的} / {模式cta或portfolio} / {日线/分钟线} / {做多/做空} / {回测区间或"引擎默认最近1年"} {若有token_hint则在此呈现，无则省略此行} 请回复「开始生成」，我来为你生成策略代码并提交回测。 ``` **第 1 轮禁止**：不做代码生成、不调用 submit、不创建文件。 **第 1 轮最多命令**：`data_capability_guard.py`（1条）。 ### 第 2 轮：代码生成 + 提交（极速流程） **触发**：第 1 轮确认后，用户发送任意消息（`开始生成`/`好`/`1` 等）；若同条消息同时包含完整需求+第2轮触发词，也可直通第2轮。 **直通首响（强制）**：直通第2轮时先立即回复一句 `已收到，开始生成中...`，再执行代码生成与提交，避免长时间无反馈。 **第 2 轮速度约束（强制）**： - **禁止**重跑 `data_capability_guard`（第 1 轮已检查） - **禁止**单独跑 `py_compile`（submit 内部已含编译+静态检查+冒烟测试） - 理想路径 **2 次工具调用**：① 写策略文件 ② submit - submit **同步等待预检**（compile→lint→dryrun，通常 10~60 秒）再返回结果： - 预检通过 → 返回 `status: "accepted"` + `monitor_url`（回测已在后台运行） - 预检失败 → 返回 `status: "lint_error"/"compile_error"/"dryrun_error"` + `error` + `strategy_file`（**不返回 monitor_url**） - 预检失败时 **在 Round 2 内立即修复**： 1. **先告知用户**当前情况（如"检测到 import 路径错误，正在自动修复..."），保持透明 2. 读 `error` + `strategy_file` → 修复代码 → 重新 submit 3. 最多 **6 轮**修复重试，超过交由用户决策 - submit 返回 `accepted` 后若回测运行时/超时/数据失败 → 已有 `monitor_url`，在第 3 轮处理 1. **生成策略代码**（agent 使用 LLM 能力，直接写入文件）： - 根据 `parsed["mode"]` 选择正确模板： - `cta` → 继承 `CtaTemplate`，`on_bar(self, bar)`，`self.buy(price, vol)` / `self.sell(price, vol)`，`self.pos`，初始化用 `self.load_bar(N)`（**单数**，N=bar 数量） - `portfolio` → 继承 `StrategyTemplate`（vnpy_portfoliostrategy），`on_bars(self, bars: dict)`，`self.buy(vt_symbol, price, vol)` / `self.sell(vt_symbol, price, vol)`，`self.get_pos(vt_symbol)`，初始化用 `self.load_bars(days)`（**复数**，days=天数） - **严禁混用**：CTA 策略禁止用 `load_bars`，Portfolio 策略禁止用 `load_bar` - **仓位计算（强制——用户未指定时默认全仓）**： - 用户明确说了手数/股数/仓位比例 → 按用户要求 - 用户未提仓位 → **默认全仓**（用 `self.available_cash` 动态计算最大可买手数） - **绝对禁止** `fixed_size = 100` 或任何硬编码固定股数作为默认仓位（这是 vnpy 教程示例值，不是真实交易逻辑） - CTA 全仓计算参考： ```python vol = int(self.available_cash / (bar.close_price * 1.0003)) // 100 * 100 # 主板100股整数倍 if vol >= 100: self.buy(bar.close_price, vol) ``` - Portfolio 等权全仓参考： ```python per_capital = self.available_cash / max(len(target_symbols), 1) vol = int(per_capital / (bar.close_price * 1.0003)) // 100 * 100 if vol >= 100: self.buy(vt_symbol, bar.close_price, vol) ``` - 引擎会自动管理资金扣减/回款，并在资金不足时自动调减到可买最大手数 - **账户属性（引擎自动注入，策略直接用）**： - `self.available_cash` — 可用现金（买入扣减，卖出回款） - `self.total_value` — 账户总值（现金+持仓市值） - `self.closable_pos` — CTA可卖数量（T+1 自动扣减当日买入） - `self.closable_positions` — Portfolio per-symbol可卖量dict（`self.closable_positions.get(vt_symbol, 0)`） - `self.capital` — 等于 available_cash（兼容） - `self.trade_calendar` — 交易日历（set of "YYYYMMDD"），可用 `date_str in self.trade_calendar` 判断交易日 - `self.last_order_status` — 最近一次下单结果（`{"ok":True/False,"reason":"...","symbol":"..."}`） - `self.order_reject_log` — 最近200条被拒订单记录（停牌/涨跌停/资金不足等） - **资金不足兜底**：引擎会自动调减买入量到可用现金能买的最大手数，无需策略手动检查 - **停牌/涨跌停兜底**：引擎自动拦截停牌日下单和触及涨跌停价格的委托，返回空列表。**策略判断下单是否成功应优先检查 `buy()/sell()` 返回值**（空列表=被拒），而非假设一定成功 - **交易所合规（强制）**：沪深主板/创业板 100 股整数倍，科创板(688xxx) 200 股起步+1 股递增（205 股合法） - **ArrayManager API**：均线用 `am.sma()`，禁止用 `am.ma()`（vnpy 不存在此方法） - **CTA 必须在 `on_bar` 开头调用 `am.update_bar(bar)`**：否则 ArrayManager 永远不会 `inited`，导致全程 0 交易。引擎有运行时兜底但不能依赖 - 写入 `${QUANTCLAW_ROOT}/strategies/{module_name}.py` **Portfolio 轮动策略速查**（减少生成思考时间）： - 周轮动：`on_bars(self, bars)` 中 `list(bars.values())[0].datetime.weekday()==0` 判断周一调仓 - 排序选股：遍历 `self.vt_symbols` 计算因子 → `sorted()` → 取前N名 - 等权全仓：每只 `self.available_cash / N`，按交易所规则取整手 - 周线数据：`pro.weekly(ts_code=code, start_date=..., end_date=...)` 直取，无需从日线合成 2. **直接提交**（submit 内置编译+静态检查+预导入，无需单独 py_compile）： ```bash "${PYTHON_BIN}" "${QUANTCLAW_ROOT}/backtests/pipeline_orchestrator.py" submit \ --requirement "{用户原始需求}" \ --strategy-file "${QUANTCLAW_ROOT}/strategies/{module_name}.py" \ --strategy-module "{module_name}" \ --strategy-class "{class_name}" \ --symbols "{策略中所有vt_symbol逗号分隔}" \ --monitor-public-base "${MONITOR_PUBLIC_BASE:-}" \ --monitor-port-candidates "${ORCH_MONITOR_PORT_CANDIDATES:-8767}" \ --timeout-sec 1200 ``` - submit 内部同步执行预检：py_compile → _lint_strategy → _preflight_import → dryrun - 预检通过后自动启动回测，返回 `status: "accepted"` + `monitor_url` - 预检失败返回 `status: "<error_type>"` + `error` + `strategy_file`，**不启动回测、不返回 monitor_url** - agent 判断 submit 输出： - `status == "accepted"` → 进入步骤 3（回复用户） - `status` 为 `compile_error`/`lint_error`/`dryrun_error` → **先输出一句话告知用户**（如"检测到 xxx 错误，正在修复第 N/6 次..."）→ 读 error + strategy_file → 修复代码 → 重新 submit（最多 6 轮） - 6 轮后仍失败 → 将最后一次错误信息告知用户，交由用户决策 3. **回复用户**（仅在 submit 返回 `accepted` 后）： - `run_id` + `monitor_url` + 当前状态 - 引导词（必须覆盖两个语义点）： - A：打开监控页实时查看策略代码/曲线/交易 - B：完成后发送「查看结果」获取摘要与报告链接 ### 第 3 轮：查看结果 / 诊断修复 **触发词**：`查看结果`、`结果`、`status`、`重新生成` 1. **查询状态**： ```bash "${PYTHON_BIN}" "${QUANTCLAW_ROOT}/backtests/pipeline_orchestrator.py" status --run-id "{run_id}" ``` 2. **根据状态分流**： | status | 处理 | |--------|------| | `running` | 告知当前进度，提示继续等待 | | `completed` | 输出摘要 + 强制输出 `report_url`，不用 `monitor_url` 表述完整报告 | | `failed` | 根据 `last_error.error_type` 分流处理（见下方错误分流表） | 3. **错误分流（agent 决策表）**： | error_type | 含义 | agent 策略 | |-----------|------|-----------:| | `compile_error` | py_compile 失败 | 读 strategy_file + traceback → LLM 修复代码 → 重新提交 | | `lint_error` | 静态检查 blocker（如 am.ma()、vnpy.trading 导入） | 同 compile_error 处理 | | `dryrun_error` | 冒烟测试失败（50根K线采样回放运行时异常） | 同 compile_error 处理：读 strategy_file + traceback → LLM 修复 → 重新提交 | | `runtime_error` | 回测运行时异常 | 读 strategy_file + traceback → LLM 分析修复 → 重新提交 | | `compat_error` | 引擎兼容性（如 portfolio+WEEKLY 未降级） | 提示用户调整参数，通常不应出现（parse_requirement 已自动降级） | | `data_error` | 数据加载失败/为空 | 提示用户检查标的代码/日期范围/token | | `config_error` | 环境/配置问题 | 提示用户检查配置 | | `timeout_error` | 超时 | 建议缩短日期范围或标的数量 | - 只对 `compile_error`、`lint_error`、`dryrun_error` 和 `runtime_error` 尝试自动修复，其余直接报告用户。 - Round 2 预检失败（compile/lint/dryrun）自动修复最多 **6 轮**；Round 3 运行时修复最多 **3 轮**。超过交由用户决策。 3.5 **结果校验告警（status=completed 时优先检查）**： `status` 输出中若包含 `result_warnings` 字段（非空列表），说明回测结果校验发现语义异常： - 读 `result_warnings` + `strategy_file` → LLM 分析是否为策略 bug - 若判断为 bug → 修复策略代码 → 重新提交 - 若判断为正常行为（如趋势策略在熊市期间无反向信号）→ 照常输出结果，附带告警说明 常见告警类型：零交易、单边信号（有买无卖/有卖无买）、胜率极端值（100%/0%）。 4. **完成后引导**（status=completed 时必须附带）： ``` 回测已完成，{摘要}。您可以： 1. 回复「优化参数」对策略参数进行网格搜索 2. 回复「模拟交易」在 QMT 中启动自动交易（需 Windows + QMT 环境） ``` --- ## 参数优化流程（回测完成后由用户触发） **触发词**：`优化`、`调参`、`参数优化`、`网格搜索` **前提**：已完成至少一次回测（数据已缓存在数据库中）。 1. **确认优化方案**：agent 分析当前策略的可调参数，向用户确认： - 优化目标（默认 `sharpe_ratio`，可选 `total_return`/`annual_return`/`max_ddpercent`） - 参数范围（`[起始, 终止, 步长]`） - 预估组合数 2. **执行优化**： ```bash "${PYTHON_BIN}" "${QUANTCLAW_ROOT}/backtests/pipeline_orchestrator.py" optimize \ --strategy-file "${QUANTCLAW_ROOT}/strategies/{module_name}.py" \ --strategy-class "{class_name}" \ --symbols "{vt_symbol}" \ --start "{YYYYMMDD}" --end "{YYYYMMDD}" \ --optimize-params '{"target":"sharpe_ratio","params":{"fast_window":[5,30,5],"slow_window":[10,60,10]}}' ``` 支持 `"algorithm":"ga"` 使用遗传算法（大参数空间时推荐）。 3. **展示结果**：输出 JSON 含 `best`（最优参数+指标）和 `results`（Top N），agent 以表格形式呈现。 4. **后续选择**：用户可选择用最优参数重新回测验证，或继续调整参数范围。 --- ## 绝对禁止清单（违反任何一条 = 严重事故） ### 链路禁止 - **禁止**在项目目录或工作目录下创建 `.html` 报告文件 - **禁止**读取、搜索、引用 `reports/` 目录下的历史文件 - **禁止**直接调用 `backtest_runner.py`、`monitor_server.py`、`/api/code` - **禁止**使用 `python -m http.server` 或任何临时 HTTP 服务 - **禁止**生成临时回测脚本或手工拼接回测执行流程 - **禁止**在 submit 之前检查"策略是否已存在" - **禁止**在编排器找不到时降级为手动脚本、akshare/yfinance 临时方案、或任何非编排器回测方式 ### 输出禁止 - **禁止**寒暄/自我介绍/营销文案 - **禁止**承诺"完成后自动推送摘要"或"跨轮次自动再回复" - **禁止**使用"模拟数据演示"口径替代真实回测 - **禁止**硬编码市场数据 - **禁止**繁琐状态输出 `[run_id][N/M][状态]` - **禁止**发了"请确认"却自动继续 - **禁止**前台启动长驻进程 ### 安全禁止 - **禁止**硬编码绝对路径（统一使用 `QUANTCLAW_ROOT`） - **禁止**在 SKILL 或脚本中提交明文凭据 - **禁止**策略文件路径指向 `strategies/` 目录之外


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)