禁止退出信号股票当日补仓
This commit is contained in:
@@ -8762,21 +8762,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
if !stop_hit && !profit_hit {
|
if !stop_hit && !profit_hit {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let lower_limit_blocked = match self.stock_state_at_time(
|
stop_take_exit_signal_symbols.insert(position.symbol.clone());
|
||||||
ctx,
|
|
||||||
projection_date,
|
|
||||||
&position.symbol,
|
|
||||||
Some(self.intraday_execution_start_time()),
|
|
||||||
) {
|
|
||||||
Ok(stock) => stock.lower_limit > 0.0 && stock.last <= stock.lower_limit,
|
|
||||||
Err(BacktestError::Data(crate::data::DataSetError::MissingSnapshot {
|
|
||||||
..
|
|
||||||
})) => false,
|
|
||||||
Err(error) => return Err(error),
|
|
||||||
};
|
|
||||||
if !lower_limit_blocked {
|
|
||||||
stop_take_exit_signal_symbols.insert(position.symbol.clone());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9255,6 +9241,9 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
let rebalance_value_symbols = rebalance_working_symbols.clone();
|
let rebalance_value_symbols = rebalance_working_symbols.clone();
|
||||||
let mut rebalance_pending_buy_value = 0.0_f64;
|
let mut rebalance_pending_buy_value = 0.0_f64;
|
||||||
for symbol in stock_list.iter().take(selection_limit) {
|
for symbol in stock_list.iter().take(selection_limit) {
|
||||||
|
if exit_symbols.contains(symbol) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let decision_stock = self.stock_state_with_factor_date(
|
let decision_stock = self.stock_state_with_factor_date(
|
||||||
ctx,
|
ctx,
|
||||||
decision_date,
|
decision_date,
|
||||||
@@ -17290,7 +17279,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn platform_aiquant_allows_target_adjust_when_stop_loss_sell_is_lower_limit_blocked() {
|
fn platform_aiquant_blocks_target_increase_when_stop_loss_sell_is_lower_limit_blocked() {
|
||||||
let prev_date = d(2025, 1, 10);
|
let prev_date = d(2025, 1, 10);
|
||||||
let date = d(2025, 1, 13);
|
let date = d(2025, 1, 13);
|
||||||
let symbol = "603269.SH";
|
let symbol = "603269.SH";
|
||||||
@@ -17420,7 +17409,7 @@ mod tests {
|
|||||||
reason,
|
reason,
|
||||||
} if intent_symbol == symbol && *target_value == 0.0 && reason == "stop_loss_exit"
|
} if intent_symbol == symbol && *target_value == 0.0 && reason == "stop_loss_exit"
|
||||||
)));
|
)));
|
||||||
assert!(decision.order_intents.iter().any(|intent| matches!(
|
assert!(!decision.order_intents.iter().any(|intent| matches!(
|
||||||
intent,
|
intent,
|
||||||
OrderIntent::Shares {
|
OrderIntent::Shares {
|
||||||
symbol: intent_symbol,
|
symbol: intent_symbol,
|
||||||
@@ -21189,7 +21178,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn platform_weak_market_adjusts_before_lower_limit_blocked_stop_loss() {
|
fn platform_weak_market_skips_target_increase_before_lower_limit_blocked_stop_loss() {
|
||||||
let prev_date = d(2025, 2, 2);
|
let prev_date = d(2025, 2, 2);
|
||||||
let date = d(2025, 2, 3);
|
let date = d(2025, 2, 3);
|
||||||
let symbols = ["000001.SZ", "000002.SZ"];
|
let symbols = ["000001.SZ", "000002.SZ"];
|
||||||
@@ -21315,36 +21304,28 @@ mod tests {
|
|||||||
|
|
||||||
let decision = strategy.on_day(&ctx).expect("platform decision");
|
let decision = strategy.on_day(&ctx).expect("platform decision");
|
||||||
|
|
||||||
let stop_loss_index = decision
|
|
||||||
.order_intents
|
|
||||||
.iter()
|
|
||||||
.position(|intent| {
|
|
||||||
matches!(
|
|
||||||
intent,
|
|
||||||
OrderIntent::TargetValue {
|
|
||||||
symbol,
|
|
||||||
target_value,
|
|
||||||
reason,
|
|
||||||
} if symbol == "000001.SZ" && *target_value == 0.0 && reason == "stop_loss_exit"
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.expect("stop-loss exit");
|
|
||||||
assert!(
|
assert!(
|
||||||
decision.order_intents[..stop_loss_index]
|
!decision.order_intents.iter().any(|intent| matches!(
|
||||||
.iter()
|
intent,
|
||||||
.any(|intent| matches!(
|
OrderIntent::Shares {
|
||||||
intent,
|
symbol,
|
||||||
OrderIntent::Shares {
|
quantity,
|
||||||
symbol,
|
reason,
|
||||||
quantity,
|
} if symbol == "000001.SZ"
|
||||||
reason,
|
&& reason == "daily_position_target_adjust"
|
||||||
} if symbol == "000001.SZ"
|
&& *quantity > 0
|
||||||
&& reason == "daily_position_target_adjust"
|
)),
|
||||||
&& *quantity > 0
|
|
||||||
)),
|
|
||||||
"{:?}",
|
"{:?}",
|
||||||
decision.order_intents
|
decision.order_intents
|
||||||
);
|
);
|
||||||
|
assert!(decision.order_intents.iter().any(|intent| matches!(
|
||||||
|
intent,
|
||||||
|
OrderIntent::TargetValue {
|
||||||
|
symbol,
|
||||||
|
target_value,
|
||||||
|
reason,
|
||||||
|
} if symbol == "000001.SZ" && *target_value == 0.0 && reason == "stop_loss_exit"
|
||||||
|
)));
|
||||||
assert!(decision.order_intents.iter().any(|intent| matches!(
|
assert!(decision.order_intents.iter().any(|intent| matches!(
|
||||||
intent,
|
intent,
|
||||||
OrderIntent::Value { symbol, reason, .. }
|
OrderIntent::Value { symbol, reason, .. }
|
||||||
@@ -21353,7 +21334,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn platform_weak_market_adjusts_before_intraday_stop_loss_sell_blocked() {
|
fn platform_weak_market_skips_target_increase_before_intraday_stop_loss_sell_blocked() {
|
||||||
let prev_date = d(2025, 2, 2);
|
let prev_date = d(2025, 2, 2);
|
||||||
let date = d(2025, 2, 3);
|
let date = d(2025, 2, 3);
|
||||||
let symbols = ["000001.SZ", "000002.SZ"];
|
let symbols = ["000001.SZ", "000002.SZ"];
|
||||||
@@ -21497,36 +21478,28 @@ mod tests {
|
|||||||
|
|
||||||
let decision = strategy.on_day(&ctx).expect("platform decision");
|
let decision = strategy.on_day(&ctx).expect("platform decision");
|
||||||
|
|
||||||
let stop_loss_index = decision
|
|
||||||
.order_intents
|
|
||||||
.iter()
|
|
||||||
.position(|intent| {
|
|
||||||
matches!(
|
|
||||||
intent,
|
|
||||||
OrderIntent::TargetValue {
|
|
||||||
symbol,
|
|
||||||
target_value,
|
|
||||||
reason,
|
|
||||||
} if symbol == "000001.SZ" && *target_value == 0.0 && reason == "stop_loss_exit"
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.expect("stop-loss exit");
|
|
||||||
assert!(
|
assert!(
|
||||||
decision.order_intents[..stop_loss_index]
|
!decision.order_intents.iter().any(|intent| matches!(
|
||||||
.iter()
|
intent,
|
||||||
.any(|intent| matches!(
|
OrderIntent::Shares {
|
||||||
intent,
|
symbol,
|
||||||
OrderIntent::Shares {
|
quantity,
|
||||||
symbol,
|
reason,
|
||||||
quantity,
|
} if symbol == "000001.SZ"
|
||||||
reason,
|
&& reason == "daily_position_target_adjust"
|
||||||
} if symbol == "000001.SZ"
|
&& *quantity > 0
|
||||||
&& reason == "daily_position_target_adjust"
|
)),
|
||||||
&& *quantity > 0
|
|
||||||
)),
|
|
||||||
"{:?}",
|
"{:?}",
|
||||||
decision.order_intents
|
decision.order_intents
|
||||||
);
|
);
|
||||||
|
assert!(decision.order_intents.iter().any(|intent| matches!(
|
||||||
|
intent,
|
||||||
|
OrderIntent::TargetValue {
|
||||||
|
symbol,
|
||||||
|
target_value,
|
||||||
|
reason,
|
||||||
|
} if symbol == "000001.SZ" && *target_value == 0.0 && reason == "stop_loss_exit"
|
||||||
|
)));
|
||||||
assert!(decision.order_intents.iter().any(|intent| matches!(
|
assert!(decision.order_intents.iter().any(|intent| matches!(
|
||||||
intent,
|
intent,
|
||||||
OrderIntent::Value { symbol, reason, .. }
|
OrderIntent::Value { symbol, reason, .. }
|
||||||
@@ -24915,7 +24888,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn platform_periodic_rebalance_can_rebuy_released_unsellable_stop_loss_symbol() {
|
fn platform_periodic_rebalance_skips_unsellable_stop_loss_symbol() {
|
||||||
let prev_date = d(2025, 5, 13);
|
let prev_date = d(2025, 5, 13);
|
||||||
let date = d(2025, 5, 14);
|
let date = d(2025, 5, 14);
|
||||||
let symbols = ["000001.SZ", "000002.SZ", "000003.SZ"];
|
let symbols = ["000001.SZ", "000002.SZ", "000003.SZ"];
|
||||||
@@ -25078,7 +25051,7 @@ mod tests {
|
|||||||
decision.order_intents
|
decision.order_intents
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
decision.order_intents.iter().any(|intent| matches!(
|
!decision.order_intents.iter().any(|intent| matches!(
|
||||||
intent,
|
intent,
|
||||||
OrderIntent::Value { symbol, reason, .. }
|
OrderIntent::Value { symbol, reason, .. }
|
||||||
if symbol == "000001.SZ" && reason == "periodic_rebalance_buy"
|
if symbol == "000001.SZ" && reason == "periodic_rebalance_buy"
|
||||||
@@ -25086,6 +25059,15 @@ mod tests {
|
|||||||
"{:?}",
|
"{:?}",
|
||||||
decision.order_intents
|
decision.order_intents
|
||||||
);
|
);
|
||||||
|
assert!(
|
||||||
|
decision.order_intents.iter().any(|intent| matches!(
|
||||||
|
intent,
|
||||||
|
OrderIntent::Value { symbol, reason, .. }
|
||||||
|
if symbol == "000003.SZ" && reason == "periodic_rebalance_buy"
|
||||||
|
)),
|
||||||
|
"{:?}",
|
||||||
|
decision.order_intents
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user