优化分钟历史窗口读取并移除滚动双口径
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
use std::hint::black_box;
|
||||
use std::time::Instant;
|
||||
|
||||
use chrono::{Duration, NaiveDate, NaiveDateTime, NaiveTime};
|
||||
use fidc_core::{
|
||||
BenchmarkSnapshot, DailyMarketSnapshot, DataSet, Instrument, IntradayExecutionQuote,
|
||||
};
|
||||
|
||||
const SYMBOL: &str = "000001.SZ";
|
||||
|
||||
fn dataset(day_count: usize, bars_per_day: usize) -> (DataSet, Vec<NaiveDate>) {
|
||||
let start = NaiveDate::from_ymd_opt(2025, 1, 1).expect("valid start date");
|
||||
let dates = (0..day_count)
|
||||
.map(|offset| start + Duration::days(offset as i64))
|
||||
.collect::<Vec<_>>();
|
||||
let markets = dates
|
||||
.iter()
|
||||
.map(|date| DailyMarketSnapshot {
|
||||
date: *date,
|
||||
symbol: SYMBOL.to_string(),
|
||||
timestamp: None,
|
||||
day_open: 10.0,
|
||||
open: 10.0,
|
||||
high: 10.5,
|
||||
low: 9.5,
|
||||
close: 10.0,
|
||||
last_price: 10.0,
|
||||
bid1: 9.99,
|
||||
ask1: 10.01,
|
||||
prev_close: 10.0,
|
||||
volume: 1_000_000,
|
||||
minute_volume: 1_000,
|
||||
bid1_volume: 10_000,
|
||||
ask1_volume: 10_000,
|
||||
trading_phase: Some("continuous".to_string()),
|
||||
paused: false,
|
||||
upper_limit: 11.0,
|
||||
lower_limit: 9.0,
|
||||
price_tick: 0.01,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let benchmarks = dates
|
||||
.iter()
|
||||
.map(|date| BenchmarkSnapshot {
|
||||
date: *date,
|
||||
benchmark: "000852.SH".to_string(),
|
||||
open: 1_000.0,
|
||||
close: 1_000.0,
|
||||
prev_close: 1_000.0,
|
||||
volume: 10_000_000,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let mut quotes = Vec::with_capacity(day_count * bars_per_day);
|
||||
for date in &dates {
|
||||
let session_start = date
|
||||
.and_hms_opt(9, 30, 0)
|
||||
.expect("valid session start");
|
||||
for offset in 0..bars_per_day {
|
||||
let timestamp = session_start + Duration::minutes(offset as i64);
|
||||
quotes.push(IntradayExecutionQuote {
|
||||
date: *date,
|
||||
symbol: SYMBOL.to_string(),
|
||||
timestamp,
|
||||
last_price: 10.0 + offset as f64 / 10_000.0,
|
||||
bid1: 9.99,
|
||||
ask1: 10.01,
|
||||
bid1_volume: 10_000,
|
||||
ask1_volume: 10_000,
|
||||
volume_delta: 1_000,
|
||||
amount_delta: 10_000.0,
|
||||
trading_phase: Some("continuous".to_string()),
|
||||
});
|
||||
}
|
||||
}
|
||||
let data = DataSet::from_components_with_actions_and_quotes(
|
||||
vec![Instrument {
|
||||
symbol: SYMBOL.to_string(),
|
||||
name: "平安银行".to_string(),
|
||||
board: "SZ".to_string(),
|
||||
round_lot: 100,
|
||||
listed_at: Some(start - Duration::days(1_000)),
|
||||
delisted_at: None,
|
||||
status: "active".to_string(),
|
||||
}],
|
||||
markets,
|
||||
Vec::new(),
|
||||
Vec::new(),
|
||||
benchmarks,
|
||||
Vec::new(),
|
||||
quotes,
|
||||
)
|
||||
.expect("build intraday history dataset");
|
||||
(data, dates)
|
||||
}
|
||||
|
||||
fn timestamp(date: NaiveDate, time: &str) -> NaiveDateTime {
|
||||
let time = NaiveTime::parse_from_str(time, "%H:%M:%S").expect("valid time");
|
||||
date.and_time(time)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn intraday_history_is_bounded_by_visibility_and_preserves_order() {
|
||||
let (data, dates) = dataset(3, 4);
|
||||
let rows = data.history_intraday_quotes_at(
|
||||
dates[1],
|
||||
Some(timestamp(dates[1], "09:32:00")),
|
||||
SYMBOL,
|
||||
3,
|
||||
false,
|
||||
);
|
||||
assert_eq!(
|
||||
rows.iter().map(|row| row.timestamp).collect::<Vec<_>>(),
|
||||
vec![
|
||||
timestamp(dates[0], "09:33:00"),
|
||||
timestamp(dates[1], "09:30:00"),
|
||||
timestamp(dates[1], "09:31:00"),
|
||||
]
|
||||
);
|
||||
|
||||
let including_now = data.history_intraday_quotes_at(
|
||||
dates[1],
|
||||
Some(timestamp(dates[1], "09:32:00")),
|
||||
SYMBOL,
|
||||
3,
|
||||
true,
|
||||
);
|
||||
assert_eq!(
|
||||
including_now
|
||||
.iter()
|
||||
.map(|row| row.timestamp)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![
|
||||
timestamp(dates[1], "09:30:00"),
|
||||
timestamp(dates[1], "09:31:00"),
|
||||
timestamp(dates[1], "09:32:00"),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "manual release-mode intraday history benchmark"]
|
||||
fn benchmark_bounded_intraday_history() {
|
||||
let (data, dates) = dataset(250, 240);
|
||||
let active_datetime = timestamp(*dates.last().expect("last date"), "13:29:00");
|
||||
|
||||
for _ in 0..5 {
|
||||
black_box(data.history_intraday_quotes_at(
|
||||
active_datetime.date(),
|
||||
Some(active_datetime),
|
||||
SYMBOL,
|
||||
30,
|
||||
true,
|
||||
));
|
||||
}
|
||||
|
||||
let started = Instant::now();
|
||||
let mut checksum = 0_i64;
|
||||
for _ in 0..200 {
|
||||
let rows = data.history_intraday_quotes_at(
|
||||
active_datetime.date(),
|
||||
Some(active_datetime),
|
||||
SYMBOL,
|
||||
30,
|
||||
true,
|
||||
);
|
||||
checksum += rows
|
||||
.last()
|
||||
.expect("history row")
|
||||
.timestamp
|
||||
.and_utc()
|
||||
.timestamp();
|
||||
black_box(&rows);
|
||||
}
|
||||
let elapsed = started.elapsed();
|
||||
eprintln!(
|
||||
"intraday_history_benchmark iterations=200 rows_per_dataset=60000 elapsed_seconds={:.6} checksum={checksum}",
|
||||
elapsed.as_secs_f64(),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user