chore: 更新 fidc-backtest-engine - 2026-05-22
This commit is contained in:
@@ -9,6 +9,7 @@ use thiserror::Error;
|
||||
use crate::calendar::TradingCalendar;
|
||||
use crate::futures::{FuturesCommissionType, FuturesTradingParameter};
|
||||
use crate::instrument::Instrument;
|
||||
use crate::risk_control::ChinaAShareRiskControl;
|
||||
|
||||
mod date_format {
|
||||
use chrono::NaiveDate;
|
||||
@@ -1080,8 +1081,12 @@ impl DataSet {
|
||||
let market_series_by_symbol = build_market_series(&market_by_date);
|
||||
let benchmark_series_cache =
|
||||
BenchmarkPriceSeries::new(&benchmark_by_date.values().cloned().collect::<Vec<_>>());
|
||||
let eligible_universe_by_date =
|
||||
build_eligible_universe(&factor_by_date, &candidate_index, &market_index);
|
||||
let eligible_universe_by_date = build_eligible_universe(
|
||||
&factor_by_date,
|
||||
&candidate_index,
|
||||
&market_index,
|
||||
&instruments,
|
||||
);
|
||||
let futures_params_by_symbol = build_futures_params_index(futures_params);
|
||||
|
||||
Ok(Self {
|
||||
@@ -3287,6 +3292,7 @@ fn build_eligible_universe(
|
||||
factor_by_date: &BTreeMap<NaiveDate, Vec<DailyFactorSnapshot>>,
|
||||
candidate_index: &HashMap<(NaiveDate, String), CandidateEligibility>,
|
||||
market_index: &HashMap<(NaiveDate, String), DailyMarketSnapshot>,
|
||||
instruments: &HashMap<String, Instrument>,
|
||||
) -> BTreeMap<NaiveDate, Vec<EligibleUniverseSnapshot>> {
|
||||
let mut per_date = BTreeMap::<NaiveDate, Vec<EligibleUniverseSnapshot>>::new();
|
||||
|
||||
@@ -3303,7 +3309,14 @@ fn build_eligible_universe(
|
||||
let Some(market) = market_index.get(&key) else {
|
||||
continue;
|
||||
};
|
||||
if !candidate.eligible_for_selection() || market.paused {
|
||||
if ChinaAShareRiskControl::selection_rejection_reason(
|
||||
*date,
|
||||
candidate,
|
||||
market,
|
||||
instruments.get(&factor.symbol),
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
rows.push(EligibleUniverseSnapshot {
|
||||
@@ -3324,6 +3337,11 @@ fn build_eligible_universe(
|
||||
per_date
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn instrument_passes_baseline_selection(instrument: Option<&Instrument>, date: NaiveDate) -> bool {
|
||||
ChinaAShareRiskControl::instrument_rejection_reason(instrument, date).is_none()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -3363,6 +3381,49 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn baseline_selection_uses_structured_instrument_dates_and_status_only() {
|
||||
let date = NaiveDate::parse_from_str("2025-01-02", "%Y-%m-%d").unwrap();
|
||||
let instrument = |name: &str, status: &str, delisted_at: Option<NaiveDate>| Instrument {
|
||||
symbol: "000001.SZ".to_string(),
|
||||
name: name.to_string(),
|
||||
board: "SZ".to_string(),
|
||||
round_lot: 100,
|
||||
listed_at: Some(NaiveDate::parse_from_str("2020-01-01", "%Y-%m-%d").unwrap()),
|
||||
delisted_at,
|
||||
status: status.to_string(),
|
||||
};
|
||||
|
||||
assert!(instrument_passes_baseline_selection(
|
||||
Some(&instrument("Short History Stock", "active", None)),
|
||||
date
|
||||
));
|
||||
assert!(instrument_passes_baseline_selection(
|
||||
Some(&instrument("*ST测试", "active", None)),
|
||||
date
|
||||
));
|
||||
assert!(instrument_passes_baseline_selection(
|
||||
Some(&instrument("ST测试", "active", None)),
|
||||
date
|
||||
));
|
||||
assert!(instrument_passes_baseline_selection(
|
||||
Some(&instrument("退市测试", "active", None)),
|
||||
date
|
||||
));
|
||||
assert!(!instrument_passes_baseline_selection(
|
||||
Some(&instrument("正常名称", "delisted", None)),
|
||||
date
|
||||
));
|
||||
assert!(!instrument_passes_baseline_selection(
|
||||
Some(&instrument(
|
||||
"正常名称",
|
||||
"active",
|
||||
Some(NaiveDate::parse_from_str("2025-01-01", "%Y-%m-%d").unwrap()),
|
||||
)),
|
||||
date
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decision_volume_average_uses_previous_completed_days_only() {
|
||||
let series = SymbolPriceSeries::new(&[
|
||||
|
||||
Reference in New Issue
Block a user