修正内置选择器next-open风控语义
This commit is contained in:
@@ -1506,6 +1506,7 @@ impl Strategy for CnSmallCapRotationStrategy {
|
||||
data: ctx.data,
|
||||
dynamic_universe: ctx.dynamic_universe,
|
||||
risk_config: Some(&self.config.risk_config),
|
||||
defer_selection_risk: ctx.is_lagged_execution(),
|
||||
});
|
||||
let before_ma_count = selected_before_ma.len();
|
||||
let mut ma_rejects = Vec::new();
|
||||
|
||||
@@ -50,15 +50,16 @@ pub struct SelectionContext<'a> {
|
||||
pub data: &'a DataSet,
|
||||
pub dynamic_universe: Option<&'a BTreeSet<String>>,
|
||||
pub risk_config: Option<&'a FidcRiskControlConfig>,
|
||||
pub defer_selection_risk: bool,
|
||||
}
|
||||
|
||||
impl SelectionContext<'_> {
|
||||
fn eligible_universe(&self) -> Vec<EligibleUniverseSnapshot> {
|
||||
let eligible = match self.risk_config {
|
||||
Some(risk_config) => self
|
||||
let eligible = match (self.risk_config, self.defer_selection_risk) {
|
||||
(Some(risk_config), false) => self
|
||||
.data
|
||||
.eligible_universe_on_with_risk_config(self.decision_date, risk_config),
|
||||
None => self.data.eligible_universe_on(self.decision_date).to_vec(),
|
||||
_ => self.data.eligible_universe_on(self.decision_date).to_vec(),
|
||||
};
|
||||
match self.dynamic_universe {
|
||||
Some(symbols) if !symbols.is_empty() => eligible
|
||||
@@ -406,6 +407,7 @@ mod tests {
|
||||
data: &data,
|
||||
dynamic_universe: None,
|
||||
risk_config: Some(&risk_config),
|
||||
defer_selection_risk: false,
|
||||
});
|
||||
|
||||
let rules = diagnostics
|
||||
@@ -463,6 +465,7 @@ mod tests {
|
||||
data: &data,
|
||||
dynamic_universe: None,
|
||||
risk_config: Some(&risk_config),
|
||||
defer_selection_risk: false,
|
||||
});
|
||||
|
||||
let selected_symbols = selected
|
||||
@@ -481,4 +484,61 @@ mod tests {
|
||||
assert!(rules.contains("st"), "{:?}", diagnostics.risk_decisions);
|
||||
assert!(rules.contains("kcb"), "{:?}", diagnostics.risk_decisions);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn selector_can_defer_configured_selection_risk_without_losing_diagnostics() {
|
||||
let data = DataSet::from_components(
|
||||
vec![
|
||||
instrument("000001.SZ"),
|
||||
instrument("688001.SH"),
|
||||
instrument("000002.SZ"),
|
||||
],
|
||||
vec![
|
||||
market("000001.SZ", 10.0),
|
||||
market("688001.SH", 10.0),
|
||||
market("000002.SZ", 10.0),
|
||||
],
|
||||
vec![
|
||||
factor("000001.SZ", 8.0),
|
||||
factor("688001.SH", 9.0),
|
||||
factor("000002.SZ", 10.0),
|
||||
],
|
||||
vec![
|
||||
candidate("000001.SZ", true, false),
|
||||
candidate("688001.SH", false, true),
|
||||
candidate("000002.SZ", false, false),
|
||||
],
|
||||
vec![benchmark()],
|
||||
)
|
||||
.unwrap();
|
||||
let selector = DynamicMarketCapBandSelector::new(2000.0, 7.0, 10.0, 0.0, 10, 0.0, 0.0, 0.0);
|
||||
let mut risk_config = FidcRiskControlConfig::default();
|
||||
risk_config.static_rules.reject_st_selection = true;
|
||||
risk_config.static_rules.reject_kcb_selection = true;
|
||||
|
||||
let (selected, diagnostics) = selector.select_with_diagnostics(&SelectionContext {
|
||||
decision_date: d(),
|
||||
benchmark: &benchmark(),
|
||||
reference_level: 2000.0,
|
||||
data: &data,
|
||||
dynamic_universe: None,
|
||||
risk_config: Some(&risk_config),
|
||||
defer_selection_risk: true,
|
||||
});
|
||||
|
||||
let selected_symbols = selected
|
||||
.iter()
|
||||
.map(|candidate| candidate.symbol.as_str())
|
||||
.collect::<BTreeSet<_>>();
|
||||
assert!(selected_symbols.contains("000001.SZ"));
|
||||
assert!(selected_symbols.contains("688001.SH"));
|
||||
assert!(selected_symbols.contains("000002.SZ"));
|
||||
let rules = diagnostics
|
||||
.risk_decisions
|
||||
.iter()
|
||||
.map(|decision| decision.rule_code.as_str())
|
||||
.collect::<BTreeSet<_>>();
|
||||
assert!(rules.contains("st"), "{:?}", diagnostics.risk_decisions);
|
||||
assert!(rules.contains("kcb"), "{:?}", diagnostics.risk_decisions);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user