Add generic rolling factor helpers
This commit is contained in:
@@ -402,6 +402,23 @@ impl SymbolPriceSeries {
|
||||
Some(sum / lookback as f64)
|
||||
}
|
||||
|
||||
fn decision_close_rolling_average(&self, date: NaiveDate, lookback: usize) -> Option<f64> {
|
||||
if lookback == 0 {
|
||||
return None;
|
||||
}
|
||||
let end = self.decision_end_index(date)?;
|
||||
if end == 0 {
|
||||
return None;
|
||||
}
|
||||
let start = end.saturating_sub(lookback);
|
||||
let count = end.saturating_sub(start);
|
||||
if count == 0 {
|
||||
return None;
|
||||
}
|
||||
let sum = self.prev_close_prefix[end] - self.prev_close_prefix[start];
|
||||
Some(sum / count as f64)
|
||||
}
|
||||
|
||||
fn decision_volume_moving_average(&self, date: NaiveDate, lookback: usize) -> Option<f64> {
|
||||
if lookback == 0 {
|
||||
return None;
|
||||
@@ -415,6 +432,23 @@ impl SymbolPriceSeries {
|
||||
Some(sum / lookback as f64)
|
||||
}
|
||||
|
||||
fn decision_volume_rolling_average(&self, date: NaiveDate, lookback: usize) -> Option<f64> {
|
||||
if lookback == 0 {
|
||||
return None;
|
||||
}
|
||||
let end = self.decision_end_index(date)?;
|
||||
if end == 0 {
|
||||
return None;
|
||||
}
|
||||
let start = end.saturating_sub(lookback);
|
||||
let count = end.saturating_sub(start);
|
||||
if count == 0 {
|
||||
return None;
|
||||
}
|
||||
let sum = self.volume_prefix[end] - self.volume_prefix[start];
|
||||
Some(sum / count as f64)
|
||||
}
|
||||
|
||||
fn end_index(&self, date: NaiveDate) -> Option<usize> {
|
||||
match self.dates.binary_search(&date) {
|
||||
Ok(idx) => Some(idx + 1),
|
||||
@@ -827,6 +861,71 @@ impl DataSet {
|
||||
.and_then(|series| series.decision_volume_moving_average(date, lookback))
|
||||
}
|
||||
|
||||
pub fn factor_numeric_value(
|
||||
&self,
|
||||
date: NaiveDate,
|
||||
symbol: &str,
|
||||
field: &str,
|
||||
) -> Option<f64> {
|
||||
self.factor(date, symbol)
|
||||
.and_then(|snapshot| factor_numeric_value(snapshot, field))
|
||||
}
|
||||
|
||||
pub fn factor_moving_average(
|
||||
&self,
|
||||
date: NaiveDate,
|
||||
symbol: &str,
|
||||
field: &str,
|
||||
lookback: usize,
|
||||
) -> Option<f64> {
|
||||
if lookback == 0 {
|
||||
return None;
|
||||
}
|
||||
let dates = self.calendar.trailing_days(date, lookback);
|
||||
if dates.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let mut sum = 0.0_f64;
|
||||
let mut count = 0usize;
|
||||
for trading_day in dates {
|
||||
let snapshot = self.factor(trading_day, symbol)?;
|
||||
let value = factor_numeric_value(snapshot, field)?;
|
||||
sum += value;
|
||||
count += 1;
|
||||
}
|
||||
if count == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(sum / count as f64)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn market_decision_numeric_moving_average(
|
||||
&self,
|
||||
date: NaiveDate,
|
||||
symbol: &str,
|
||||
field: &str,
|
||||
lookback: usize,
|
||||
) -> Option<f64> {
|
||||
match field {
|
||||
"close" | "prev_close" | "stock_close" | "price" => {
|
||||
self.market_series_by_symbol
|
||||
.get(symbol)
|
||||
.and_then(|series| series.decision_close_rolling_average(date, lookback))
|
||||
}
|
||||
"volume" | "stock_volume" => {
|
||||
self.market_series_by_symbol
|
||||
.get(symbol)
|
||||
.and_then(|series| series.decision_volume_rolling_average(date, lookback))
|
||||
}
|
||||
"open" => self.market_moving_average(date, symbol, lookback, PriceField::Open),
|
||||
"last" | "last_price" => {
|
||||
self.market_moving_average(date, symbol, lookback, PriceField::Last)
|
||||
}
|
||||
other => self.factor_moving_average(date, symbol, other, lookback),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn market_moving_average(
|
||||
&self,
|
||||
date: NaiveDate,
|
||||
@@ -981,6 +1080,19 @@ fn read_factors(path: &Path) -> Result<Vec<DailyFactorSnapshot>, DataSetError> {
|
||||
Ok(snapshots)
|
||||
}
|
||||
|
||||
fn factor_numeric_value(snapshot: &DailyFactorSnapshot, field: &str) -> Option<f64> {
|
||||
match field {
|
||||
"market_cap" | "market_cap_bn" => Some(snapshot.market_cap_bn),
|
||||
"free_float_cap" | "free_float_market_cap" | "free_float_cap_bn" => {
|
||||
Some(snapshot.free_float_cap_bn)
|
||||
}
|
||||
"pe_ttm" => Some(snapshot.pe_ttm),
|
||||
"turnover_ratio" => snapshot.turnover_ratio,
|
||||
"effective_turnover_ratio" => snapshot.effective_turnover_ratio,
|
||||
other => snapshot.extra_factors.get(other).copied(),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_candidates(path: &Path) -> Result<Vec<CandidateEligibility>, DataSetError> {
|
||||
let rows = read_rows(path)?;
|
||||
let mut snapshots = Vec::new();
|
||||
|
||||
Reference in New Issue
Block a user