补充目标仓位降仓回归测试

This commit is contained in:
boris
2026-07-12 16:44:39 +08:00
parent bacb70e327
commit 5f5f0fcf16
+116
View File
@@ -7437,6 +7437,122 @@ mod tests {
assert_eq!(portfolio.position("000001.SZ").unwrap().quantity, 500);
}
#[test]
fn target_value_reduction_from_full_to_twenty_percent_sells_eighty_percent() {
let date = chrono::NaiveDate::from_ymd_opt(2025, 1, 2).expect("valid date");
let broker = BrokerSimulator::new_with_execution_price(
ChinaAShareCostModel::default(),
ChinaEquityRuleHooks,
PriceField::Open,
)
.with_matching_type(MatchingType::NextBarOpen)
.with_volume_limit(false)
.with_liquidity_limit(false)
.with_inactive_limit(false);
let mut snapshot = limit_test_snapshot();
snapshot.date = date;
snapshot.prev_close = 10.0;
snapshot.open = 10.0;
snapshot.close = 10.0;
let data = DataSet::from_components_with_actions_and_quotes(
vec![limit_test_instrument()],
vec![snapshot],
Vec::new(),
vec![limit_test_candidate(true, true)],
vec![limit_test_benchmark()],
Vec::new(),
Vec::new(),
)
.expect("valid dataset");
let mut portfolio = PortfolioState::new(100_000.0);
portfolio.position_mut("000001.SZ").buy(
date.pred_opt().expect("previous date"),
10_000,
10.0,
);
portfolio.apply_cash_delta(-100_000.0);
let mut report = BrokerExecutionReport::default();
broker
.process_target_value(
date,
&mut portfolio,
&data,
"000001.SZ",
20_000.0,
"target_value_20_percent",
&mut BTreeMap::new(),
&mut BTreeMap::new(),
&mut None,
&mut BTreeMap::new(),
&mut report,
)
.expect("target value reduction");
assert_eq!(report.fill_events.len(), 1);
assert_eq!(report.fill_events[0].side, OrderSide::Sell);
assert_eq!(report.fill_events[0].quantity, 8_000);
assert_eq!(portfolio.position("000001.SZ").unwrap().quantity, 2_000);
}
#[test]
fn target_percent_reduction_from_full_to_twenty_percent_sells_eighty_percent() {
let date = chrono::NaiveDate::from_ymd_opt(2025, 1, 2).expect("valid date");
let broker = BrokerSimulator::new_with_execution_price(
ChinaAShareCostModel::default(),
ChinaEquityRuleHooks,
PriceField::Open,
)
.with_matching_type(MatchingType::NextBarOpen)
.with_volume_limit(false)
.with_liquidity_limit(false)
.with_inactive_limit(false);
let mut snapshot = limit_test_snapshot();
snapshot.date = date;
snapshot.prev_close = 10.0;
snapshot.open = 10.0;
snapshot.close = 10.0;
let data = DataSet::from_components_with_actions_and_quotes(
vec![limit_test_instrument()],
vec![snapshot],
Vec::new(),
vec![limit_test_candidate(true, true)],
vec![limit_test_benchmark()],
Vec::new(),
Vec::new(),
)
.expect("valid dataset");
let mut portfolio = PortfolioState::new(100_000.0);
portfolio.position_mut("000001.SZ").buy(
date.pred_opt().expect("previous date"),
10_000,
10.0,
);
portfolio.apply_cash_delta(-100_000.0);
let mut report = BrokerExecutionReport::default();
broker
.process_target_percent(
date,
&mut portfolio,
&data,
"000001.SZ",
0.2,
"target_percent_20_percent",
&mut BTreeMap::new(),
&mut BTreeMap::new(),
&mut None,
&mut BTreeMap::new(),
&mut report,
)
.expect("target percent reduction");
assert_eq!(report.fill_events.len(), 1);
assert_eq!(report.fill_events[0].side, OrderSide::Sell);
assert_eq!(report.fill_events[0].quantity, 8_000);
assert_eq!(portfolio.position("000001.SZ").unwrap().quantity, 2_000);
}
#[test]
fn target_portfolio_smart_ignores_zero_weight_symbols_without_market_snapshot() {
let date = chrono::NaiveDate::from_ymd_opt(2025, 1, 2).expect("valid date");