切换分钟执行价语义

This commit is contained in:
boris
2026-06-26 09:27:21 +08:00
parent 02e2a20aff
commit 6db480b91d
7 changed files with 48 additions and 33 deletions
+2 -2
View File
@@ -4,7 +4,7 @@
## 当前能力
- 日频分钟、tick 级策略生命周期与确定性回放。
- 日频分钟执行价策略生命周期与确定性回放;旧 tick 入口仅作为兼容别名映射到分钟执行价
- A 股行情、估值、因子、基准、候选资格、涨跌停触达、停牌和 ST 标记。
- 平台策略 DSL 与 `StrategyContext` 数据 API,不暴露非平台脚本语法。
- `BacktestConfig` 支持起止日期、初始资金、决策滞后、执行价格字段、基准代码。
@@ -51,7 +51,7 @@
- `futures`: 期货账户、合约参数、保证金、手续费和多空持仓。
- `rules`: 中国市场交易规则和风控校验。
- `broker`: 股票撮合、订单簿、滑点、成交量约束、限价和显式订单执行。
- `scheduler`: 日、周、月分钟、tick 调度规则。
- `scheduler`: 日、周、月分钟调度规则。
- `platform_expr_strategy`: 平台 DSL 解析后的表达式策略执行模型。
- `strategy`: 策略 trait、内置策略和运行时视图。
- `strategy_ai`: 策略 AI 手册、提示词生成和数据库字段目录合并。
+4 -5
View File
@@ -1598,7 +1598,7 @@ impl DataSet {
}
match normalize_history_frequency(frequency).as_deref() {
Some("1d") => self.history_daily_values(date, symbol, bar_count, field, include_now),
Some("1m") | Some("tick") => self.history_intraday_values(
Some("1m") => self.history_intraday_values(
date,
active_datetime,
symbol,
@@ -2143,7 +2143,7 @@ impl DataSet {
.map(Arc::as_ref)
.map(daily_market_price_bar)
.collect(),
Some("1m") | Some("tick") => {
Some("1m") => {
let mut bars = self
.execution_quotes_by_date
.iter()
@@ -3042,7 +3042,7 @@ fn intraday_quote_price_bar(snapshot: &IntradayExecutionQuote) -> PriceBar {
date: snapshot.date,
timestamp: Some(snapshot.timestamp.format("%Y-%m-%d %H:%M:%S").to_string()),
symbol: snapshot.symbol.clone(),
frequency: "tick".to_string(),
frequency: "1m".to_string(),
open: snapshot.last_price,
high: snapshot.last_price,
low: snapshot.last_price,
@@ -3090,8 +3090,7 @@ fn normalize_history_frequency(frequency: &str) -> Option<String> {
let normalized = normalize_field(frequency);
match normalized.as_str() {
"1d" | "d" | "day" | "daily" => Some("1d".to_string()),
"1m" | "m" | "minute" | "min" => Some("1m".to_string()),
"tick" | "t" => Some("tick".to_string()),
"1m" | "m" | "minute" | "min" | "tick" | "t" => Some("1m".to_string()),
_ => None,
}
}
+12 -2
View File
@@ -828,6 +828,8 @@ impl PlatformExprStrategy {
| "free_float_market_cap"
| "pe_ttm"
| "volume"
| "minute_volume"
| "intraday_volume"
| "tick_volume"
| "bid1_volume"
| "ask1_volume"
@@ -2872,6 +2874,8 @@ impl PlatformExprStrategy {
scope.push("free_float_market_cap", stock.free_float_cap);
scope.push("pe_ttm", stock.pe_ttm);
scope.push("volume", stock.volume);
scope.push("minute_volume", stock.tick_volume);
scope.push("intraday_volume", stock.tick_volume);
scope.push("tick_volume", stock.tick_volume);
scope.push("bid1_volume", stock.bid1_volume);
scope.push("ask1_volume", stock.ask1_volume);
@@ -2973,6 +2977,8 @@ impl PlatformExprStrategy {
);
factors.insert("pe_ttm".into(), Dynamic::from(stock.pe_ttm));
factors.insert("volume".into(), Dynamic::from(stock.volume));
factors.insert("minute_volume".into(), Dynamic::from(stock.tick_volume));
factors.insert("intraday_volume".into(), Dynamic::from(stock.tick_volume));
factors.insert("tick_volume".into(), Dynamic::from(stock.tick_volume));
factors.insert("bid1_volume".into(), Dynamic::from(stock.bid1_volume));
factors.insert("ask1_volume".into(), Dynamic::from(stock.ask1_volume));
@@ -5973,7 +5979,7 @@ impl PlatformExprStrategy {
"free_float_cap_bn" => Some(stock.free_float_cap_bn),
"pe_ttm" => Some(stock.pe_ttm),
"volume" => Some(stock.volume),
"tick_volume" => Some(stock.tick_volume as f64),
"minute_volume" | "intraday_volume" | "tick_volume" => Some(stock.tick_volume as f64),
"bid1_volume" => Some(stock.bid1_volume as f64),
"ask1_volume" => Some(stock.ask1_volume as f64),
"turnover" | "turnover_ratio" => Some(stock.turnover_ratio),
@@ -6315,7 +6321,9 @@ impl PlatformExprStrategy {
for name in Self::extract_identifier_candidates(&expr) {
match name.as_str() {
"last" | "last_price" | "bid1" | "ask1" | "bid1_volume" | "ask1_volume"
| "tick_volume" => return StockFilterQuoteUsage::IntradayQuote,
| "minute_volume" | "intraday_volume" | "tick_volume" => {
return StockFilterQuoteUsage::IntradayQuote;
}
"at_upper_limit" | "at_lower_limit" => {
usage = StockFilterQuoteUsage::LimitStateOnly;
}
@@ -6513,6 +6521,8 @@ impl PlatformExprStrategy {
| "free_float_cap_bn"
| "pe_ttm"
| "volume"
| "minute_volume"
| "intraday_volume"
| "tick_volume"
| "bid1_volume"
| "ask1_volume"
@@ -429,9 +429,11 @@ fn parse_matching_type(value: Option<&str>) -> Option<MatchingType> {
"open_auction" => Some(MatchingType::OpenAuction),
"current_bar_close" => Some(MatchingType::CurrentBarClose),
"next_bar_open" => Some(MatchingType::NextBarOpen),
"next_tick_last" => Some(MatchingType::NextTickLast),
"next_tick_best_own" => Some(MatchingType::NextTickBestOwn),
"next_tick_best_counterparty" => Some(MatchingType::NextTickBestCounterparty),
"next_minute_last" | "next_tick_last" => Some(MatchingType::NextTickLast),
"next_minute_best_own" | "next_tick_best_own" => Some(MatchingType::NextTickBestOwn),
"next_minute_best_counterparty" | "next_tick_best_counterparty" => {
Some(MatchingType::NextTickBestCounterparty)
}
"counterparty_offer" => Some(MatchingType::CounterpartyOffer),
"vwap" => Some(MatchingType::Vwap),
"twap" => Some(MatchingType::Twap),
@@ -1678,7 +1680,7 @@ mod tests {
let spec = serde_json::json!({
"execution": {
"compatibilityProfile": "aiquant_rqalpha",
"matchingType": "next_tick_last",
"matchingType": "next_minute_last",
"slippageModel": "price_ratio",
"slippageValue": 0.001,
"strictValueBudget": true
+10 -10
View File
@@ -203,8 +203,8 @@ pub fn built_in_strategy_manual() -> StrategyAiManual {
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: "回测内核支持 平台内核 风格的 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(),
title: "bar / minute execution 生命周期".to_string(),
detail: "回测内核支持 平台内核 风格的 bar/分钟执行价生命周期:日内会发布 pre_bar/bar/post_bar 过程事件;存在分钟执行价订阅或分钟调度规则时,会按 execution_quotes 的时间顺序发布 pre_tick/tick/post_tick 兼容事件,并把日内阶段下单限制在当前分钟执行价时间窗内撮合。平台 DSL 中可通过 subscribe([...])、trading.subscription_guard(true) 和 process_event 字段配合显式订单模拟日内订阅策略。".to_string(),
},
ManualSection {
title: "selection.market_cap_band / selection.limit / ordering.rank_by / ordering.rank_expr".to_string(),
@@ -220,7 +220,7 @@ pub fn built_in_strategy_manual() -> StrategyAiManual {
},
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 买一卖一近似 平台内核 的 tick 最优价语义;counterparty_offer 在存在 order_book_depth 多档盘口数据时会按真实档位逐档扫单并计算加权成交价,不存在 depth 时回退 L1 对手方报价;vwap 会在盘中执行价链路上聚合多笔成交为单条 VWAP 成交;next_bar_open 使用决策日信号并在下一可交易日开盘撮合,禁止把执行日 open/high/low/close 解释为下单前已知数据;open_auction 使用当日集合竞价开盘价 day_open 进行撮合,且不额外施加滑点,并按竞价成交量而不是盘口一档流动性限制成交;滑点支持 execution.slippage(\"none\") / execution.slippage(\"price_ratio\", 0.001) / execution.slippage(\"tick_size\", 1) / execution.slippage(\"limit_price\"),其中 limit_price 会在限价单成交时按挂单价模拟 平台内核 的最坏成交价。".to_string(),
detail: "设置撮合模式和滑点。优先使用 execution.matching_type(\"next_minute_last\" | \"next_minute_best_own\" | \"next_minute_best_counterparty\" | \"counterparty_offer\" | \"vwap\" | \"current_bar_close\" | \"next_bar_open\" | \"open_auction\");旧的 next_tick_* 仅作为兼容别名,内部仍读取分钟执行价。next_minute_last 使用分钟执行价 last_pricenext_minute_best_own / next_minute_best_counterparty 会按 L1 买一卖一近似 平台内核 的最优价语义;counterparty_offer 在存在 order_book_depth 多档盘口数据时会按真实档位逐档扫单并计算加权成交价,不存在 depth 时回退 L1 对手方报价;vwap 会在盘中执行价链路上聚合多笔成交为单条 VWAP 成交;next_bar_open 使用决策日信号并在下一可交易日开盘撮合,禁止把执行日 open/high/low/close 解释为下单前已知数据;open_auction 使用当日集合竞价开盘价 day_open 进行撮合,且不额外施加滑点,并按竞价成交量而不是盘口一档流动性限制成交;滑点支持 execution.slippage(\"none\") / execution.slippage(\"price_ratio\", 0.001) / execution.slippage(\"tick_size\", 1) / execution.slippage(\"limit_price\"),其中 limit_price 会在限价单成交时按挂单价模拟 平台内核 的最坏成交价。".to_string(),
},
ManualSection {
title: "期货提交校验".to_string(),
@@ -228,7 +228,7 @@ pub fn built_in_strategy_manual() -> StrategyAiManual {
},
ManualSection {
title: "trading.rotation / order.* / cancel.* / update_universe / subscribe".to_string(),
detail: "支持显式下单、撤单、AlgoOrder、动态 universe 和账户资金动作。可以用 trading.rotation(false) 关闭默认轮动链路,再用 trading.stage(\"open_auction\" | \"on_day\") 指定执行阶段;需要模拟 平台内核 的 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\"])、account.deposit_withdraw(100000, receiving_days=0)、account.finance_repay(50000)、account.set_management_fee_rate(0.001)。其中 order.target_shares(...) 对应 平台内核 的 order_toorder.target_portfolio_smart(...) 对应 平台内核 的 order_target_portfolio_smart 批量目标权重语义;account.deposit_withdraw(...) 和 account.finance_repay(...) 对应 平台内核 账户出入金与融资/还款语义;order_prices 既可以是逐标的限价映射,也可以是 VWAPOrder/TWAPOrder 这类全局 AlgoOrderorder.vwap_* / order.twap_* 对应 平台内核 的 AlgoOrder 时间窗订单风格,而 update_universe/subscribe/unsubscribe 对应 平台内核 的动态 universe 与订阅接口。symbol 使用标准证券代码;数量、金额、仓位、时间窗、限价、order_id 和 symbol 列表都支持表达式;这些语句也支持放进 when/unless 条件块。".to_string(),
detail: "支持显式下单、撤单、AlgoOrder、动态 universe 和账户资金动作。可以用 trading.rotation(false) 关闭默认轮动链路,再用 trading.stage(\"open_auction\" | \"on_day\") 指定执行阶段;需要模拟 平台内核 的日内订阅保护时,可写 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\"])、account.deposit_withdraw(100000, receiving_days=0)、account.finance_repay(50000)、account.set_management_fee_rate(0.001)。其中 order.target_shares(...) 对应 平台内核 的 order_toorder.target_portfolio_smart(...) 对应 平台内核 的 order_target_portfolio_smart 批量目标权重语义;account.deposit_withdraw(...) 和 account.finance_repay(...) 对应 平台内核 账户出入金与融资/还款语义;order_prices 既可以是逐标的限价映射,也可以是 VWAPOrder/TWAPOrder 这类全局 AlgoOrderorder.vwap_* / order.twap_* 对应 平台内核 的 AlgoOrder 时间窗订单风格,而 update_universe/subscribe/unsubscribe 对应 平台内核 的动态 universe 与订阅接口。symbol 使用标准证券代码;数量、金额、仓位、时间窗、限价、order_id 和 symbol 列表都支持表达式;这些语句也支持放进 when/unless 条件块。".to_string(),
},
ManualSection {
title: "when / unless / else".to_string(),
@@ -272,7 +272,7 @@ pub fn built_in_strategy_manual() -> StrategyAiManual {
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: "touched_upper_limit/touched_lower_limit/hit_upper_limit/hit_lower_limit".to_string(), field_type: "bool".to_string(), detail: "当日分钟执行价曾经触达涨跌停。".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: "latest_symbol_open_order_status/latest_symbol_open_order_unfilled_qty".to_string(), field_type: "string/int".to_string(), detail: "当前证券最近一笔挂单的状态和未成交数量。".to_string() },
ManualField { name: "in_dynamic_universe/is_subscribed".to_string(), field_type: "bool".to_string(), detail: "当前证券是否在动态 universe 内,以及是否仍在订阅集合中。".to_string() },
@@ -304,7 +304,7 @@ pub fn built_in_strategy_manual() -> StrategyAiManual {
functions: vec![
ManualFunction { name: "factor".to_string(), signature: "factor(\"column_name\")".to_string(), detail: "读取当前股票当日可用因子列。数值因子返回 float,字符串因子返回 string;缺失字段默认返回 0 或空字符串,建议重要条件配合 diagnostics 查看候选过滤数量。".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: "history_bars".to_string(), signature: "ctx.history_bars(symbol, count, \"1d\" | \"1m\", \"close\", include_now)".to_string(), detail: "回测内核策略上下文数据 API,返回指定证券最近 N 条数值序列。日线字段支持 open/high/low/close/last/prev_close/volume/upper_limit/lower_limit;分钟字段支持 last/bid1/ask1/volume_delta/amount_delta。\"tick\" 频率只作为兼容别名映射到 \"1m\",不再表示逐笔数据。日线 include_now=false 排除当前交易日;分钟线会按当前 on_bar、日内事件或调度时刻截断,include_now=false 排除当前分钟执行价,避免未来函数。".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: "active_instruments/instruments_history".to_string(), signature: "ctx.active_instruments(&[symbol])".to_string(), detail: "active_instruments 返回当前交易日已上市且未退市的证券;instruments_history 返回给定代码的历史证券记录,包含当前已退市标的,对齐 平台内核 的 active_instruments/instruments_history 能力。".to_string() },
@@ -360,7 +360,7 @@ pub fn built_in_strategy_manual() -> StrategyAiManual {
},
ManualFactorSource {
table: "盘口深度参数".to_string(),
detail: "可选字段包括 date、symbol、timestamp、level、bid_price、bid_volume、ask_price、ask_volume。存在盘口深度时,期货 counterparty_offer / next_tick_best_counterparty 可按真实多档盘口逐档扫单;不存在时不会伪造 depth。".to_string(),
detail: "可选字段包括 date、symbol、timestamp、level、bid_price、bid_volume、ask_price、ask_volume。存在盘口深度时,期货 counterparty_offer / next_minute_best_counterparty 可按真实多档盘口逐档扫单;不存在时不会伪造 depth。".to_string(),
fields: vec![],
},
ManualFactorSource {
@@ -383,8 +383,8 @@ pub fn built_in_strategy_manual() -> StrategyAiManual {
code: "filter.stock_expr(industry_name(\"citics\", 1) == \"电子\" && factor_text(\"concept\") == \"ai_chip\")".to_string(),
},
ManualExample {
title: "next tick 撮合 + tick 滑点".to_string(),
code: "execution.matching_type(\"next_tick_last\")\nexecution.slippage(\"tick_size\", 1)".to_string(),
title: "分钟执行价撮合 + 最小价位滑点".to_string(),
code: "execution.matching_type(\"next_minute_last\")\nexecution.slippage(\"tick_size\", 1)".to_string(),
},
ManualExample {
title: "动态 universe 和订阅".to_string(),
@@ -518,7 +518,7 @@ 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=\"market_cap\" 或 field=\"free_float_cap\", lower=..., upper=...;禁止使用 float_market_cap;禁止使用 ma60、stock_ma60、signal_ma60、benchmark_ma6060日价格均线写 rolling_mean(\"close\", 60) 或 ma(\"close\", 60),任意窗口均量写 rolling_mean(\"volume\", n) 或 vma(n);不要生成 fn score() 这类零参数函数,股票字段排序直接写在 ordering.rank_expr 内或用带参数函数;布尔字段按布尔使用,写 !is_st、!paused、!at_upper_limit、!at_lower_limit,不要写 is_st == 0risk.index_exposure 只能传一个数值表达式,不要使用 risk.exposure;完整三元表达式 cond ? a : b 可以使用,但不得输出残缺问号/冒号片段;execution.matching_type 只能取 next_tick_last、next_tick_best_own、next_tick_best_counterparty、counterparty_offer、vwap、current_bar_close、next_bar_open、open_auctionnext_bar_open 只能使用决策日信号,不能把执行日价格当作下单前信息;execution.slippage 必须写 execution.slippage(\"none\") 或 execution.slippage(\"price_ratio\", 0.001)。\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_ma6060日价格均线写 rolling_mean(\"close\", 60) 或 ma(\"close\", 60),任意窗口均量写 rolling_mean(\"volume\", n) 或 vma(n);不要生成 fn score() 这类零参数函数,股票字段排序直接写在 ordering.rank_expr 内或用带参数函数;布尔字段按布尔使用,写 !is_st、!paused、!at_upper_limit、!at_lower_limit,不要写 is_st == 0risk.index_exposure 只能传一个数值表达式,不要使用 risk.exposure;完整三元表达式 cond ? a : b 可以使用,但不得输出残缺问号/冒号片段;execution.matching_type 只能取 next_minute_last、next_minute_best_own、next_minute_best_counterparty、counterparty_offer、vwap、current_bar_close、next_bar_open、open_auction旧 next_tick_* 只是兼容别名,新生成策略不要使用;next_bar_open 只能使用决策日信号,不能把执行日价格当作下单前信息;execution.slippage 必须写 execution.slippage(\"none\") 或 execution.slippage(\"price_ratio\", 0.001)。\n");
prompt.push_str("回测成功但 tradeCount=0 或 holdingCount=0 是无效策略;第一版必须保持稳定买入覆盖率,复杂因子只能在后续优化中逐步加严。\n");
prompt.push_str("可参考但不要照抄的最小模板,回复时不要包含 ``` 代码围栏:\nstrategy(\"cn_a_smallcap_factor_rotation\") {\nmarket(\"CN_A\")\nbenchmark(\"000852.SH\")\nsignal(\"000001.SH\")\nrebalance.every_days(5).at([\"10:18\"])\nselection.limit(40)\nselection.market_cap_band(field=\"market_cap\", lower=0, upper=1000)\nfilter.stock_expr(listed_days >= 60 && !is_st && !paused && close > 2 && !at_upper_limit && !at_lower_limit)\nordering.rank_by(\"market_cap\", \"asc\")\nallocation.buy_scale(1.0)\nrisk.index_exposure(1.0)\nrisk.stop_loss(holding_return < -0.08)\nexecution.slippage(\"price_ratio\", 0.001)\n}\n\n");
prompt.push_str("用户目标:\n");
+9 -5
View File
@@ -919,14 +919,18 @@ impl Strategy for DataApiProbeStrategy {
let daily_price_count = ctx
.get_price("000001.SZ", d(2025, 1, 3), ctx.execution_date, "1d")
.len();
let tick_price_count = ctx
.get_price("000001.SZ", d(2025, 1, 3), ctx.execution_date, "tick")
.len();
let tick_alias_price_bars =
ctx.get_price("000001.SZ", d(2025, 1, 3), ctx.execution_date, "tick");
let tick_price_count = tick_alias_price_bars.len();
let tick_alias_frequency = tick_alias_price_bars
.first()
.map(|bar| bar.frequency.clone())
.unwrap_or_default();
let instrument_history_count =
ctx.instruments_history(&["000001.SZ", "000002.SZ"]).len();
let active_instrument_count = ctx.active_instruments(&["000001.SZ", "000002.SZ"]).len();
self.snapshots.borrow_mut().push(format!(
"daily={daily_close};previous={previous_close};tick={tick_last};previous_tick={previous_tick_last};current={current_close};instrument={instrument_name};all={};history={instrument_history_count};active={active_instrument_count};range={trading_date_count};prev={prev_date};next={next_date};suspended={suspended};st={st_flags};price_daily={daily_price_count};price_tick={tick_price_count}",
"daily={daily_close};previous={previous_close};minute={tick_last};previous_minute={previous_tick_last};current={current_close};instrument={instrument_name};all={};history={instrument_history_count};active={active_instrument_count};range={trading_date_count};prev={prev_date};next={next_date};suspended={suspended};st={st_flags};price_daily={daily_price_count};price_tick={tick_price_count};tick_alias_frequency={tick_alias_frequency}",
ctx.all_instruments().len()
));
}
@@ -2249,7 +2253,7 @@ fn strategy_context_exposes_engine_native_data_helpers() {
assert_eq!(
snapshots.borrow().as_slice(),
[
"daily=10.10,10.20;previous=10.00,10.10;tick=10.15,10.25;previous_tick=10.15;current=10.20;instrument=Anchor;all=2;history=2;active=1;range=3;prev=2025-01-03;next=2025-01-06;suspended=0,1,0;st=0,1,0;price_daily=2;price_tick=3"
"daily=10.10,10.20;previous=10.00,10.10;minute=10.15,10.25;previous_minute=10.15;current=10.20;instrument=Anchor;all=2;history=2;active=1;range=3;prev=2025-01-03;next=2025-01-06;suspended=0,1,0;st=0,1,0;price_daily=2;price_tick=3;tick_alias_frequency=1m"
]
);
}
+5 -5
View File
@@ -52,22 +52,22 @@ futures path. Confirmed aligned areas:
- [x] Rich explicit order styles exposed to platform scripts.
- [x] Minute-level `time_rule` semantics including market-open, market-close,
and physical-time style schedules.
- [x] Fine-grained daily, minute, and tick strategy execution entrypoints.
- [x] Fine-grained daily and minute execution quote strategy entrypoints.
- [x] Scheduled actions evaluated against explicit intraday times.
- [x] `update_universe`, `subscribe`, and `unsubscribe`.
- [x] Tick-frequency subscription guards at strategy API level.
- [x] Intraday subscription guards at strategy API level; legacy tick naming is compatibility-only.
- [x] VWAP and TWAP explicit action styles.
- [x] `order_target_portfolio_smart(..., order_prices=AlgoOrder, valuation_prices=...)`.
- [x] Trading PnL, position PnL, dividend receivable, and richer position
lifecycle fields.
- [x] Stock position aliases including `order_book_id`, `avg_price`,
`sellable`, `closable`, `equity`, and `position_prev_close`.
- [x] `history_bars` numeric helper for daily, intraday, and tick fields.
- [x] `history_bars` numeric helper for daily and minute execution quote fields.
- [x] `current_snapshot`, instrument metadata, all-instrument queries, and
active/historical instrument helpers.
- [x] Trading-date range, previous-date, and next-date helpers.
- [x] Phase-aware minute/tick history cursor semantics matching the active bar
or tick callback.
- [x] Phase-aware minute history cursor semantics matching the active bar or
intraday execution quote callback.
- [x] Suspension, ST, date-range price, active instrument, and instrument
history helpers.
- [x] Open-order status, unfilled quantity, final order lookup, average fill