test: cover next-open side flips and trim redundant default fields

This commit is contained in:
boris
2026-09-08 00:43:54 +08:00
parent da27204a71
commit 7d293f092e
5 changed files with 38 additions and 48 deletions
+38 -9
View File
@@ -8218,7 +8218,6 @@ mod tests {
fn next_open_buy_decision() -> StrategyDecision {
StrategyDecision {
buy_denials: Default::default(),
order_intents: vec![OrderIntent::Shares {
symbol: "000001.SZ".to_string(),
quantity: 100,
@@ -8277,9 +8276,46 @@ mod tests {
assert!(broker.runtime_buy_denials.borrow().is_empty());
}
#[test]
fn decision_buy_denial_uses_actual_next_open_target_delta() {
let first = chrono::NaiveDate::from_ymd_opt(2025, 1, 2).unwrap();
let second = chrono::NaiveDate::from_ymd_opt(2025, 1, 3).unwrap();
let mut next = dated_limit_test_snapshot(second);
next.day_open = 9.5;
next.open = 9.5;
next.close = 9.5;
next.last_price = 9.5;
next.bid1 = 9.5;
next.ask1 = 9.5;
let data = DataSet::from_components(vec![limit_test_instrument()],
vec![dated_limit_test_snapshot(first), next], Vec::new(),
vec![dated_limit_test_candidate(first, false, false, true, true),
dated_limit_test_candidate(second, false, false, true, true)],
vec![dated_limit_test_benchmark(first), dated_limit_test_benchmark(second)]).unwrap();
let broker = BrokerSimulator::new(ChinaAShareCostModel::default(), ChinaEquityRuleHooks)
.with_matching_type(MatchingType::NextBarOpen);
let mut portfolio = PortfolioState::new(1_000_000.0);
let mut initial = StrategyDecision::default();
initial.order_intents.push(OrderIntent::Shares {
symbol: "000001.SZ".to_string(), quantity: 10_000, reason: "initial".to_string(),
});
broker.execute(first, &mut portfolio, &data, &initial).unwrap();
let mut decision = StrategyDecision::default();
decision.buy_denials.insert("000001.SZ".to_string(), "strategy_buy_condition_false".to_string());
// Below the signal-day holding value, but above next-open value.
decision.order_intents.push(OrderIntent::TargetValue {
symbol: "000001.SZ".to_string(), target_value: 97_500.0, reason: "target".to_string(),
});
let report = broker.execute_with_event_dates_and_decision_equity(
second, first, first, None, &mut portfolio, &data, &decision).unwrap();
assert!(report.fill_events.is_empty());
assert_eq!(portfolio.position("000001.SZ").unwrap().quantity, 10_000);
assert!(report.order_events.iter().any(|event| event.side == OrderSide::Buy));
assert!(broker.runtime_buy_denials.borrow().is_empty());
}
fn next_open_sell_decision() -> StrategyDecision {
StrategyDecision {
buy_denials: Default::default(),
order_intents: vec![OrderIntent::Shares {
symbol: "000001.SZ".to_string(),
quantity: -100,
@@ -10257,7 +10293,6 @@ mod tests {
)
.expect("valid dataset");
let decision = StrategyDecision {
buy_denials: Default::default(),
order_intents: vec![
OrderIntent::TargetValue {
symbol: "000002.SZ".to_string(),
@@ -10370,7 +10405,6 @@ mod tests {
.position_mut("000002.SZ")
.buy(prev_date, 1_000, 10.0);
let decision = StrategyDecision {
buy_denials: Default::default(),
order_intents: vec![
OrderIntent::TargetValue {
symbol: "000001.SZ".to_string(),
@@ -10438,7 +10472,6 @@ mod tests {
.position_mut("000002.SZ")
.buy(prev_date, 1_000, 10.0);
let decision = StrategyDecision {
buy_denials: Default::default(),
order_intents: vec![
OrderIntent::TargetValue {
symbol: "000003.SZ".to_string(),
@@ -10499,7 +10532,6 @@ mod tests {
.position_mut("000001.SZ")
.buy(prev_date, 1_000, 10.0);
let decision = StrategyDecision {
buy_denials: Default::default(),
order_intents: vec![OrderIntent::TargetValue {
symbol: "000003.SZ".to_string(),
target_value: 9_000.0,
@@ -10664,7 +10696,6 @@ mod tests {
.with_inactive_limit(false);
let mut portfolio = PortfolioState::new(20_000.0);
let decision = StrategyDecision {
buy_denials: Default::default(),
order_intents: vec![OrderIntent::TargetValue {
symbol: "000001.SZ".to_string(),
target_value: 10_000.0,
@@ -10706,7 +10737,6 @@ mod tests {
.with_inactive_limit(false);
let mut portfolio = PortfolioState::new(20_000.0);
let decision = StrategyDecision {
buy_denials: Default::default(),
order_intents: vec![OrderIntent::TargetValue {
symbol: "000001.SZ".to_string(),
target_value: 10_000.0,
@@ -10744,7 +10774,6 @@ mod tests {
.with_inactive_limit(false);
let mut portfolio = PortfolioState::new(20_000.0);
let decision = StrategyDecision {
buy_denials: Default::default(),
order_intents: vec![OrderIntent::TargetPortfolioSmart {
target_weights: BTreeMap::from([("000001.SZ".to_string(), 0.5)]),
order_prices: None,
-12
View File
@@ -4856,7 +4856,6 @@ mod tests {
) -> Result<StrategyDecision, super::BacktestError> {
if ctx.decision_date == self.decision_date && ctx.portfolio.position(SYMBOL).is_none() {
return Ok(StrategyDecision {
buy_denials: Default::default(),
order_intents: vec![OrderIntent::Shares {
symbol: SYMBOL.to_string(),
quantity: 100,
@@ -4897,7 +4896,6 @@ mod tests {
);
if ctx.portfolio.position(SYMBOL).is_none() {
return Ok(StrategyDecision {
buy_denials: Default::default(),
order_intents: vec![OrderIntent::Shares {
symbol: SYMBOL.to_string(),
quantity: 100,
@@ -4957,7 +4955,6 @@ mod tests {
rule: &ScheduleRule,
) -> Result<StrategyDecision, super::BacktestError> {
Ok(StrategyDecision {
buy_denials: Default::default(),
order_intents: vec![OrderIntent::Shares {
symbol: SYMBOL.to_string(),
quantity: 100,
@@ -4995,7 +4992,6 @@ mod tests {
return Ok(StrategyDecision::default());
}
Ok(StrategyDecision {
buy_denials: Default::default(),
order_intents: vec![OrderIntent::Shares {
symbol: SYMBOL.to_string(),
quantity: 100,
@@ -5032,7 +5028,6 @@ mod tests {
return Ok(StrategyDecision::default());
}
Ok(StrategyDecision {
buy_denials: Default::default(),
order_intents: vec![OrderIntent::TargetPortfolioSmart {
target_weights: self.target_weights.clone(),
order_prices: None,
@@ -5075,7 +5070,6 @@ mod tests {
Vec::new()
};
Ok(StrategyDecision {
buy_denials: Default::default(),
order_intents,
..StrategyDecision::default()
})
@@ -5112,7 +5106,6 @@ mod tests {
return Ok(StrategyDecision::default());
};
Ok(StrategyDecision {
buy_denials: Default::default(),
order_intents: vec![OrderIntent::Shares {
symbol,
quantity: 100,
@@ -5173,7 +5166,6 @@ mod tests {
.unwrap_or_default()
));
Ok(StrategyDecision {
buy_denials: Default::default(),
order_intents: vec![OrderIntent::Shares {
symbol: SYMBOL.to_string(),
quantity: 100,
@@ -5214,7 +5206,6 @@ mod tests {
&& ctx.portfolio.position(SYMBOL).is_none()
{
return Ok(StrategyDecision {
buy_denials: Default::default(),
order_intents: vec![OrderIntent::Shares {
symbol: SYMBOL.to_string(),
quantity: 100,
@@ -5226,7 +5217,6 @@ mod tests {
if ctx.decision_date == self.sell_decision_date {
if let Some(position) = ctx.portfolio.position(SYMBOL) {
return Ok(StrategyDecision {
buy_denials: Default::default(),
order_intents: vec![OrderIntent::Shares {
symbol: SYMBOL.to_string(),
quantity: -(position.quantity as i32),
@@ -5266,7 +5256,6 @@ mod tests {
&& ctx.portfolio.position(SYMBOL).is_none()
{
return Ok(StrategyDecision {
buy_denials: Default::default(),
order_intents: vec![OrderIntent::Shares {
symbol: SYMBOL.to_string(),
quantity: 100,
@@ -5278,7 +5267,6 @@ mod tests {
if ctx.decision_date == self.rebuy_decision_date {
if let Some(position) = ctx.portfolio.position(SYMBOL) {
return Ok(StrategyDecision {
buy_denials: Default::default(),
order_intents: vec![
OrderIntent::Shares {
symbol: SYMBOL.to_string(),