修正next-open策略上下文日期
This commit is contained in:
@@ -4059,7 +4059,9 @@ mod date_format {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::collections::BTreeMap;
|
use std::cell::RefCell;
|
||||||
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use chrono::NaiveDate;
|
use chrono::NaiveDate;
|
||||||
|
|
||||||
@@ -4072,6 +4074,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
use crate::events::{OrderSide, OrderStatus};
|
use crate::events::{OrderSide, OrderStatus};
|
||||||
use crate::instrument::Instrument;
|
use crate::instrument::Instrument;
|
||||||
|
use crate::portfolio::PortfolioState;
|
||||||
use crate::risk_control::{FidcRiskControlConfig, RiskCheckScope};
|
use crate::risk_control::{FidcRiskControlConfig, RiskCheckScope};
|
||||||
use crate::rules::ChinaEquityRuleHooks;
|
use crate::rules::ChinaEquityRuleHooks;
|
||||||
use crate::scheduler::{ScheduleRule, ScheduleStage};
|
use crate::scheduler::{ScheduleRule, ScheduleStage};
|
||||||
@@ -4147,6 +4150,66 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ScheduledDataDateProbeStrategy {
|
||||||
|
rule: ScheduleRule,
|
||||||
|
expected_decision_date: NaiveDate,
|
||||||
|
observed: Rc<RefCell<Vec<String>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Strategy for ScheduledDataDateProbeStrategy {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"scheduled_data_date_probe"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn schedule_rules(&self) -> Vec<ScheduleRule> {
|
||||||
|
vec![self.rule.clone()]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_scheduled(
|
||||||
|
&mut self,
|
||||||
|
ctx: &StrategyContext<'_>,
|
||||||
|
rule: &ScheduleRule,
|
||||||
|
) -> Result<StrategyDecision, super::BacktestError> {
|
||||||
|
assert_eq!(rule.name, self.rule.name);
|
||||||
|
if ctx.decision_date != self.expected_decision_date {
|
||||||
|
return Ok(StrategyDecision::default());
|
||||||
|
}
|
||||||
|
let current_close = ctx
|
||||||
|
.current_snapshot(SYMBOL)
|
||||||
|
.map(|snapshot| format!("{:.2}", snapshot.close))
|
||||||
|
.unwrap_or_default();
|
||||||
|
let history_close = ctx
|
||||||
|
.history_bars(SYMBOL, 2, "1d", "close", true)
|
||||||
|
.iter()
|
||||||
|
.map(|value| format!("{value:.2}"))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(",");
|
||||||
|
let suspended = ctx
|
||||||
|
.is_suspended(SYMBOL, 1)
|
||||||
|
.into_iter()
|
||||||
|
.map(|value| if value { "1" } else { "0" })
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(",");
|
||||||
|
self.observed.borrow_mut().push(format!(
|
||||||
|
"signal={};execution={};active={};current={current_close};history={history_close};suspended={suspended}",
|
||||||
|
ctx.signal_date(),
|
||||||
|
ctx.execution_trade_date(),
|
||||||
|
ctx.current_datetime()
|
||||||
|
.map(|datetime| datetime.date().to_string())
|
||||||
|
.unwrap_or_default()
|
||||||
|
));
|
||||||
|
Ok(StrategyDecision {
|
||||||
|
order_intents: vec![OrderIntent::Shares {
|
||||||
|
symbol: SYMBOL.to_string(),
|
||||||
|
quantity: 100,
|
||||||
|
reason: "data_date_probe_buy".to_string(),
|
||||||
|
}],
|
||||||
|
..StrategyDecision::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct ScheduledRoundTripStrategy {
|
struct ScheduledRoundTripStrategy {
|
||||||
rule: ScheduleRule,
|
rule: ScheduleRule,
|
||||||
@@ -4679,6 +4742,80 @@ mod tests {
|
|||||||
assert_eq!(result.fills[0].price, 12.0);
|
assert_eq!(result.fills[0].price, 12.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn next_bar_open_strategy_context_data_helpers_use_decision_date() {
|
||||||
|
let first = d(2025, 1, 2);
|
||||||
|
let second = d(2025, 1, 3);
|
||||||
|
let observed = Rc::new(RefCell::new(Vec::new()));
|
||||||
|
let dataset = dataset_with(
|
||||||
|
market_with_state(first, 10.0, 11.5, true, 11.5, 9.0),
|
||||||
|
market_with_state(second, 12.0, 99.0, false, 200.0, 1.0),
|
||||||
|
candidate_with_state(first, true, false),
|
||||||
|
candidate(second),
|
||||||
|
);
|
||||||
|
let portfolio = PortfolioState::new(100_000.0);
|
||||||
|
let subscriptions = BTreeSet::new();
|
||||||
|
let manual_ctx = StrategyContext {
|
||||||
|
execution_date: second,
|
||||||
|
decision_date: first,
|
||||||
|
decision_index: 0,
|
||||||
|
data: &dataset,
|
||||||
|
portfolio: &portfolio,
|
||||||
|
futures_account: None,
|
||||||
|
open_orders: &[],
|
||||||
|
dynamic_universe: None,
|
||||||
|
subscriptions: &subscriptions,
|
||||||
|
process_events: &[],
|
||||||
|
active_process_event: None,
|
||||||
|
active_datetime: Some(second.and_hms_opt(9, 31, 0).unwrap()),
|
||||||
|
order_events: &[],
|
||||||
|
fills: &[],
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
manual_ctx
|
||||||
|
.current_snapshot(SYMBOL)
|
||||||
|
.map(|snapshot| snapshot.close),
|
||||||
|
Some(11.5)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
manual_ctx.history_bars(SYMBOL, 2, "1d", "close", true),
|
||||||
|
vec![11.5]
|
||||||
|
);
|
||||||
|
assert_eq!(manual_ctx.is_suspended(SYMBOL, 1), vec![true]);
|
||||||
|
let broker = scheduled_next_open_broker(FidcRiskControlConfig::default());
|
||||||
|
let config = BacktestConfig {
|
||||||
|
initial_cash: 100_000.0,
|
||||||
|
benchmark_code: "000852.SH".to_string(),
|
||||||
|
start_date: Some(first),
|
||||||
|
end_date: Some(second),
|
||||||
|
decision_lag_trading_days: 1,
|
||||||
|
execution_price_field: PriceField::Open,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = BacktestEngine::new(
|
||||||
|
dataset.clone(),
|
||||||
|
ScheduledDataDateProbeStrategy {
|
||||||
|
rule: ScheduleRule::daily("daily_data_date_probe", ScheduleStage::OnDay),
|
||||||
|
expected_decision_date: first,
|
||||||
|
observed: observed.clone(),
|
||||||
|
},
|
||||||
|
broker,
|
||||||
|
config,
|
||||||
|
)
|
||||||
|
.run()
|
||||||
|
.expect("backtest run");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
observed.borrow().as_slice(),
|
||||||
|
[
|
||||||
|
"signal=2025-01-02;execution=2025-01-03;active=2025-01-02;current=11.50;history=11.50;suspended=1"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
assert_eq!(result.fills.len(), 1);
|
||||||
|
assert_eq!(result.fills[0].date, second);
|
||||||
|
assert_eq!(result.fills[0].price, 12.0);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn next_bar_open_execution_risk_ignores_decision_day_paused_state() {
|
fn next_bar_open_execution_risk_ignores_decision_day_paused_state() {
|
||||||
let first = d(2025, 1, 2);
|
let first = d(2025, 1, 2);
|
||||||
|
|||||||
@@ -156,6 +156,31 @@ pub struct StrategyContext<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl StrategyContext<'_> {
|
impl StrategyContext<'_> {
|
||||||
|
pub fn signal_date(&self) -> NaiveDate {
|
||||||
|
self.decision_date
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execution_trade_date(&self) -> NaiveDate {
|
||||||
|
self.execution_date
|
||||||
|
}
|
||||||
|
|
||||||
|
fn current_data_date(&self) -> NaiveDate {
|
||||||
|
let active_date = self
|
||||||
|
.active_datetime
|
||||||
|
.map(|datetime| datetime.date())
|
||||||
|
.unwrap_or(self.decision_date);
|
||||||
|
if self.is_lagged_execution() && active_date > self.decision_date {
|
||||||
|
self.decision_date
|
||||||
|
} else {
|
||||||
|
active_date
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn current_data_datetime(&self) -> Option<NaiveDateTime> {
|
||||||
|
self.active_datetime
|
||||||
|
.map(|datetime| self.current_data_date().and_time(datetime.time()))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_lagged_execution(&self) -> bool {
|
pub fn is_lagged_execution(&self) -> bool {
|
||||||
self.execution_date != self.decision_date
|
self.execution_date != self.decision_date
|
||||||
}
|
}
|
||||||
@@ -544,7 +569,7 @@ impl StrategyContext<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_snapshot(&self, symbol: &str) -> Option<&DailyMarketSnapshot> {
|
pub fn current_snapshot(&self, symbol: &str) -> Option<&DailyMarketSnapshot> {
|
||||||
self.data.market(self.execution_date, symbol)
|
self.data.market(self.current_data_date(), symbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn history_bars(
|
pub fn history_bars(
|
||||||
@@ -556,8 +581,8 @@ impl StrategyContext<'_> {
|
|||||||
include_now: bool,
|
include_now: bool,
|
||||||
) -> Vec<f64> {
|
) -> Vec<f64> {
|
||||||
self.data.history_bars_at(
|
self.data.history_bars_at(
|
||||||
self.execution_date,
|
self.current_data_date(),
|
||||||
self.active_datetime,
|
self.current_data_datetime(),
|
||||||
symbol,
|
symbol,
|
||||||
bar_count,
|
bar_count,
|
||||||
frequency,
|
frequency,
|
||||||
@@ -573,7 +598,7 @@ impl StrategyContext<'_> {
|
|||||||
include_now: bool,
|
include_now: bool,
|
||||||
) -> Vec<DailyMarketSnapshot> {
|
) -> Vec<DailyMarketSnapshot> {
|
||||||
self.data
|
self.data
|
||||||
.history_daily_snapshots(self.execution_date, symbol, bar_count, include_now)
|
.history_daily_snapshots(self.current_data_date(), symbol, bar_count, include_now)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn history_intraday_quotes(
|
pub fn history_intraday_quotes(
|
||||||
@@ -583,8 +608,8 @@ impl StrategyContext<'_> {
|
|||||||
include_now: bool,
|
include_now: bool,
|
||||||
) -> Vec<IntradayExecutionQuote> {
|
) -> Vec<IntradayExecutionQuote> {
|
||||||
self.data.history_intraday_quotes_at(
|
self.data.history_intraday_quotes_at(
|
||||||
self.execution_date,
|
self.current_data_date(),
|
||||||
self.active_datetime,
|
self.current_data_datetime(),
|
||||||
symbol,
|
symbol,
|
||||||
bar_count,
|
bar_count,
|
||||||
include_now,
|
include_now,
|
||||||
@@ -607,7 +632,8 @@ impl StrategyContext<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn active_instruments(&self, symbols: &[&str]) -> Vec<&Instrument> {
|
pub fn active_instruments(&self, symbols: &[&str]) -> Vec<&Instrument> {
|
||||||
self.data.active_instruments(self.execution_date, symbols)
|
self.data
|
||||||
|
.active_instruments(self.current_data_date(), symbols)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn all_instruments(&self) -> Vec<&Instrument> {
|
pub fn all_instruments(&self) -> Vec<&Instrument> {
|
||||||
@@ -628,12 +654,12 @@ impl StrategyContext<'_> {
|
|||||||
|
|
||||||
pub fn is_suspended(&self, symbol: &str, count: usize) -> Vec<bool> {
|
pub fn is_suspended(&self, symbol: &str, count: usize) -> Vec<bool> {
|
||||||
self.data
|
self.data
|
||||||
.is_suspended_flags(self.execution_date, symbol, count)
|
.is_suspended_flags(self.current_data_date(), symbol, count)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_st_stock(&self, symbol: &str, count: usize) -> Vec<bool> {
|
pub fn is_st_stock(&self, symbol: &str, count: usize) -> Vec<bool> {
|
||||||
self.data
|
self.data
|
||||||
.is_st_stock_flags(self.execution_date, symbol, count)
|
.is_st_stock_flags(self.current_data_date(), symbol, count)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_price(
|
pub fn get_price(
|
||||||
@@ -647,18 +673,20 @@ impl StrategyContext<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_dividend(&self, symbol: &str, start: NaiveDate) -> Vec<DividendRecord> {
|
pub fn get_dividend(&self, symbol: &str, start: NaiveDate) -> Vec<DividendRecord> {
|
||||||
|
let current_data_date = self.current_data_date();
|
||||||
let end = self
|
let end = self
|
||||||
.data
|
.data
|
||||||
.previous_trading_date(self.execution_date, 1)
|
.previous_trading_date(current_data_date, 1)
|
||||||
.unwrap_or(self.execution_date);
|
.unwrap_or(current_data_date);
|
||||||
self.data.get_dividend(symbol, start, end)
|
self.data.get_dividend(symbol, start, end)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_split(&self, symbol: &str, start: NaiveDate) -> Vec<SplitRecord> {
|
pub fn get_split(&self, symbol: &str, start: NaiveDate) -> Vec<SplitRecord> {
|
||||||
|
let current_data_date = self.current_data_date();
|
||||||
let end = self
|
let end = self
|
||||||
.data
|
.data
|
||||||
.previous_trading_date(self.execution_date, 1)
|
.previous_trading_date(current_data_date, 1)
|
||||||
.unwrap_or(self.execution_date);
|
.unwrap_or(current_data_date);
|
||||||
self.data.get_split(symbol, start, end)
|
self.data.get_split(symbol, start, end)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -693,7 +721,7 @@ impl StrategyContext<'_> {
|
|||||||
|
|
||||||
pub fn get_margin_stocks(&self, margin_type: &str) -> Vec<String> {
|
pub fn get_margin_stocks(&self, margin_type: &str) -> Vec<String> {
|
||||||
self.data
|
self.data
|
||||||
.get_margin_stocks(self.execution_date, margin_type)
|
.get_margin_stocks(self.current_data_date(), margin_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_securities_margin(
|
pub fn get_securities_margin(
|
||||||
@@ -787,7 +815,7 @@ impl StrategyContext<'_> {
|
|||||||
|
|
||||||
pub fn get_industry(&self, symbol: &str, source: &str, level: usize) -> Option<FactorValue> {
|
pub fn get_industry(&self, symbol: &str, source: &str, level: usize) -> Option<FactorValue> {
|
||||||
self.data
|
self.data
|
||||||
.get_industry(symbol, self.execution_date, source, level)
|
.get_industry(symbol, self.current_data_date(), source, level)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_industry_name(
|
pub fn get_industry_name(
|
||||||
@@ -797,12 +825,12 @@ impl StrategyContext<'_> {
|
|||||||
level: usize,
|
level: usize,
|
||||||
) -> Option<FactorTextValue> {
|
) -> Option<FactorTextValue> {
|
||||||
self.data
|
self.data
|
||||||
.get_industry_name(symbol, self.execution_date, source, level)
|
.get_industry_name(symbol, self.current_data_date(), source, level)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_dominant_future(&self, underlying_symbol: &str) -> Option<String> {
|
pub fn get_dominant_future(&self, underlying_symbol: &str) -> Option<String> {
|
||||||
self.data
|
self.data
|
||||||
.get_dominant_future(underlying_symbol, self.execution_date)
|
.get_dominant_future(underlying_symbol, self.current_data_date())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_dominant_future_price(
|
pub fn get_dominant_future_price(
|
||||||
|
|||||||
@@ -311,12 +311,12 @@ pub fn built_in_strategy_manual() -> StrategyAiManual {
|
|||||||
functions: vec![
|
functions: vec![
|
||||||
ManualFunction { name: "factor".to_string(), signature: "factor(\"column_name\")".to_string(), detail: "读取当前股票当日可用因子列。数值因子返回 float,字符串因子返回 string;缺失字段默认返回 0 或空字符串,建议重要条件配合 diagnostics 查看候选过滤数量。".to_string() },
|
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: "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\", \"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。日线 include_now=false 排除当前交易日;分钟线会按当前 on_bar、日内事件或调度时刻截断,include_now=false 排除当前分钟执行价,避免未来函数。".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。日线 include_now=false 排除当前信号日;分钟线会按当前 on_bar、日内事件或调度时刻截断,include_now=false 排除当前分钟执行价。next_bar_open 下该 API 只能看到信号日数据,不能读取实际成交日数据。".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: "current_snapshot".to_string(), signature: "ctx.current_snapshot(symbol)".to_string(), detail: "读取当前信号日指定证券的日级快照,可用于获得信号日 open/close/last/upper_limit/lower_limit 等字段;next_bar_open 的实际成交日涨跌停、停牌、ST、退市、一元、黑名单、成交量和盘口流动性由撮合层按执行日判断。".to_string() },
|
||||||
ManualFunction { name: "instrument/instruments/all_instruments".to_string(), signature: "ctx.instrument(symbol)".to_string(), detail: "读取证券元数据,包括名称、板块、上市日期、退市日期、最小下单量、整手、最小价位等;all_instruments 按证券代码稳定排序返回全量证券。".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() },
|
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() },
|
||||||
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: "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: "is_suspended/is_st_stock".to_string(), signature: "ctx.is_suspended(symbol, count)".to_string(), detail: "读取指定证券截至当前交易日最近 count 个交易日的停牌或 ST 标记,返回 bool 序列,顺序从旧到新;对应平台内核的 is_suspended/is_st_stock 数据能力。".to_string() },
|
ManualFunction { name: "is_suspended/is_st_stock".to_string(), signature: "ctx.is_suspended(symbol, count)".to_string(), detail: "读取指定证券截至当前信号日最近 count 个交易日的停牌或 ST 标记,返回 bool 序列,顺序从旧到新;对应平台内核的 is_suspended/is_st_stock 数据能力。执行日停牌或 ST 只能由撮合风控判断,不能在 next_bar_open 的 T 日提前固化。".to_string() },
|
||||||
ManualFunction { name: "get_price".to_string(), signature: "ctx.get_price(symbol, start_date, end_date, \"1d\" | \"1m\")".to_string(), detail: "按日期区间读取统一 PriceBar 序列。日线返回 open/high/low/close/last/volume/盘口字段;分钟线返回按 timestamp 排序的 last/bid1/ask1/volume_delta/amount_delta 映射,便于服务层转成表格或前端明细。".to_string() },
|
ManualFunction { name: "get_price".to_string(), signature: "ctx.get_price(symbol, start_date, end_date, \"1d\" | \"1m\")".to_string(), detail: "按日期区间读取统一 PriceBar 序列。日线返回 open/high/low/close/last/volume/盘口字段;分钟线返回按 timestamp 排序的 last/bid1/ask1/volume_delta/amount_delta 映射,便于服务层转成表格或前端明细。".to_string() },
|
||||||
ManualFunction { name: "get_dividend / dividend_cash / has_dividend".to_string(), signature: "dividend_cash(lookback) / has_dividend(lookback)".to_string(), detail: "高级数据 风格分红 API。Rust Context 可用 ctx.get_dividend(symbol, start_date) 读取明细;平台表达式可用 dividend_cash(lookback) 汇总当前股票最近 N 个交易日现金分红,用 has_dividend(lookback) 判断是否发生分红,也支持 dividend_cash(\"600000.SH\", lookback)。".to_string() },
|
ManualFunction { name: "get_dividend / dividend_cash / has_dividend".to_string(), signature: "dividend_cash(lookback) / has_dividend(lookback)".to_string(), detail: "高级数据 风格分红 API。Rust Context 可用 ctx.get_dividend(symbol, start_date) 读取明细;平台表达式可用 dividend_cash(lookback) 汇总当前股票最近 N 个交易日现金分红,用 has_dividend(lookback) 判断是否发生分红,也支持 dividend_cash(\"600000.SH\", lookback)。".to_string() },
|
||||||
ManualFunction { name: "get_split / split_ratio / has_split".to_string(), signature: "split_ratio(lookback) / has_split(lookback)".to_string(), detail: "高级数据 风格拆分/送转 API。Rust Context 可用 ctx.get_split(symbol, start_date) 读取明细;平台表达式可用 split_ratio(lookback) 计算当前股票最近 N 个交易日累计拆分比例,has_split(lookback) 判断是否发生送转。".to_string() },
|
ManualFunction { name: "get_split / split_ratio / has_split".to_string(), signature: "split_ratio(lookback) / has_split(lookback)".to_string(), detail: "高级数据 风格拆分/送转 API。Rust Context 可用 ctx.get_split(symbol, start_date) 读取明细;平台表达式可用 split_ratio(lookback) 计算当前股票最近 N 个交易日累计拆分比例,has_split(lookback) 判断是否发生送转。".to_string() },
|
||||||
|
|||||||
Reference in New Issue
Block a user