修复日内时钟提前记账并按原订单续执行算法单
This commit is contained in:
@@ -2748,6 +2748,7 @@ fn strategy_context_exposes_engine_native_account_runtime_view() {
|
||||
avg_price: 0.0,
|
||||
transaction_cost: 0.0,
|
||||
limit_price: 12.0,
|
||||
reserved_cash: None,
|
||||
reason: "pending_buy".to_string(),
|
||||
}];
|
||||
let subscriptions = BTreeSet::new();
|
||||
|
||||
@@ -887,6 +887,42 @@ fn historical_etf_late_signal_freezes_money_and_requantifies_at_next_official_op
|
||||
assert!(result.terminal_audit.is_clean());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deferred_etf_open_does_not_appear_in_a_pre_open_minute_callback() {
|
||||
use fidc_core::strategy::{Strategy,StrategyContext};
|
||||
use std::{cell::RefCell,rc::Rc};
|
||||
struct ObservedPool { inner:EtfPoolSignal, observations:Rc<RefCell<Vec<(chrono::NaiveDateTime,u32,usize)>>> }
|
||||
impl Strategy for ObservedPool {
|
||||
fn name(&self)->&str {"ETF actual opening clock"}
|
||||
fn initial_subscriptions(&self)->BTreeSet<String> {BTreeSet::from([code(1)])}
|
||||
fn decision_quote_times(&self)->Vec<chrono::NaiveTime> {self.inner.decision_quote_times()}
|
||||
fn decision_quote_symbols(&mut self,ctx:&StrategyContext<'_>)->Result<BTreeSet<String>,fidc_core::BacktestError> {self.inner.decision_quote_symbols(ctx)}
|
||||
fn on_day(&mut self,ctx:&StrategyContext<'_>)->Result<StrategyDecision,fidc_core::BacktestError> {self.inner.on_day(ctx)}
|
||||
fn on_minute(&mut self,ctx:&StrategyContext<'_>,quote:&IntradayExecutionQuote)->Result<StrategyDecision,fidc_core::BacktestError> {
|
||||
if quote.date==day(5) {self.observations.borrow_mut().push((quote.timestamp,
|
||||
ctx.portfolio.position(&code(2)).map_or(0,|position|position.quantity),ctx.fills.iter().filter(|fill|fill.symbol==code(2)).count()));}
|
||||
Ok(StrategyDecision::default())
|
||||
}
|
||||
}
|
||||
let time=chrono::NaiveTime::from_hms_opt(13,0,0).unwrap();
|
||||
let mut data=etf_fallback_fixture(time);
|
||||
let quote=data.execution_quotes_on(day(5),&code(1))[0].clone();
|
||||
data.add_execution_quotes([(9,15),(9,31)].into_iter().map(|(hour,minute)| {
|
||||
let mut row=quote.clone();row.timestamp=day(5).and_hms_opt(hour,minute,0).unwrap();row
|
||||
}).collect());
|
||||
let observations=Rc::new(RefCell::new(Vec::new()));
|
||||
let broker=broker(false).with_matching_type(MatchingType::MinuteLast)
|
||||
.with_execution_price_field(PriceField::Last).with_intraday_execution_start_time(time)
|
||||
.with_historical_etf_open_fallback(true);
|
||||
let result=BacktestEngine::new(data,ObservedPool {inner:EtfPoolSignal{at:time,condition:String::new()},observations:observations.clone()},broker,BacktestConfig {
|
||||
initial_cash:30000.,benchmark_code:"000300.SH".into(),start_date:Some(day(2)),end_date:Some(day(5)),decision_lag_trading_days:0,execution_price_field:PriceField::Last,
|
||||
}).with_execution_quote_loader(|_|Ok(vec![])).run().unwrap();
|
||||
let observations=observations.borrow();
|
||||
assert_eq!(observations[0],(day(5).and_hms_opt(9,15,0).unwrap(),0,0));
|
||||
assert_eq!(observations[1],(day(5).and_hms_opt(9,31,0).unwrap(),3700,1));
|
||||
assert_eq!(result.fills.iter().filter(|fill|fill.symbol==code(2)).count(),1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn historical_etf_pending_target_at_end_is_not_a_fake_order_or_fill() {
|
||||
let result=run_etf_fallback(chrono::NaiveTime::from_hms_opt(13,0,0).unwrap(),day(2),true,"",false,false).unwrap();
|
||||
|
||||
Reference in New Issue
Block a user