按实际委托时间选择盘后撮合阶段
This commit is contained in:
@@ -638,15 +638,23 @@ where
|
||||
if self.execution_quote_loader.is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
let post_close_window = self
|
||||
.broker
|
||||
.post_close_execution_quote_window(execution_date);
|
||||
if self.broker.execution_price_field() != PriceField::Last
|
||||
&& !decision_has_algo_execution(decision)
|
||||
&& post_close_window.is_none()
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let caller_start_time = start_time;
|
||||
let caller_end_time = end_time;
|
||||
let start_time = caller_start_time.or_else(|| self.broker.intraday_execution_start_time());
|
||||
let start_time = post_close_window
|
||||
.map(|window| window.0)
|
||||
.or(caller_start_time)
|
||||
.or_else(|| self.broker.intraday_execution_start_time());
|
||||
let end_time = post_close_window.map(|window| window.1).or(caller_end_time);
|
||||
let mut symbols = execution_quote_symbols_for_decision(decision, portfolio, open_orders);
|
||||
self.load_missing_execution_quotes(execution_date, start_time, end_time, &mut symbols)?;
|
||||
|
||||
@@ -4614,15 +4622,16 @@ mod tests {
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use chrono::NaiveDate;
|
||||
use chrono::{NaiveDate, NaiveTime};
|
||||
|
||||
use super::{BacktestConfig, BacktestEngine};
|
||||
use crate::broker::{BrokerSimulator, MatchingType};
|
||||
use crate::broker::{BrokerSimulator, MatchingType, SlippageModel};
|
||||
use crate::cost::ChinaAShareCostModel;
|
||||
use crate::data::{
|
||||
BenchmarkSnapshot, CandidateEligibility, DailyFactorSnapshot, DailyMarketSnapshot, DataSet,
|
||||
PriceField,
|
||||
IntradayExecutionQuote, PriceField,
|
||||
};
|
||||
use crate::events::{OrderSide, OrderStatus};
|
||||
use crate::instrument::Instrument;
|
||||
@@ -5302,6 +5311,85 @@ mod tests {
|
||||
.expect("backtest run")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn current_close_order_at_1500_loads_and_uses_post_close_matching_window() {
|
||||
let date = d(2026, 7, 6);
|
||||
let data = dataset_from_market_and_candidates(
|
||||
vec![market(date, 9.5, 10.0)],
|
||||
vec![candidate(date)],
|
||||
);
|
||||
let broker = BrokerSimulator::new_with_execution_price(
|
||||
ChinaAShareCostModel::default(),
|
||||
ChinaEquityRuleHooks,
|
||||
PriceField::Close,
|
||||
)
|
||||
.with_matching_type(MatchingType::CurrentBarClose)
|
||||
.with_intraday_execution_start_time(
|
||||
NaiveTime::from_hms_opt(15, 0, 0).expect("valid submission time"),
|
||||
)
|
||||
.with_slippage_model(SlippageModel::PriceRatio(0.25))
|
||||
.with_volume_limit(false)
|
||||
.with_liquidity_limit(false)
|
||||
.with_inactive_limit(false);
|
||||
let config = BacktestConfig {
|
||||
initial_cash: 100_000.0,
|
||||
benchmark_code: "000852.SH".to_string(),
|
||||
start_date: Some(date),
|
||||
end_date: Some(date),
|
||||
decision_lag_trading_days: 0,
|
||||
execution_price_field: PriceField::Close,
|
||||
};
|
||||
let requests = Arc::new(Mutex::new(Vec::new()));
|
||||
let captured = Arc::clone(&requests);
|
||||
let mut engine = BacktestEngine::new(
|
||||
data,
|
||||
BuyWhenDecisionDateStrategy {
|
||||
decision_date: date,
|
||||
},
|
||||
broker,
|
||||
config,
|
||||
)
|
||||
.with_execution_quote_loader(move |request| {
|
||||
captured
|
||||
.lock()
|
||||
.expect("request capture lock")
|
||||
.push((request.start_time, request.end_time));
|
||||
Ok(request
|
||||
.symbols
|
||||
.into_iter()
|
||||
.map(|symbol| IntradayExecutionQuote {
|
||||
date: request.date,
|
||||
symbol,
|
||||
timestamp: request.date.and_hms_opt(15, 5, 0).expect("valid timestamp"),
|
||||
last_price: 12.0,
|
||||
bid1: 11.99,
|
||||
ask1: 12.01,
|
||||
bid1_volume: 10_000,
|
||||
ask1_volume: 10_000,
|
||||
volume_delta: 10_000,
|
||||
amount_delta: 120_000.0,
|
||||
trading_phase: Some("post_close_fixed_price".to_string()),
|
||||
})
|
||||
.collect())
|
||||
});
|
||||
|
||||
let result = engine.run().expect("post-close backtest run");
|
||||
|
||||
assert_eq!(
|
||||
requests.lock().expect("request capture lock").as_slice(),
|
||||
&[(
|
||||
NaiveTime::from_hms_opt(15, 5, 0),
|
||||
NaiveTime::from_hms_opt(15, 30, 0),
|
||||
)]
|
||||
);
|
||||
assert_eq!(result.fills.len(), 1, "{result:?}");
|
||||
assert_eq!(result.fills[0].price, 10.0);
|
||||
assert_eq!(
|
||||
result.fills[0].execution_timestamp,
|
||||
date.and_hms_opt(15, 5, 0)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compact_progress_keeps_counts_without_event_payload_clones() {
|
||||
let mut engine = engine_with_matching(MatchingType::CurrentBarClose, PriceField::Close, 0);
|
||||
|
||||
Reference in New Issue
Block a user