增强回测引擎第二版策略与快照层
This commit is contained in:
@@ -110,14 +110,31 @@ pub struct CandidateEligibility {
|
||||
pub is_paused: bool,
|
||||
pub allow_buy: bool,
|
||||
pub allow_sell: bool,
|
||||
pub is_kcb: bool,
|
||||
pub is_one_yuan: bool,
|
||||
}
|
||||
|
||||
impl CandidateEligibility {
|
||||
pub fn eligible_for_selection(&self) -> bool {
|
||||
!self.is_st && !self.is_new_listing && !self.is_paused && self.allow_buy && self.allow_sell
|
||||
!self.is_st
|
||||
&& !self.is_new_listing
|
||||
&& !self.is_paused
|
||||
&& !self.is_kcb
|
||||
&& !self.is_one_yuan
|
||||
&& self.allow_buy
|
||||
&& self.allow_sell
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DailySnapshotBundle {
|
||||
pub date: NaiveDate,
|
||||
pub benchmark: BenchmarkSnapshot,
|
||||
pub market: Vec<DailyMarketSnapshot>,
|
||||
pub factors: Vec<DailyFactorSnapshot>,
|
||||
pub candidates: Vec<CandidateEligibility>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DataSet {
|
||||
instruments: HashMap<String, Instrument>,
|
||||
@@ -246,6 +263,20 @@ impl DataSet {
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn bundle_on(&self, date: NaiveDate) -> Result<DailySnapshotBundle, DataSetError> {
|
||||
let benchmark = self
|
||||
.benchmark(date)
|
||||
.cloned()
|
||||
.ok_or(DataSetError::MissingBenchmark { date })?;
|
||||
Ok(DailySnapshotBundle {
|
||||
date,
|
||||
benchmark,
|
||||
market: self.market_by_date.get(&date).cloned().unwrap_or_default(),
|
||||
factors: self.factor_by_date.get(&date).cloned().unwrap_or_default(),
|
||||
candidates: self.candidate_by_date.get(&date).cloned().unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn benchmark_closes_up_to(&self, date: NaiveDate, lookback: usize) -> Vec<f64> {
|
||||
self.calendar
|
||||
.trailing_days(date, lookback)
|
||||
@@ -342,6 +373,8 @@ fn read_candidates(path: &Path) -> Result<Vec<CandidateEligibility>, DataSetErro
|
||||
is_paused: row.parse_bool(4)?,
|
||||
allow_buy: row.parse_bool(5)?,
|
||||
allow_sell: row.parse_bool(6)?,
|
||||
is_kcb: row.parse_optional_bool(7).unwrap_or(false),
|
||||
is_one_yuan: row.parse_optional_bool(8).unwrap_or(false),
|
||||
});
|
||||
}
|
||||
Ok(snapshots)
|
||||
@@ -415,6 +448,12 @@ impl CsvRow {
|
||||
message: format!("invalid bool: {err}"),
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_optional_bool(&self, index: usize) -> Option<bool> {
|
||||
self.fields
|
||||
.get(index)
|
||||
.and_then(|value| value.parse::<bool>().ok())
|
||||
}
|
||||
}
|
||||
|
||||
fn read_rows(path: &Path) -> Result<Vec<CsvRow>, DataSetError> {
|
||||
|
||||
Reference in New Issue
Block a user