test(engine): reproduce account-sensitive quote scope bypass
This commit is contained in:
@@ -162,6 +162,45 @@ fn single_day_quote_plan_data(date: NaiveDate) -> DataSet {
|
|||||||
.expect("dataset")
|
.expect("dataset")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn runtime_account_dependent_quote_scope_cannot_be_replaced_by_an_empty_preplan() {
|
||||||
|
struct AccountDependentQuoteReader;
|
||||||
|
impl Strategy for AccountDependentQuoteReader {
|
||||||
|
fn name(&self) -> &str { "account_dependent_quote_reader" }
|
||||||
|
fn decision_quote_times(&self) -> Vec<NaiveTime> { vec![t(10, 18, 0)] }
|
||||||
|
fn decision_quote_symbols(&mut self, ctx: &StrategyContext<'_>) -> Result<BTreeSet<String>, fidc_core::BacktestError> {
|
||||||
|
Ok(if ctx.portfolio.cash() < 50_000.0 {
|
||||||
|
BTreeSet::from(["000001.SZ".into()])
|
||||||
|
} else { BTreeSet::new() })
|
||||||
|
}
|
||||||
|
fn on_day(&mut self, ctx: &StrategyContext<'_>) -> Result<StrategyDecision, fidc_core::BacktestError> {
|
||||||
|
assert!(ctx.data.execution_quotes_on(ctx.execution_date, "000001.SZ").iter().any(|quote|
|
||||||
|
quote.timestamp.time()==t(10,17,59) && quote.last_price==10.0),
|
||||||
|
"actual account-dependent quote scope must be loaded before decision");
|
||||||
|
Ok(StrategyDecision::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let date = d(2026, 1, 5);
|
||||||
|
let broker = BrokerSimulator::new_with_execution_price(
|
||||||
|
ChinaAShareCostModel::default(), ChinaEquityRuleHooks, PriceField::Close,
|
||||||
|
).with_volume_capacity_mode(fidc_core::execution_capacity::VolumeCapacityMode::SessionCapacityAudit)
|
||||||
|
.with_matching_type(MatchingType::CurrentBarClose);
|
||||||
|
let config = BacktestConfig {
|
||||||
|
initial_cash:10_000.0, benchmark_code:"000852.SH".into(),
|
||||||
|
start_date:Some(date), end_date:Some(date), decision_lag_trading_days:0,
|
||||||
|
execution_price_field:PriceField::Close,
|
||||||
|
};
|
||||||
|
let mut engine = BacktestEngine::new(single_day_quote_plan_data(date), AccountDependentQuoteReader, broker, config)
|
||||||
|
.with_execution_quote_loader(move |request| Ok(request.symbols.into_iter().map(|symbol| IntradayExecutionQuote {
|
||||||
|
observation_kind:Default::default(), date:request.date, symbol,
|
||||||
|
timestamp:request.date.and_time(t(10,17,59)), last_price:10.0,bid1:10.0,ask1:10.0,
|
||||||
|
bid1_volume:10_000,ask1_volume:10_000,volume_delta:10_000,amount_delta:100_000.0,
|
||||||
|
trading_phase:Some("continuous".into()),
|
||||||
|
}).collect()))
|
||||||
|
.with_preplanned_decision_quote_symbols_by_date(Arc::new(BTreeMap::new()));
|
||||||
|
engine.run().expect("account-dependent quote planning");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn engine_uses_preplanned_decision_symbols_without_recomputing_strategy_plan() {
|
fn engine_uses_preplanned_decision_symbols_without_recomputing_strategy_plan() {
|
||||||
let date = d(2026, 1, 5);
|
let date = d(2026, 1, 5);
|
||||||
|
|||||||
Reference in New Issue
Block a user