docs: align strategy ai skill constraints
This commit is contained in:
@@ -98,12 +98,16 @@ pub fn built_in_strategy_manual() -> StrategyAiManual {
|
||||
"支持 let 变量、fn 自定义函数、when/unless/else 条件块、可用指标/因子字段映射。".to_string(),
|
||||
"支持数值型和字符串型因子,字符串字段可用于行业、概念、标签、板块等分类过滤。".to_string(),
|
||||
"当前默认回测数据已支持 OHLCV、市值、流通市值、换手率、有效换手率、上市天数、停牌/ST/板块、涨跌停价格、tick 触达涨跌停、常用价格/成交量均线;复杂技术指标和财务报表字段必须来自预计算因子或后续扩展函数。".to_string(),
|
||||
"AI 生成策略时只能输出完整 engine-script 代码,不输出 Markdown、解释、推理过程、JSON 包装或手册复述。".to_string(),
|
||||
"表达式字段以运行时字段为准:市值使用 market_cap,流通市值使用 free_float_cap;不要在策略表达式中使用数据库原始字段 float_market_cap。".to_string(),
|
||||
"60日价格均线使用 rolling_mean(\"close\", 60),不要使用 ma60、stock_ma60、signal_ma60 或 benchmark_ma60。".to_string(),
|
||||
"自定义 fn 必须通过参数传入运行时字段;不要用 fn score() 这类零参数函数直接引用 market_cap、close、ma5 等股票字段。".to_string(),
|
||||
"禁止自由 Python/JavaScript 命令式语句,最终必须输出平台 DSL。".to_string(),
|
||||
],
|
||||
ai_workflows: vec![
|
||||
ManualSection {
|
||||
title: "从用户提示词生成策略".to_string(),
|
||||
detail: "先抽取市场、基准、信号指数、调仓频率、持仓数量、选股条件、排序、仓位、止盈止损和排除条件;再生成完整 engine-script。代码必须包含 strategy(\"...\")、market、benchmark、signal、rebalance、selection、filter、ordering;动态参数必须写成 let 或 fn,不能硬编码成不可修改常量。".to_string(),
|
||||
detail: "先抽取市场、基准、信号指数、调仓频率、持仓数量、选股条件、排序、仓位、止盈止损和排除条件;再生成完整 engine-script。代码必须包含 strategy(\"...\")、market、benchmark、signal、rebalance、selection、filter、ordering;动态参数必须写成 let 或 fn,不能硬编码成不可修改常量。回复只能是代码,禁止 Markdown、解释、JSON 包装和未支持伪 DSL。".to_string(),
|
||||
},
|
||||
ManualSection {
|
||||
title: "生成后立即可回测".to_string(),
|
||||
@@ -418,6 +422,16 @@ pub fn render_manual_markdown(manual: &StrategyAiManual) -> String {
|
||||
render_manual_sections(&mut out, "回测 API", &manual.backtest_api);
|
||||
render_manual_sections(&mut out, "结果呈现", &manual.result_presentation);
|
||||
render_manual_sections(&mut out, "策略优化闭环", &manual.optimization_playbook);
|
||||
out.push_str("## AI 代码生成硬约束\n");
|
||||
out.push_str("- 只输出完整 `engine-script` 代码;第一行必须是 `strategy(\"...\")`、`let`、`fn`、`const` 或 `//`。\n");
|
||||
out.push_str("- 禁止输出 Markdown、解释、推理过程、JSON 包装、手册复述或结果报告。\n");
|
||||
out.push_str("- 只使用支持语句块:`market`、`benchmark`、`signal`、`rebalance.every_days(...).at([...])`、`selection.limit`、`selection.market_cap_band`、`filter.stock_ma`、`filter.stock_expr`、`ordering.rank_by`、`ordering.rank_expr`、`allocation.buy_scale`、`risk.stop_loss`、`risk.take_profit`、`risk.index_exposure`、`execution.matching_type`、`execution.slippage`、`universe.exclude`。\n");
|
||||
out.push_str("- 禁止伪 DSL:`filter(...)`、`rank(...)`、`select.top(...)`、`weight.equal(...)`、`sell_rule(...)`、`backtest(...)`、`risk.max_position(...)`。\n");
|
||||
out.push_str("- 市值表达式字段只能用 `market_cap` 或 `free_float_cap`;不要使用数据库原始字段 `float_market_cap`。\n");
|
||||
out.push_str("- 60日价格均线使用 `rolling_mean(\"close\", 60)`;不要使用 `ma60`、`stock_ma60`、`signal_ma60` 或 `benchmark_ma60`。\n");
|
||||
out.push_str("- 自定义 `fn` 必须通过参数传入运行时字段;不要用 `fn score()` 这类零参数函数直接引用 `market_cap`、`close`、`ma5` 等股票字段。\n");
|
||||
out.push_str("- `selection.market_cap_band` 必须写命名参数:`field=\"market_cap\"` 或 `field=\"free_float_cap\"`,并包含 `lower=...` 与 `upper=...`。\n");
|
||||
out.push_str("- `risk.index_exposure(...)` 只能传一个表达式;`execution.matching_type(...)` 和 `execution.slippage(...)` 必须使用手册列出的合法取值。\n\n");
|
||||
out.push_str("## 语句块\n");
|
||||
for item in &manual.statement_blocks {
|
||||
out.push_str(&format!("- `{}`: {}\n", item.title, item.detail));
|
||||
@@ -492,8 +506,8 @@ pub fn build_generation_prompt(
|
||||
prompt.push_str("- 生成的代码必须能转换为 strategy_spec 并提交 POST /v1/backtests。\n");
|
||||
prompt.push_str("- 不要使用手册未列出的字段、函数或外部平台 API 名称。\n\n");
|
||||
prompt.push_str("只允许使用这些可编译语句:market、benchmark、signal、rebalance.every_days(...).at([...])、selection.limit、selection.market_cap_band、filter.stock_ma、filter.stock_expr、ordering.rank_by、ordering.rank_expr、allocation.buy_scale、risk.stop_loss、risk.take_profit、risk.index_exposure、execution.matching_type、execution.slippage、universe.exclude。禁止输出 filter(...)、rank(...)、select.top(...)、weight.equal()、sell_rule(...)、backtest(...)、risk.max_position(...) 这类未支持伪语法。\n");
|
||||
prompt.push_str("参数形态必须严格:selection.market_cap_band 必须写 field=\"float_market_cap\", lower=..., upper=...;risk.index_exposure 只能传一个数值表达式,例如 ((signal_close < signal_ma20) ? 0.35 : 1.0);execution.matching_type 只能取 next_tick_last、next_tick_best_own、next_tick_best_counterparty、counterparty_offer、vwap、current_bar_close、next_bar_open、open_auction;execution.slippage 必须写 execution.slippage(\"none\") 或 execution.slippage(\"price_ratio\", 0.001)。\n");
|
||||
prompt.push_str("可参考但不要照抄的最小模板:\n```txt\nstrategy(\"cn_a_smallcap_factor_rotation\") {\nmarket(\"CN_A\")\nbenchmark(\"000852.SH\")\nsignal(\"000001.SH\")\nrebalance.every_days(5).at([\"10:18\"])\nselection.limit(28)\nselection.market_cap_band(field=\"float_market_cap\", lower=1500000000, upper=25000000000)\nfilter.stock_expr(listed_days >= 120 && is_st == 0 && paused == 0 && close > 3 && close < 60 && ma5 > ma20 && rolling_mean(\"volume\", 5) < rolling_mean(\"volume\", 60) && at_upper_limit == false && at_lower_limit == false)\nordering.rank_expr(safe_div(1.0, max(float_market_cap, 1.0), 0.0) * 0.55 + effective_turnover_ratio * 0.25 + safe_div(close, max(ma20, 0.01), 0.0) * 0.20, \"desc\")\nallocation.buy_scale(1.0)\nrisk.index_exposure((signal_close < signal_ma20) ? 0.35 : 1.0)\nrisk.stop_loss(holding_return < -0.08)\nrisk.take_profit(holding_return > 0.16)\nexecution.slippage(\"price_ratio\", 0.001)\n}\n```\n\n");
|
||||
prompt.push_str("参数形态必须严格:selection.market_cap_band 必须写 field=\"market_cap\" 或 field=\"free_float_cap\", lower=..., upper=...;禁止使用 float_market_cap;禁止使用 ma60、stock_ma60、signal_ma60、benchmark_ma60,60日价格均线写 rolling_mean(\"close\", 60);不要生成 fn score() 这类零参数函数,股票字段排序直接写在 ordering.rank_expr 内或用带参数函数;risk.index_exposure 只能传一个数值表达式,例如 ((signal_close < signal_ma20) ? 0.35 : 1.0);execution.matching_type 只能取 next_tick_last、next_tick_best_own、next_tick_best_counterparty、counterparty_offer、vwap、current_bar_close、next_bar_open、open_auction;execution.slippage 必须写 execution.slippage(\"none\") 或 execution.slippage(\"price_ratio\", 0.001)。\n");
|
||||
prompt.push_str("可参考但不要照抄的最小模板:\n```txt\nstrategy(\"cn_a_smallcap_factor_rotation\") {\nmarket(\"CN_A\")\nbenchmark(\"000852.SH\")\nsignal(\"000001.SH\")\nrebalance.every_days(5).at([\"10:18\"])\nselection.limit(28)\nselection.market_cap_band(field=\"market_cap\", lower=12, upper=180)\nfilter.stock_expr(listed_days >= 120 && is_st == 0 && paused == 0 && close > 3 && close < 60 && ma5 > ma20 && rolling_mean(\"volume\", 5) < rolling_mean(\"volume\", 60) && at_upper_limit == false && at_lower_limit == false)\nordering.rank_expr(safe_div(1.0, max(market_cap, 1.0), 0.0) * 0.55 + effective_turnover_ratio * 0.25 + safe_div(close, max(ma20, 0.01), 0.0) * 0.20, \"desc\")\nallocation.buy_scale(1.0)\nrisk.index_exposure((signal_close < signal_ma20) ? 0.35 : 1.0)\nrisk.stop_loss(holding_return < -0.08)\nrisk.take_profit(holding_return > 0.16)\nexecution.slippage(\"price_ratio\", 0.001)\n}\n```\n\n");
|
||||
prompt.push_str("用户目标:\n");
|
||||
prompt.push_str(&format!("- {}\n", request.user_goal));
|
||||
if !request.constraints.is_empty() {
|
||||
|
||||
Reference in New Issue
Block a user