修复allow_sell执行风控开关
This commit is contained in:
@@ -2295,7 +2295,10 @@ where
|
|||||||
position: &crate::portfolio::Position,
|
position: &crate::portfolio::Position,
|
||||||
algo_request: Option<&AlgoExecutionRequest>,
|
algo_request: Option<&AlgoExecutionRequest>,
|
||||||
) -> RuleCheck {
|
) -> RuleCheck {
|
||||||
if !self.aiquant_execution_rules && !candidate.allow_sell {
|
if self.risk_config.static_rules.respect_allow_buy_sell
|
||||||
|
&& !self.aiquant_execution_rules
|
||||||
|
&& !candidate.allow_sell
|
||||||
|
{
|
||||||
return RuleCheck::reject("sell_disabled");
|
return RuleCheck::reject("sell_disabled");
|
||||||
}
|
}
|
||||||
let check_price = if self.aiquant_execution_rules {
|
let check_price = if self.aiquant_execution_rules {
|
||||||
|
|||||||
@@ -3911,10 +3911,13 @@ fn execution_risk_rule_code(reason: &str) -> Option<&'static str> {
|
|||||||
if normalized.contains("one_yuan") {
|
if normalized.contains("one_yuan") {
|
||||||
return Some("one_yuan");
|
return Some("one_yuan");
|
||||||
}
|
}
|
||||||
if normalized.contains("trade_disabled")
|
if normalized.contains("buy_disabled") || normalized.contains("allow_buy") {
|
||||||
|| normalized.contains("allow_buy")
|
return Some("buy_disabled");
|
||||||
|| normalized.contains("allow_sell")
|
}
|
||||||
{
|
if normalized.contains("sell_disabled") || normalized.contains("allow_sell") {
|
||||||
|
return Some("sell_disabled");
|
||||||
|
}
|
||||||
|
if normalized.contains("trade_disabled") {
|
||||||
return Some("trade_disabled");
|
return Some("trade_disabled");
|
||||||
}
|
}
|
||||||
if normalized.contains("no_volume") || normalized.contains("volume_limit") {
|
if normalized.contains("no_volume") || normalized.contains("volume_limit") {
|
||||||
@@ -4921,6 +4924,61 @@ mod tests {
|
|||||||
assert_round_trip_sell_canceled_with_reason(&result, "open at or below lower limit");
|
assert_round_trip_sell_canceled_with_reason(&result, "open at or below lower limit");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn next_bar_open_sell_respects_allow_sell_policy_on_execution_day() {
|
||||||
|
let first = d(2025, 1, 2);
|
||||||
|
let second = d(2025, 1, 3);
|
||||||
|
let third = d(2025, 1, 6);
|
||||||
|
let fourth = d(2025, 1, 7);
|
||||||
|
let dataset = dataset_from_market_and_candidates(
|
||||||
|
vec![
|
||||||
|
market(first, 10.0, 10.5),
|
||||||
|
market(second, 11.0, 11.5),
|
||||||
|
market(third, 12.0, 12.5),
|
||||||
|
market(fourth, 12.0, 12.2),
|
||||||
|
],
|
||||||
|
vec![
|
||||||
|
candidate(first),
|
||||||
|
candidate(second),
|
||||||
|
candidate(third),
|
||||||
|
candidate_with_sell_state(fourth, false, false),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
let rejected = run_scheduled_round_trip_next_open_with_dataset_and_broker(
|
||||||
|
dataset.clone(),
|
||||||
|
scheduled_next_open_broker(FidcRiskControlConfig::default()),
|
||||||
|
);
|
||||||
|
assert_round_trip_sell_canceled_with_reason(&rejected, "sell_disabled");
|
||||||
|
let execution_decision = rejected
|
||||||
|
.risk_decisions
|
||||||
|
.iter()
|
||||||
|
.find(|decision| {
|
||||||
|
decision.date == fourth
|
||||||
|
&& decision.symbol == SYMBOL
|
||||||
|
&& decision.scope == RiskCheckScope::Sell
|
||||||
|
&& decision.stage == "execution"
|
||||||
|
&& decision.rule_code == "sell_disabled"
|
||||||
|
&& !decision.accepted
|
||||||
|
})
|
||||||
|
.expect("execution-day sell_disabled rejection should be audited");
|
||||||
|
assert_eq!(execution_decision.order_id.as_deref(), Some("2"));
|
||||||
|
|
||||||
|
let mut relaxed_config = FidcRiskControlConfig::default();
|
||||||
|
relaxed_config.static_rules.respect_allow_buy_sell = false;
|
||||||
|
let relaxed = run_scheduled_round_trip_next_open_with_dataset_and_broker(
|
||||||
|
dataset,
|
||||||
|
scheduled_next_open_broker(relaxed_config),
|
||||||
|
);
|
||||||
|
let sell_fill = relaxed
|
||||||
|
.fills
|
||||||
|
.iter()
|
||||||
|
.find(|fill| fill.side == OrderSide::Sell)
|
||||||
|
.expect("sell should execute when allow_sell policy is disabled");
|
||||||
|
assert_eq!(sell_fill.date, fourth);
|
||||||
|
assert_eq!(sell_fill.price, 12.0);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn next_bar_open_sell_volume_limit_ignores_decision_day_zero_volume() {
|
fn next_bar_open_sell_volume_limit_ignores_decision_day_zero_volume() {
|
||||||
let first = d(2025, 1, 2);
|
let first = d(2025, 1, 2);
|
||||||
|
|||||||
Reference in New Issue
Block a user