Add suspended and ST data helpers
This commit is contained in:
@@ -947,6 +947,18 @@ impl DataSet {
|
|||||||
self.calendar.next_trading_date(date, n)
|
self.calendar.next_trading_date(date, n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_suspended_flags(&self, date: NaiveDate, symbol: &str, count: usize) -> Vec<bool> {
|
||||||
|
self.historical_daily_flags(date, symbol, count, |candidate, market| {
|
||||||
|
candidate.is_some_and(|row| row.is_paused) || market.is_some_and(|row| row.paused)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_st_stock_flags(&self, date: NaiveDate, symbol: &str, count: usize) -> Vec<bool> {
|
||||||
|
self.historical_daily_flags(date, symbol, count, |candidate, _| {
|
||||||
|
candidate.is_some_and(|row| row.is_st)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn price(&self, date: NaiveDate, symbol: &str, field: PriceField) -> Option<f64> {
|
pub fn price(&self, date: NaiveDate, symbol: &str, field: PriceField) -> Option<f64> {
|
||||||
let snapshot = self.market(date, symbol)?;
|
let snapshot = self.market(date, symbol)?;
|
||||||
Some(snapshot.price(field))
|
Some(snapshot.price(field))
|
||||||
@@ -1047,6 +1059,36 @@ impl DataSet {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn historical_daily_flags<F>(
|
||||||
|
&self,
|
||||||
|
date: NaiveDate,
|
||||||
|
symbol: &str,
|
||||||
|
count: usize,
|
||||||
|
evaluator: F,
|
||||||
|
) -> Vec<bool>
|
||||||
|
where
|
||||||
|
F: Fn(Option<&CandidateEligibility>, Option<&DailyMarketSnapshot>) -> bool,
|
||||||
|
{
|
||||||
|
if count == 0 {
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
|
let days = self
|
||||||
|
.calendar
|
||||||
|
.iter()
|
||||||
|
.filter(|day| *day <= date)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let start = days.len().saturating_sub(count);
|
||||||
|
days[start..]
|
||||||
|
.iter()
|
||||||
|
.map(|day| {
|
||||||
|
evaluator(
|
||||||
|
self.candidate_index.get(&(*day, symbol.to_string())),
|
||||||
|
self.market_index.get(&(*day, symbol.to_string())),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn market_decision_close(&self, date: NaiveDate, symbol: &str) -> Option<f64> {
|
pub fn market_decision_close(&self, date: NaiveDate, symbol: &str) -> Option<f64> {
|
||||||
self.market_series_by_symbol
|
self.market_series_by_symbol
|
||||||
.get(symbol)
|
.get(symbol)
|
||||||
|
|||||||
@@ -285,6 +285,16 @@ impl StrategyContext<'_> {
|
|||||||
self.data.next_trading_date(date, n)
|
self.data.next_trading_date(date, n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_suspended(&self, symbol: &str, count: usize) -> Vec<bool> {
|
||||||
|
self.data
|
||||||
|
.is_suspended_flags(self.execution_date, symbol, count)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_st_stock(&self, symbol: &str, count: usize) -> Vec<bool> {
|
||||||
|
self.data
|
||||||
|
.is_st_stock_flags(self.execution_date, symbol, count)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn has_subscriptions(&self) -> bool {
|
pub fn has_subscriptions(&self) -> bool {
|
||||||
!self.subscriptions.is_empty()
|
!self.subscriptions.is_empty()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -199,6 +199,7 @@ pub fn built_in_strategy_manual() -> StrategyAiManual {
|
|||||||
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 等字段。".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: "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 序列,顺序从旧到新;对应 RQAlpha 的 is_suspended/is_st_stock 数据源能力。".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: "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: "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: "round/floor/ceil/abs/min/max/clamp".to_string(), signature: "round(x)".to_string(), detail: "常用数值函数。".to_string() },
|
||||||
|
|||||||
@@ -20,6 +20,14 @@ fn dt(year: i32, month: u32, day: u32, hour: u32, minute: u32, second: u32) -> N
|
|||||||
.expect("valid datetime")
|
.expect("valid datetime")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bool_flags(values: Vec<bool>) -> String {
|
||||||
|
values
|
||||||
|
.into_iter()
|
||||||
|
.map(|value| if value { "1" } else { "0" })
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(",")
|
||||||
|
}
|
||||||
|
|
||||||
struct HookProbeStrategy {
|
struct HookProbeStrategy {
|
||||||
log: Rc<RefCell<Vec<String>>>,
|
log: Rc<RefCell<Vec<String>>>,
|
||||||
}
|
}
|
||||||
@@ -420,8 +428,10 @@ impl Strategy for DataApiProbeStrategy {
|
|||||||
let trading_date_count = ctx
|
let trading_date_count = ctx
|
||||||
.get_trading_dates(d(2025, 1, 2), ctx.execution_date)
|
.get_trading_dates(d(2025, 1, 2), ctx.execution_date)
|
||||||
.len();
|
.len();
|
||||||
|
let suspended = bool_flags(ctx.is_suspended("000001.SZ", 3));
|
||||||
|
let st_flags = bool_flags(ctx.is_st_stock("000001.SZ", 3));
|
||||||
self.snapshots.borrow_mut().push(format!(
|
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={};range={trading_date_count};prev={prev_date};next={next_date}",
|
"daily={daily_close};previous={previous_close};tick={tick_last};previous_tick={previous_tick_last};current={current_close};instrument={instrument_name};all={};range={trading_date_count};prev={prev_date};next={next_date};suspended={suspended};st={st_flags}",
|
||||||
ctx.all_instruments().len()
|
ctx.all_instruments().len()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -935,20 +945,24 @@ fn strategy_context_exposes_rqalpha_style_data_helpers() {
|
|||||||
extra_factors: BTreeMap::new(),
|
extra_factors: BTreeMap::new(),
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let candidates = [date1, date2, date3]
|
let candidates = [
|
||||||
.into_iter()
|
(date1, false, false),
|
||||||
.map(|date| CandidateEligibility {
|
(date2, true, true),
|
||||||
date,
|
(date3, false, false),
|
||||||
symbol: "000001.SZ".to_string(),
|
]
|
||||||
is_st: false,
|
.into_iter()
|
||||||
is_new_listing: false,
|
.map(|(date, is_paused, is_st)| CandidateEligibility {
|
||||||
is_paused: false,
|
date,
|
||||||
allow_buy: true,
|
symbol: "000001.SZ".to_string(),
|
||||||
allow_sell: true,
|
is_st,
|
||||||
is_kcb: false,
|
is_new_listing: false,
|
||||||
is_one_yuan: false,
|
is_paused,
|
||||||
})
|
allow_buy: true,
|
||||||
.collect::<Vec<_>>();
|
allow_sell: true,
|
||||||
|
is_kcb: false,
|
||||||
|
is_one_yuan: false,
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
let benchmarks = [
|
let benchmarks = [
|
||||||
(date1, 100.0, 99.0),
|
(date1, 100.0, 99.0),
|
||||||
(date2, 101.0, 100.0),
|
(date2, 101.0, 100.0),
|
||||||
@@ -1045,7 +1059,7 @@ fn strategy_context_exposes_rqalpha_style_data_helpers() {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
snapshots.borrow().as_slice(),
|
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=1;range=3;prev=2025-01-03;next=2025-01-06"
|
"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=1;range=3;prev=2025-01-03;next=2025-01-06;suspended=0,1,0;st=0,1,0"
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,13 @@ current alignment pass.
|
|||||||
- [x] phase-aware minute/tick history cursor semantics matching the active
|
- [x] phase-aware minute/tick history cursor semantics matching the active
|
||||||
bar or tick callback
|
bar or tick callback
|
||||||
|
|
||||||
|
### Phase 7: Remaining stock data-source API parity
|
||||||
|
|
||||||
|
- [x] `is_suspended`
|
||||||
|
- [x] `is_st_stock`
|
||||||
|
- [ ] `get_price` style date-range tabular API
|
||||||
|
- [ ] `instruments_history`
|
||||||
|
|
||||||
## Execution Order
|
## Execution Order
|
||||||
|
|
||||||
1. Close the explicit order API gap with target-shares / `order_to` parity.
|
1. Close the explicit order API gap with target-shares / `order_to` parity.
|
||||||
@@ -64,10 +71,11 @@ current alignment pass.
|
|||||||
4. Add dynamic universe APIs.
|
4. Add dynamic universe APIs.
|
||||||
5. Add algo-order styles.
|
5. Add algo-order styles.
|
||||||
6. Finish position accounting parity.
|
6. Finish position accounting parity.
|
||||||
7. Continue parity audit for remaining account, order, and data-source APIs.
|
7. Continue stock data-source API parity.
|
||||||
|
8. Continue parity audit for remaining account and order object APIs.
|
||||||
|
|
||||||
## Current Step
|
## Current Step
|
||||||
|
|
||||||
Active implementation target: continue parity audit for remaining account,
|
Active implementation target: continue stock data-source API parity after
|
||||||
order, and data-source APIs after the stock strategy API, scheduler, universe,
|
covering suspended/ST historical flags; next larger gap is a `get_price` style
|
||||||
algo-order, position accounting, and core strategy data helpers are covered.
|
date-range tabular API and instruments history.
|
||||||
|
|||||||
Reference in New Issue
Block a user