Files
fidc-backtest-engine/crates/fidc-core/src/strategy_ai.rs
2026-04-23 19:29:12 -07:00

375 lines
27 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StrategyAiManual {
pub title: String,
pub language: String,
pub overview: Vec<String>,
pub statement_blocks: Vec<ManualSection>,
pub runtime_field_groups: Vec<ManualFieldGroup>,
pub functions: Vec<ManualFunction>,
pub factor_sources: Vec<ManualFactorSource>,
pub examples: Vec<ManualExample>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ManualSection {
pub title: String,
pub detail: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ManualFieldGroup {
pub title: String,
pub detail: String,
pub fields: Vec<ManualField>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ManualField {
pub name: String,
pub field_type: String,
pub detail: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ManualFunction {
pub name: String,
pub signature: String,
pub detail: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ManualFactorSource {
pub table: String,
pub detail: String,
pub fields: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ManualExample {
pub title: String,
pub code: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct StrategyAiCatalog {
pub daily_fields: Vec<String>,
pub benchmark_fields: Vec<String>,
pub indicator_factors: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StrategyAiGenerateRequest {
pub user_goal: String,
pub constraints: Vec<String>,
pub market: String,
pub benchmark_symbol: String,
pub signal_symbol: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StrategyAiOptimizeRequest {
pub current_code: String,
pub objective: String,
pub result_summary: serde_json::Value,
pub diagnostics: Vec<String>,
}
pub fn built_in_strategy_manual() -> StrategyAiManual {
StrategyAiManual {
title: "OmniQuant 平台策略脚本手册".to_string(),
language: "engine-script".to_string(),
overview: vec![
"平台策略脚本采用声明式 DSL + 表达式执行模型。".to_string(),
"支持 let 变量、fn 自定义函数、when/unless/else 条件块、数据库字段因子映射。".to_string(),
"禁止自由 Python/JavaScript 命令式语句,最终必须输出平台 DSL。".to_string(),
],
statement_blocks: vec![
ManualSection {
title: "strategy(\"name\") { ... }".to_string(),
detail: "策略入口。内部用于声明 market/benchmark/signal、选股、调仓、风控、排序、资金分配。name 是策略标识,会进入历史档案和回测记录,建议始终填写,但它本身不改变交易逻辑。".to_string(),
},
ManualSection {
title: "market / benchmark / signal".to_string(),
detail: "market(\"CN_A\") 指定市场benchmark 和 signal 使用标准证券代码,例如 000852.SH。".to_string(),
},
ManualSection {
title: "rebalance.every_days(n).at([..])".to_string(),
detail: "设置调仓周期和盘中决策/执行时刻。".to_string(),
},
ManualSection {
title: "rebalance.weekly / rebalance.monthly".to_string(),
detail: "支持按交易周或交易月调仓,例如 rebalance.weekly(weekday=5).at([\"10:18\"])、rebalance.weekly(tradingday=-1).at([\"10:18\"])、rebalance.monthly(tradingday=1).at([\"10:18\"])。`.at([...])` 的最后一个时刻会编进分钟级 schedule/time_rule当前平台把 on_day 近似到 10:18把 open_auction 近似到 09:31。".to_string(),
},
ManualSection {
title: "bar / tick 生命周期".to_string(),
detail: "回测内核支持 rqalpha 风格的 bar/tick 生命周期:日内会发布 pre_bar/bar/post_bar 过程事件;存在 tick 订阅或 tick 调度规则时,会按 execution_quotes 的时间顺序发布 pre_tick/tick/post_tick并把 tick 阶段下单限制在当前 tick 时间窗内撮合。平台 DSL 中可通过 subscribe([...])、trading.subscription_guard(true) 和 process_event 字段配合显式订单模拟 tick 订阅策略。".to_string(),
},
ManualSection {
title: "selection.market_cap_band / selection.limit / ordering.rank_by / ordering.rank_expr".to_string(),
detail: "控制候选范围、数量和排序。支持表达式驱动的动态市值带和排序表达式。".to_string(),
},
ManualSection {
title: "filter.stock_expr / risk.stop_loss / risk.take_profit / allocation.buy_scale".to_string(),
detail: "表达式型规则支持多条组合。stop_loss/take_profit 多条按 OR 组合filter.stock_expr 多条按 AND 组合。".to_string(),
},
ManualSection {
title: "corporate_actions.dividend_reinvestment".to_string(),
detail: "支持 corporate_actions.dividend_reinvestment(true)。开启后,现金分红到账会优先按 round lot 回补成同一只股票,零头保留为现金。".to_string(),
},
ManualSection {
title: "execution.matching_type / execution.slippage".to_string(),
detail: "设置撮合模式和滑点。支持 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\")。其中 next_tick_last 使用 tick 的 last_pricenext_tick_best_own / next_tick_best_counterparty 会按 L1 买一卖一近似 rqalpha 的 tick 最优价语义counterparty_offer 当前也按 L1 对手方报价近似实现vwap 会在盘中执行价链路上聚合多笔成交为单条 VWAP 成交open_auction 使用当日集合竞价开盘价 day_open 进行撮合,且不额外施加滑点,并按竞价成交量而不是盘口一档流动性限制成交;滑点支持 execution.slippage(\"none\") / execution.slippage(\"price_ratio\", 0.001) / execution.slippage(\"tick_size\", 1) / execution.slippage(\"limit_price\"),其中 limit_price 会在限价单成交时按挂单价模拟 rqalpha 的最坏成交价。".to_string(),
},
ManualSection {
title: "trading.rotation / order.* / cancel.* / update_universe / subscribe".to_string(),
detail: "支持显式下单、撤单、AlgoOrder 和动态 universe 管理。可以用 trading.rotation(false) 关闭默认轮动链路,再用 trading.stage(\"open_auction\" | \"on_day\") 指定执行阶段;需要模拟 rqalpha 的 tick 订阅保护时,可写 trading.subscription_guard(true),未订阅 symbol 的显式订单会被拦截TargetPortfolioSmart + AlgoOrder 会过滤未订阅标的。用 trading.schedule.daily().at([\"10:18\"]) / trading.schedule.weekly(weekday=5).at([\"10:18\"]) / trading.schedule.weekly(tradingday=-1).at([\"10:18\"]) / trading.schedule.monthly(tradingday=1).at([\"10:18\"]) 指定触发频率和分钟级 time_rule然后写 order.shares(\"600000.SH\", 1000)、order.target_shares(\"600000.SH\", 2000)、order.value(\"600000.SH\", cash * 0.25)、order.target_percent(\"600000.SH\", 0.05)、order.limit_value(\"600000.SH\", cash * 0.25, open * 0.99)、order.vwap_value(\"600000.SH\", cash * 0.25, \"09:31\", \"09:40\")、order.twap_percent(\"600000.SH\", 0.05, \"10:00\", \"10:30\")、order.target_portfolio_smart(weights={\"600000.SH\": 0.3, \"000001.SZ\": 0.2}, order_prices=VWAPOrder(930, 940), valuation_prices={\"600000.SH\": prev_close})、order.target_portfolio_smart(weights={\"600000.SH\": 0.3, \"000001.SZ\": 0.2}, order_prices={\"600000.SH\": open * 0.99}, valuation_prices={\"600000.SH\": prev_close})、cancel.order(12345)、cancel.symbol(\"600000.SH\")、cancel.all()、update_universe([\"600000.SH\", \"000001.SZ\"])、subscribe([\"000001.SZ\"])、unsubscribe([\"000001.SZ\"])。其中 order.target_shares(...) 对应 rqalpha 的 order_toorder.target_portfolio_smart(...) 对应 rqalpha 的 order_target_portfolio_smart 批量目标权重语义order_prices 既可以是逐标的限价映射,也可以是 VWAPOrder/TWAPOrder 这类全局 AlgoOrderorder.vwap_* / order.twap_* 对应 rqalpha 的 AlgoOrder 时间窗订单风格,而 update_universe/subscribe/unsubscribe 对应 rqalpha 的动态 universe 与订阅接口。symbol 使用标准证券代码数量、金额、仓位、时间窗、限价、order_id 和 symbol 列表都支持表达式;这些语句也支持放进 when/unless 条件块。".to_string(),
},
ManualSection {
title: "when / unless / else".to_string(),
detail: "条件块支持按日期、指数、仓位等动态切换规则。".to_string(),
},
],
runtime_field_groups: vec![
ManualFieldGroup {
title: "日级字段".to_string(),
detail: "可直接在表达式中使用的日级与账户字段。".to_string(),
fields: vec![
ManualField { name: "signal_open/signal_close".to_string(), field_type: "float".to_string(), detail: "信号指数当日开盘价与前一日收盘价。".to_string() },
ManualField { name: "benchmark_open/benchmark_close".to_string(), field_type: "float".to_string(), detail: "基准当日开盘价与前一日收盘价。".to_string() },
ManualField { name: "signal_ma5/signal_ma10/signal_ma20/signal_ma30".to_string(), field_type: "float".to_string(), detail: "信号指数滚动均线。".to_string() },
ManualField { name: "benchmark_ma5/benchmark_ma10/benchmark_ma20/benchmark_ma30".to_string(), field_type: "float".to_string(), detail: "基准指数滚动均线。".to_string() },
ManualField { name: "cash/available_cash/market_value/total_equity".to_string(), field_type: "float".to_string(), detail: "账户资金与总资产。".to_string() },
ManualField { name: "position_count/max_positions/refresh_rate".to_string(), field_type: "int".to_string(), detail: "仓位计数与调仓周期。".to_string() },
ManualField { name: "has_open_orders/open_order_count/open_buy_order_count/open_sell_order_count".to_string(), field_type: "bool/int".to_string(), detail: "当前阶段挂单簿摘要。".to_string() },
ManualField { name: "open_buy_qty/open_sell_qty/latest_open_order_id".to_string(), field_type: "int".to_string(), detail: "当前阶段未成交买卖挂单的剩余数量汇总,以及最近一笔挂单 id。".to_string() },
ManualField { name: "has_dynamic_universe/dynamic_universe_count".to_string(), field_type: "bool/int".to_string(), detail: "当前策略上下文是否存在动态 universe以及动态 universe 内证券数量。".to_string() },
ManualField { name: "has_subscriptions/subscription_count".to_string(), field_type: "bool/int".to_string(), detail: "当前订阅集合是否为空,以及订阅证券数量。".to_string() },
ManualField { name: "subscription_guard_required".to_string(), field_type: "bool".to_string(), detail: "当前显式交易是否启用订阅保护;启用后未订阅标的的显式订单会被拒绝生成。".to_string() },
ManualField { name: "has_process_events/process_event_count/process_event_counts".to_string(), field_type: "bool/int/map".to_string(), detail: "当前阶段可见的过程事件摘要process_event_counts[\"trade\"] 这类写法可直接读取当天事件计数。".to_string() },
ManualField { name: "current_process_kind/current_process_order_id/current_process_symbol/current_process_side/current_process_detail".to_string(), field_type: "string/int".to_string(), detail: "当前正在回调的过程事件上下文;没有活动事件时为空字符串或 0。".to_string() },
ManualField { name: "latest_process_kind/latest_process_order_id/latest_process_symbol/latest_process_side/latest_process_detail".to_string(), field_type: "string/int".to_string(), detail: "当前阶段最近一条过程事件的摘要,可用于让 on_day/open_auction 逻辑响应 earlier lifecycle 或订单事件。".to_string() },
ManualField { name: "year/month/quarter/day_of_month/day_of_year/week_of_year/weekday".to_string(), field_type: "int".to_string(), detail: "日期维度字段。".to_string() },
ManualField { name: "is_month_start/is_month_end".to_string(), field_type: "bool".to_string(), detail: "月初/月末标记。".to_string() },
],
},
ManualFieldGroup {
title: "个股字段".to_string(),
detail: "每只候选股票的运行时字段。".to_string(),
fields: vec![
ManualField { name: "symbol".to_string(), field_type: "string".to_string(), detail: "证券代码。".to_string() },
ManualField { name: "market_cap/free_float_cap".to_string(), field_type: "float".to_string(), detail: "总市值、流通市值。".to_string() },
ManualField { name: "turnover_ratio/effective_turnover_ratio".to_string(), field_type: "float".to_string(), detail: "换手率、有效换手率。".to_string() },
ManualField { name: "open/close/last/last_price/prev_close".to_string(), field_type: "float".to_string(), detail: "开收盘与盘中价格。".to_string() },
ManualField { name: "upper_limit/lower_limit/price_tick/round_lot/minimum_order_quantity/order_step_size".to_string(), field_type: "float/int".to_string(), detail: "涨跌停、最小价位、整手、最小下单量和数量步长。KSH/BJSE 等板块可与 round_lot 不同。".to_string() },
ManualField { name: "paused/is_st/is_kcb/is_one_yuan/is_new_listing".to_string(), field_type: "bool".to_string(), detail: "可交易性与板块标志。".to_string() },
ManualField { name: "allow_buy/allow_sell/at_upper_limit/at_lower_limit".to_string(), field_type: "bool".to_string(), detail: "盘中买卖与涨跌停状态。".to_string() },
ManualField { name: "touched_upper_limit/touched_lower_limit/hit_upper_limit/hit_lower_limit".to_string(), field_type: "bool".to_string(), detail: "当日 tick 曾经触达涨跌停。".to_string() },
ManualField { name: "symbol_open_order_count/symbol_open_buy_qty/symbol_open_sell_qty/latest_symbol_open_order_id".to_string(), field_type: "int".to_string(), detail: "当前证券在挂单簿中的未成交挂单摘要和最近挂单 id。".to_string() },
ManualField { name: "in_dynamic_universe/is_subscribed".to_string(), field_type: "bool".to_string(), detail: "当前证券是否在动态 universe 内,以及是否仍在订阅集合中。".to_string() },
ManualField { name: "stock_ma5/stock_ma10/stock_ma20/stock_ma30".to_string(), field_type: "float".to_string(), detail: "个股价格均线内建别名。只内建这几个窗口15 日、45 日等任意窗口请改用 sma(\"close\", n)。".to_string() },
ManualField { name: "stock_volume_ma5/stock_volume_ma10/stock_volume_ma20/stock_volume_ma60".to_string(), field_type: "float".to_string(), detail: "个股成交量均线内建别名。只内建这几个窗口;任意窗口请改用 rolling_mean(\"volume\", n)。".to_string() },
ManualField { name: "listed_days".to_string(), field_type: "int".to_string(), detail: "上市天数。".to_string() },
],
},
ManualFieldGroup {
title: "持仓字段".to_string(),
detail: "在持仓止盈止损表达式中可用。".to_string(),
fields: vec![
ManualField { name: "avg_cost".to_string(), field_type: "float".to_string(), detail: "持仓均价。".to_string() },
ManualField { name: "current_price".to_string(), field_type: "float".to_string(), detail: "当前盘中价格。".to_string() },
ManualField { name: "holding_return".to_string(), field_type: "float".to_string(), detail: "持仓收益率,小数。".to_string() },
ManualField { name: "profit_pct".to_string(), field_type: "float".to_string(), detail: "持仓收益率,百分比。".to_string() },
ManualField { name: "quantity/sellable_qty".to_string(), field_type: "int".to_string(), detail: "持仓数量与可卖数量。".to_string() },
ManualField { name: "trading_pnl/position_pnl".to_string(), field_type: "float".to_string(), detail: "当日交易收益和昨仓持有收益,口径更接近 rqalpha StockPosition。".to_string() },
ManualField { name: "dividend_receivable".to_string(), field_type: "float".to_string(), detail: "当前 symbol 尚未到账的应收分红。".to_string() },
ManualField { name: "available_sellable_qty/reserved_open_sell_qty".to_string(), field_type: "int".to_string(), detail: "扣掉未成交卖单占用后的可卖数量,以及当前 symbol 已占用的卖出挂单数量。".to_string() },
],
},
],
functions: vec![
ManualFunction { name: "factor".to_string(), signature: "factor(\"column_name\")".to_string(), detail: "读取当前股票的数据库因子列。".to_string() },
ManualFunction { name: "day_factor".to_string(), signature: "day_factor(\"field_name\")".to_string(), detail: "读取日级/指数级字段映射。".to_string() },
ManualFunction { name: "history_bars".to_string(), signature: "ctx.history_bars(symbol, count, \"1d\" | \"1m\" | \"tick\", \"close\", include_now)".to_string(), detail: "回测内核策略上下文数据 API返回指定证券最近 N 条数值序列。日线字段支持 open/high/low/close/last/prev_close/volume/upper_limit/lower_limit分钟或 tick 字段支持 last/bid1/ask1/volume_delta/amount_delta。日线 include_now=false 排除当前交易日;分钟/tick 会按当前 on_bar、on_tick 或调度时刻截断include_now=false 排除当前 bar/tick避免未来函数。".to_string() },
ManualFunction { name: "current_snapshot".to_string(), signature: "ctx.current_snapshot(symbol)".to_string(), detail: "读取当前交易日指定证券的日级快照,可用于获得当日 open/close/last/upper_limit/lower_limit 等字段。".to_string() },
ManualFunction { name: "instrument/instruments/all_instruments".to_string(), signature: "ctx.instrument(symbol)".to_string(), detail: "读取证券元数据包括名称、板块、上市日期、退市日期、最小下单量、整手、最小价位等all_instruments 按证券代码稳定排序返回全量证券。".to_string() },
ManualFunction { name: "get_trading_dates/get_previous_trading_date/get_next_trading_date".to_string(), signature: "ctx.get_previous_trading_date(date, n)".to_string(), detail: "交易日历 API。get_trading_dates 返回闭区间交易日previous/next 返回相对某日向前或向后的第 n 个交易日,当前日自身不计入。".to_string() },
ManualFunction { name: "rolling_mean".to_string(), signature: "rolling_mean(\"field\", lookback)".to_string(), detail: "任意字段滚动均值,支持 volume/amount/turnover_ratio、signal_open/signal_close、benchmark_open/benchmark_close 等。任意成交量窗口推荐用它,比如 rolling_mean(\"volume\", 15)。".to_string() },
ManualFunction { name: "sma".to_string(), signature: "sma(\"field\", lookback)".to_string(), detail: "rolling_mean 的别名。任意价格均线窗口推荐用它,比如 sma(\"close\", 15)。".to_string() },
ManualFunction { name: "round/floor/ceil/abs/min/max/clamp".to_string(), signature: "round(x)".to_string(), detail: "常用数值函数。".to_string() },
ManualFunction { name: "safe_div".to_string(), signature: "safe_div(lhs, rhs, fallback)".to_string(), detail: "安全除法。".to_string() },
ManualFunction { name: "contains/starts_with/ends_with/lower/upper/trim/strlen".to_string(), signature: "starts_with(symbol, \"60\")".to_string(), detail: "字符串辅助函数。".to_string() },
],
factor_sources: vec![
ManualFactorSource {
table: "fi_data_center.bt_daily_features_v1".to_string(),
detail: "回测日频主表,提供价格、均线、换手率、涨跌停触达等字段。".to_string(),
fields: vec![],
},
ManualFactorSource {
table: "fi_data_center.benchmark_daily_v1".to_string(),
detail: "指数/基准日线表。".to_string(),
fields: vec![],
},
ManualFactorSource {
table: "fi_data_center.stock_indicator_factors_v1".to_string(),
detail: "股票指标因子原表,可映射进 factors[...]。".to_string(),
fields: vec![],
},
],
examples: vec![
ManualExample {
title: "均线 + 均量过滤".to_string(),
code: "filter.stock_expr(stock_ma5 > stock_ma20 && rolling_mean(\"volume\", 5) < rolling_mean(\"volume\", 60))".to_string(),
},
ManualExample {
title: "涨停触达后满仓,否则半仓".to_string(),
code: "allocation.buy_scale(touched_upper_limit ? 1.0 : 0.5)".to_string(),
},
ManualExample {
title: "next tick 撮合 + tick 滑点".to_string(),
code: "execution.matching_type(\"next_tick_last\")\nexecution.slippage(\"tick_size\", 1)".to_string(),
},
ManualExample {
title: "动态 universe 和订阅".to_string(),
code: "when(!has_dynamic_universe) { update_universe([\"000001.SZ\", \"000002.SZ\"]) }\nwhen(subscription_count == 0) { subscribe([\"000001.SZ\"]) }".to_string(),
},
ManualExample {
title: "显式下单并关闭默认轮动".to_string(),
code: "trading.rotation(false)\norder.value(\"600000.SH\", cash * 0.25, \"manual_entry\")\ncancel.symbol(\"600000.SH\", \"manual_cancel\")".to_string(),
},
],
}
}
pub fn merge_catalog_into_manual(
mut manual: StrategyAiManual,
catalog: &StrategyAiCatalog,
) -> StrategyAiManual {
for source in &mut manual.factor_sources {
if source.table.ends_with("bt_daily_features_v1") {
source.fields = catalog.daily_fields.clone();
} else if source.table.ends_with("benchmark_daily_v1") {
source.fields = catalog.benchmark_fields.clone();
} else if source.table.ends_with("stock_indicator_factors_v1") {
source.fields = catalog.indicator_factors.clone();
}
}
manual
}
pub fn render_manual_markdown(manual: &StrategyAiManual) -> String {
let mut out = String::new();
out.push_str(&format!("# {}\n\n", manual.title));
out.push_str(&format!("语言: `{}`\n\n", manual.language));
if !manual.overview.is_empty() {
out.push_str("## 概览\n");
for item in &manual.overview {
out.push_str(&format!("- {}\n", item));
}
out.push('\n');
}
out.push_str("## 语句块\n");
for item in &manual.statement_blocks {
out.push_str(&format!("- `{}`: {}\n", item.title, item.detail));
}
out.push('\n');
out.push_str("## 运行时字段\n");
for group in &manual.runtime_field_groups {
out.push_str(&format!("### {}\n{}\n", group.title, group.detail));
for field in &group.fields {
out.push_str(&format!(
"- `{}` (`{}`): {}\n",
field.name, field.field_type, field.detail
));
}
out.push('\n');
}
out.push_str("## 内建函数\n");
for function in &manual.functions {
out.push_str(&format!(
"- `{}` / `{}`: {}\n",
function.name, function.signature, function.detail
));
}
out.push('\n');
out.push_str("## 数据库字段与因子\n");
for source in &manual.factor_sources {
out.push_str(&format!("### `{}`\n{}\n", source.table, source.detail));
if source.fields.is_empty() {
out.push_str("- 当前未加载字段清单\n");
} else {
for field in &source.fields {
out.push_str(&format!("- `{}`\n", field));
}
}
out.push('\n');
}
out.push_str("## 示例\n");
for example in &manual.examples {
out.push_str(&format!(
"### {}\n```txt\n{}\n```\n\n",
example.title, example.code
));
}
out
}
pub fn build_generation_prompt(
manual_markdown: &str,
request: &StrategyAiGenerateRequest,
) -> String {
let mut prompt = String::new();
prompt.push_str("你是 OmniQuant 平台策略脚本生成器。必须输出可运行的平台策略脚本,不要输出 Python 或聚宽语法。\n");
prompt.push_str("必须遵守以下约束:\n");
prompt.push_str("- 只输出平台策略代码。\n");
prompt.push_str("- 不要输出解释文本。\n");
prompt.push_str("- 必须使用 strategy(\"...\") { ... } 语法。\n");
prompt.push_str("- 如需自定义参数,使用 let 和 fn。\n");
prompt.push_str("- 优先使用数据库已存在字段和 factors[...]。\n\n");
prompt.push_str("用户目标:\n");
prompt.push_str(&format!("- {}\n", request.user_goal));
if !request.constraints.is_empty() {
prompt.push_str("约束:\n");
for item in &request.constraints {
prompt.push_str(&format!("- {}\n", item));
}
}
prompt.push_str(&format!(
"\n固定运行条件market={}, benchmark={}, signal={}\n\n",
request.market, request.benchmark_symbol, request.signal_symbol
));
prompt.push_str("详细手册如下:\n\n");
prompt.push_str(manual_markdown);
prompt
}
pub fn build_optimization_prompt(
manual_markdown: &str,
request: &StrategyAiOptimizeRequest,
) -> String {
let mut prompt = String::new();
prompt.push_str("你是 OmniQuant 平台策略脚本优化器。必须输出完整、可运行的平台策略脚本,不要输出解释文本。\n");
prompt.push_str("优化目标:\n");
prompt.push_str(&format!("- {}\n\n", request.objective));
prompt.push_str("当前策略代码:\n```txt\n");
prompt.push_str(&request.current_code);
prompt.push_str("\n```\n\n");
if !request.diagnostics.is_empty() {
prompt.push_str("运行诊断:\n");
for item in &request.diagnostics {
prompt.push_str(&format!("- {}\n", item));
}
prompt.push('\n');
}
prompt.push_str("结果摘要 JSON\n```json\n");
prompt.push_str(
&serde_json::to_string_pretty(&request.result_summary).unwrap_or_else(|_| "{}".to_string()),
);
prompt.push_str("\n```\n\n");
prompt.push_str("详细手册如下:\n\n");
prompt.push_str(manual_markdown);
prompt
}