Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -628,6 +628,37 @@ fn parsed_pool_program_executes_daily_membership_changes_without_legacy_translat
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pool_position_adjustments_use_execution_clock_and_restore_original_twenty_percent() {
|
||||
for timed in [false,true] {
|
||||
let program=StockPoolProgram { schema_version:1,pool_id:"position-clock".into(),version_id:"v1".into(),
|
||||
members:contract(day(2),1,false).members,exit_signals:vec![],
|
||||
allocation_policy:serde_json::json!({"target_holding_count":1,"invest_ratio_bps":2000,"portfolio_policy":{"schema_version":1,"membership":"follow_candidates","rebalance_weights":true}}),
|
||||
timing_policy:serde_json::json!({"auto_execute":true,"pricing_mode":"first_tick"}),stop_take_policy:serde_json::json!({}),out_of_pool_policy:"hold".into() };
|
||||
let risk=if timed {serde_json::json!({"positionExposureEvents":[
|
||||
{"eventId":"zero","sequence":1,"effectiveAt":"2026-01-05T09:30:00+08:00","action":"set","targetExposureBps":0},
|
||||
{"eventId":"restore","sequence":2,"effectiveAt":"2026-01-06T09:30:00+08:00","action":"restore"}
|
||||
]})}else{serde_json::json!({"positionExposureSchedule":[{"effectiveDate":"2026-01-05","targetExposureBps":1000}]})};
|
||||
let mut config=platform_expr_config_from_value("position-clock",&code(1),&serde_json::json!({
|
||||
"stockPool":program,"signalSymbol":code(1),"benchmark":{"instrumentId":"000300.SH"},"universe":{"include":[code(1),code(2)]},
|
||||
"runtimeExpressions":{"risk":risk}
|
||||
})).unwrap();
|
||||
config.market_cap_field="close".into();config.market_cap_lower_expr="0".into();config.market_cap_upper_expr="1.0e12".into();
|
||||
config.stock_filter_expr="true".into();config.selection_limit_expr="1".into();config.selection_candidate_limit_expr="2".into();config.rank_expr="0".into();
|
||||
config.matching_type=MatchingType::NextBarOpen;
|
||||
let result=BacktestEngine::new(data(false),PlatformExprStrategy::new(config),broker(false),BacktestConfig {
|
||||
// The raw engine retains its first signal day as a cash baseline;
|
||||
// Jan 2's signal executes Jan 5, across the fixture weekend.
|
||||
initial_cash:30000.,benchmark_code:"000300.SH".into(),start_date:Some(day(2)),end_date:Some(day(6)),
|
||||
decision_lag_trading_days:1,execution_price_field:PriceField::Open,
|
||||
}).run().unwrap();
|
||||
assert_eq!(result.fills.len(),1,"timed={timed}, fills={:?}",result.fills);
|
||||
assert_eq!(result.fills[0].symbol,code(1));
|
||||
assert_eq!(result.fills[0].quantity,if timed {300}else{100});
|
||||
assert_eq!(result.fills[0].date,if timed {day(6)}else{day(5)});
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parsed_typed_exit_program_keeps_ordinary_gates_and_independent_risk_targets_separate() {
|
||||
for (ordinary, risk, quote, sold) in [
|
||||
|
||||
Reference in New Issue
Block a user