切换回测执行价为分钟线语义

This commit is contained in:
boris
2026-06-26 13:27:49 +08:00
parent 6db480b91d
commit 7f40cfdab0
14 changed files with 129 additions and 132 deletions
@@ -190,7 +190,7 @@ fn engine_preloads_declared_decision_quotes_for_current_positions() {
ChinaEquityRuleHooks,
PriceField::Last,
)
.with_matching_type(MatchingType::NextTickLast)
.with_matching_type(MatchingType::NextMinuteLast)
.with_intraday_execution_start_time(t(10, 40, 0));
let config = BacktestConfig {
initial_cash: 10_000.0,
@@ -385,7 +385,7 @@ fn engine_reuses_preloaded_decision_quotes_without_loader_call() {
ChinaEquityRuleHooks,
PriceField::Last,
)
.with_matching_type(MatchingType::NextTickLast)
.with_matching_type(MatchingType::NextMinuteLast)
.with_intraday_execution_start_time(t(10, 40, 0));
let config = BacktestConfig {
initial_cash: 10_000.0,
@@ -587,7 +587,7 @@ fn engine_loads_distinct_decision_quote_times_on_same_day() {
ChinaEquityRuleHooks,
PriceField::Last,
)
.with_matching_type(MatchingType::NextTickLast)
.with_matching_type(MatchingType::NextMinuteLast)
.with_intraday_execution_start_time(t(10, 40, 0));
let config = BacktestConfig {
initial_cash: 10_000.0,
+16 -16
View File
@@ -799,7 +799,7 @@ impl Strategy for UniverseDirectiveStrategy {
impl Strategy for TickProbeStrategy {
fn name(&self) -> &str {
"tick-probe"
"minute-probe"
}
fn on_day(
@@ -812,26 +812,26 @@ impl Strategy for TickProbeStrategy {
exit_symbols: BTreeSet::new(),
order_intents: vec![OrderIntent::Subscribe {
symbols: BTreeSet::from(["000001.SZ".to_string()]),
reason: "subscribe_tick_probe".to_string(),
reason: "subscribe_minute_probe".to_string(),
}],
notes: Vec::new(),
diagnostics: Vec::new(),
})
}
fn on_tick(
fn on_minute(
&mut self,
ctx: &StrategyContext<'_>,
quote: &IntradayExecutionQuote,
) -> Result<StrategyDecision, fidc_core::BacktestError> {
let visible_last = ctx
.history_bars(&quote.symbol, 9, "tick", "last", true)
.history_bars(&quote.symbol, 9, "1m", "last", true)
.iter()
.map(|value| format!("{value:.2}"))
.collect::<Vec<_>>()
.join(",");
let previous_last = ctx
.history_bars(&quote.symbol, 9, "tick", "last", false)
.history_bars(&quote.symbol, 9, "1m", "last", false)
.iter()
.map(|value| format!("{value:.2}"))
.collect::<Vec<_>>()
@@ -853,7 +853,7 @@ impl Strategy for TickProbeStrategy {
order_intents: vec![OrderIntent::Shares {
symbol: quote.symbol.clone(),
quantity: 100,
reason: "tick_buy".to_string(),
reason: "minute_buy".to_string(),
}],
notes: Vec::new(),
diagnostics: Vec::new(),
@@ -919,10 +919,10 @@ impl Strategy for DataApiProbeStrategy {
let daily_price_count = ctx
.get_price("000001.SZ", d(2025, 1, 3), ctx.execution_date, "1d")
.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
let minute_price_bars =
ctx.get_price("000001.SZ", d(2025, 1, 3), ctx.execution_date, "1m");
let minute_price_count = minute_price_bars.len();
let minute_frequency = minute_price_bars
.first()
.map(|bar| bar.frequency.clone())
.unwrap_or_default();
@@ -930,7 +930,7 @@ impl Strategy for DataApiProbeStrategy {
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};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}",
"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={minute_price_count};minute_frequency={minute_frequency}",
ctx.all_instruments().len()
));
}
@@ -2046,25 +2046,25 @@ fn engine_runs_subscribed_tick_hooks_and_executes_tick_orders() {
]
);
assert_eq!(result.fills.len(), 1);
assert_eq!(result.fills[0].reason, "tick_buy");
assert_eq!(result.fills[0].reason, "minute_buy");
assert_eq!(result.fills[0].quantity, 100);
assert!(
result
.process_events
.iter()
.any(|event| event.kind == ProcessEventKind::PreTick)
.any(|event| event.kind == ProcessEventKind::PreMinute)
);
assert!(
result
.process_events
.iter()
.any(|event| event.kind == ProcessEventKind::Tick)
.any(|event| event.kind == ProcessEventKind::Minute)
);
assert!(
result
.process_events
.iter()
.any(|event| event.kind == ProcessEventKind::PostTick)
.any(|event| event.kind == ProcessEventKind::PostMinute)
);
}
@@ -2253,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;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"
"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;minute_frequency=1m"
]
);
}
@@ -349,7 +349,7 @@ fn broker_delayed_limit_open_sell_uses_tick_price() {
ChinaEquityRuleHooks::default(),
PriceField::Last,
)
.with_matching_type(MatchingType::NextTickLast)
.with_matching_type(MatchingType::NextMinuteLast)
.with_intraday_execution_start_time(NaiveTime::from_hms_opt(9, 31, 0).unwrap())
.with_volume_limit(false)
.with_liquidity_limit(false);
@@ -1809,7 +1809,7 @@ fn broker_rejects_intraday_last_order_without_execution_quotes() {
order_intents: vec![OrderIntent::Value {
symbol: "000002.SZ".to_string(),
value: 100_000.0,
reason: "missing_tick_quotes".to_string(),
reason: "missing_minute_quotes".to_string(),
}],
notes: Vec::new(),
diagnostics: Vec::new(),
@@ -2173,7 +2173,7 @@ fn broker_cancels_market_buy_when_tick_has_no_volume() {
report.order_events[0].status,
fidc_core::OrderStatus::Canceled
);
assert!(report.order_events[0].reason.contains("tick no volume"));
assert!(report.order_events[0].reason.contains("minute no volume"));
}
#[test]
@@ -2888,7 +2888,7 @@ fn broker_uses_best_own_price_for_intraday_matching() {
ChinaEquityRuleHooks::default(),
PriceField::Last,
)
.with_matching_type(MatchingType::NextTickBestOwn);
.with_matching_type(MatchingType::NextMinuteBestOwn);
let report = broker
.execute(
@@ -3003,7 +3003,7 @@ fn broker_uses_best_counterparty_price_for_intraday_matching() {
ChinaEquityRuleHooks::default(),
PriceField::Last,
)
.with_matching_type(MatchingType::NextTickBestCounterparty);
.with_matching_type(MatchingType::NextMinuteBestCounterparty);
let report = broker
.execute(