修复执行价索引和平台表达式回退

This commit is contained in:
boris
2026-06-12 23:46:44 +08:00
parent 9b4462f880
commit 4cf90d83a3
5 changed files with 362 additions and 124 deletions
+88 -27
View File
@@ -734,6 +734,27 @@ impl BenchmarkPriceSeries {
Some(sum / lookback as f64)
}
fn decision_values_for(
&self,
date: NaiveDate,
lookback: usize,
field: PriceField,
) -> Vec<f64> {
if lookback == 0 {
return Vec::new();
}
let end = match self.dates.binary_search(&date) {
Ok(idx) => idx,
Err(0) => return Vec::new(),
Err(idx) => idx,
};
let start = end.saturating_sub(lookback);
match field {
PriceField::DayOpen | PriceField::Open => self.opens[start..end].to_vec(),
PriceField::Close | PriceField::Last => self.closes[start..end].to_vec(),
}
}
fn moving_average_for(
&self,
date: NaiveDate,
@@ -791,7 +812,7 @@ pub struct DataSet {
candidate_by_date: BTreeMap<NaiveDate, Vec<CandidateEligibility>>,
candidate_index: HashMap<(NaiveDate, String), CandidateEligibility>,
corporate_actions_by_date: BTreeMap<NaiveDate, Vec<CorporateAction>>,
execution_quotes_index: HashMap<(NaiveDate, String), Vec<IntradayExecutionQuote>>,
execution_quotes_by_date: HashMap<NaiveDate, HashMap<String, Vec<IntradayExecutionQuote>>>,
order_book_depth_index: HashMap<(NaiveDate, String), Vec<IntradayOrderBookDepthLevel>>,
benchmark_by_date: BTreeMap<NaiveDate, BenchmarkSnapshot>,
market_series_by_symbol: HashMap<String, SymbolPriceSeries>,
@@ -1075,7 +1096,7 @@ impl DataSet {
.map(|item| ((item.date, item.symbol.clone()), item))
.collect::<HashMap<_, _>>();
let corporate_actions_by_date = group_by_date(corporate_actions, |item| item.date);
let execution_quotes_index = build_execution_quote_index(execution_quotes);
let execution_quotes_by_date = build_execution_quote_index(execution_quotes);
let order_book_depth_index = build_order_book_depth_index(order_book_depth);
let benchmark_by_date = benchmarks
@@ -1105,7 +1126,7 @@ impl DataSet {
candidate_by_date,
candidate_index,
corporate_actions_by_date,
execution_quotes_index,
execution_quotes_by_date,
order_book_depth_index,
benchmark_by_date,
market_series_by_symbol,
@@ -1177,14 +1198,22 @@ impl DataSet {
}
pub fn execution_quotes_on(&self, date: NaiveDate, symbol: &str) -> &[IntradayExecutionQuote] {
self.execution_quotes_index
.get(&(date, symbol.to_string()))
self.execution_quotes_by_date
.get(&date)
.and_then(|rows_by_symbol| rows_by_symbol.get(symbol))
.map(Vec::as_slice)
.unwrap_or(&[])
}
pub fn execution_quote_key_set(&self) -> HashSet<(NaiveDate, String)> {
self.execution_quotes_index.keys().cloned().collect()
self.execution_quotes_by_date
.iter()
.flat_map(|(date, rows_by_symbol)| {
rows_by_symbol
.keys()
.map(move |symbol| (*date, symbol.clone()))
})
.collect()
}
pub fn add_execution_quotes(&mut self, quotes: Vec<IntradayExecutionQuote>) -> usize {
@@ -1192,7 +1221,12 @@ impl DataSet {
let mut touched = HashSet::<(NaiveDate, String)>::new();
for quote in quotes {
let key = (quote.date, quote.symbol.clone());
let rows = self.execution_quotes_index.entry(key.clone()).or_default();
let rows = self
.execution_quotes_by_date
.entry(quote.date)
.or_default()
.entry(quote.symbol.clone())
.or_default();
if rows.iter().any(|existing| {
existing.timestamp == quote.timestamp && existing.symbol == quote.symbol
}) {
@@ -1202,8 +1236,12 @@ impl DataSet {
touched.insert(key);
added += 1;
}
for key in touched {
if let Some(rows) = self.execution_quotes_index.get_mut(&key) {
for (date, symbol) in touched {
if let Some(rows) = self
.execution_quotes_by_date
.get_mut(&date)
.and_then(|rows_by_symbol| rows_by_symbol.get_mut(&symbol))
{
rows.sort_by_key(|quote| quote.timestamp);
}
}
@@ -1223,10 +1261,11 @@ impl DataSet {
pub fn execution_quotes_on_date(&self, date: NaiveDate) -> Vec<IntradayExecutionQuote> {
let mut quotes = self
.execution_quotes_index
.iter()
.filter(|((quote_date, _), _)| *quote_date == date)
.flat_map(|(_, rows)| rows.iter().cloned())
.execution_quotes_by_date
.get(&date)
.into_iter()
.flat_map(|rows_by_symbol| rows_by_symbol.values())
.flat_map(|rows| rows.iter().cloned())
.collect::<Vec<_>>();
quotes.sort_by(|left, right| {
left.timestamp
@@ -1346,10 +1385,10 @@ impl DataSet {
return Vec::new();
}
let mut quotes = self
.execution_quotes_index
.iter()
.filter(|((_, quote_symbol), _)| quote_symbol == symbol)
.flat_map(|(_, rows)| rows.iter())
.execution_quotes_by_date
.values()
.filter_map(|rows_by_symbol| rows_by_symbol.get(symbol))
.flat_map(|rows| rows.iter())
.filter(|quote| intraday_quote_visible(quote, date, active_datetime, include_now))
.cloned()
.collect::<Vec<_>>();
@@ -1846,12 +1885,11 @@ impl DataSet {
.collect(),
Some("1m") | Some("tick") => {
let mut bars = self
.execution_quotes_index
.execution_quotes_by_date
.iter()
.filter(|((date, quote_symbol), _)| {
quote_symbol == symbol && *date >= start && *date <= end
})
.flat_map(|(_, rows)| rows.iter())
.filter(|(date, _)| **date >= start && **date <= end)
.filter_map(|(_, rows_by_symbol)| rows_by_symbol.get(symbol))
.flat_map(|rows| rows.iter())
.map(intraday_quote_price_bar)
.collect::<Vec<_>>();
bars.sort_by(|left, right| {
@@ -2269,6 +2307,25 @@ impl DataSet {
}
}
pub fn benchmark_decision_numeric_values(
&self,
date: NaiveDate,
field: &str,
lookback: usize,
) -> Vec<f64> {
let field = normalize_field(field);
match field.as_str() {
"open" | "day_open" | "dayopen" | "benchmark_open" => self
.benchmark_series_cache
.trailing_values_for(date, lookback, PriceField::Open),
_ => self.benchmark_series_cache.decision_values_for(
date,
lookback,
PriceField::Close,
),
}
}
pub fn market_open_moving_average(
&self,
date: NaiveDate,
@@ -3279,17 +3336,21 @@ fn build_futures_params_index(
fn build_execution_quote_index(
execution_quotes: Vec<IntradayExecutionQuote>,
) -> HashMap<(NaiveDate, String), Vec<IntradayExecutionQuote>> {
let mut grouped = HashMap::<(NaiveDate, String), Vec<IntradayExecutionQuote>>::new();
) -> HashMap<NaiveDate, HashMap<String, Vec<IntradayExecutionQuote>>> {
let mut grouped = HashMap::<NaiveDate, HashMap<String, Vec<IntradayExecutionQuote>>>::new();
for quote in execution_quotes {
grouped
.entry((quote.date, quote.symbol.clone()))
.entry(quote.date)
.or_default()
.entry(quote.symbol.clone())
.or_default()
.push(quote);
}
for quotes in grouped.values_mut() {
quotes.sort_by_key(|quote| quote.timestamp);
for rows_by_symbol in grouped.values_mut() {
for quotes in rows_by_symbol.values_mut() {
quotes.sort_by_key(|quote| quote.timestamp);
}
}
grouped