@@ -706,59 +706,10 @@ struct StockExpressionState {
|
||||
stock_volume_ma20: f64,
|
||||
stock_volume_ma60: f64,
|
||||
stock_volume_ma100: f64,
|
||||
stock_current_ma5: f64,
|
||||
stock_current_ma10: f64,
|
||||
stock_current_ma20: f64,
|
||||
stock_current_ma30: f64,
|
||||
stock_current_volume_ma5: f64,
|
||||
stock_current_volume_ma10: f64,
|
||||
stock_current_volume_ma20: f64,
|
||||
stock_current_volume_ma60: f64,
|
||||
stock_current_volume_ma100: f64,
|
||||
extra_factors: BTreeMap<String, f64>,
|
||||
extra_text_factors: BTreeMap<String, String>,
|
||||
}
|
||||
|
||||
impl StockExpressionState {
|
||||
fn standard_rolling_mean(
|
||||
&self,
|
||||
field: &str,
|
||||
lookback: usize,
|
||||
include_now: bool,
|
||||
) -> Option<f64> {
|
||||
let value = match (field, lookback, include_now) {
|
||||
("close" | "prev_close" | "stock_close" | "price", 5, false) => self.stock_ma5,
|
||||
("close" | "prev_close" | "stock_close" | "price", 10, false) => self.stock_ma10,
|
||||
("close" | "prev_close" | "stock_close" | "price", 20, false) => self.stock_ma20,
|
||||
("close" | "prev_close" | "stock_close" | "price", 30, false) => self.stock_ma30,
|
||||
("volume" | "stock_volume", 5, false) => self.stock_volume_ma5,
|
||||
("volume" | "stock_volume", 10, false) => self.stock_volume_ma10,
|
||||
("volume" | "stock_volume", 20, false) => self.stock_volume_ma20,
|
||||
("volume" | "stock_volume", 60, false) => self.stock_volume_ma60,
|
||||
("volume" | "stock_volume", 100, false) => self.stock_volume_ma100,
|
||||
("close" | "prev_close" | "stock_close" | "price", 5, true) => {
|
||||
self.stock_current_ma5
|
||||
}
|
||||
("close" | "prev_close" | "stock_close" | "price", 10, true) => {
|
||||
self.stock_current_ma10
|
||||
}
|
||||
("close" | "prev_close" | "stock_close" | "price", 20, true) => {
|
||||
self.stock_current_ma20
|
||||
}
|
||||
("close" | "prev_close" | "stock_close" | "price", 30, true) => {
|
||||
self.stock_current_ma30
|
||||
}
|
||||
("volume" | "stock_volume", 5, true) => self.stock_current_volume_ma5,
|
||||
("volume" | "stock_volume", 10, true) => self.stock_current_volume_ma10,
|
||||
("volume" | "stock_volume", 20, true) => self.stock_current_volume_ma20,
|
||||
("volume" | "stock_volume", 60, true) => self.stock_current_volume_ma60,
|
||||
("volume" | "stock_volume", 100, true) => self.stock_current_volume_ma100,
|
||||
_ => return None,
|
||||
};
|
||||
value.is_finite().then_some(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum StockFilterQuoteUsage {
|
||||
DailyOnly,
|
||||
@@ -776,7 +727,6 @@ enum StockRollingField {
|
||||
struct StockRollingRequirements {
|
||||
all: bool,
|
||||
fields: BTreeSet<(StockRollingField, usize)>,
|
||||
current_fields: BTreeSet<(StockRollingField, usize)>,
|
||||
}
|
||||
|
||||
impl StockRollingRequirements {
|
||||
@@ -786,12 +736,6 @@ impl StockRollingRequirements {
|
||||
}
|
||||
}
|
||||
|
||||
fn require_current(&mut self, field: StockRollingField, lookback: usize) {
|
||||
if lookback > 0 {
|
||||
self.current_fields.insert((field, lookback));
|
||||
}
|
||||
}
|
||||
|
||||
fn require_all(&mut self) {
|
||||
self.all = true;
|
||||
}
|
||||
@@ -807,19 +751,6 @@ impl StockRollingRequirements {
|
||||
};
|
||||
self.fields.contains(&(field, lookback))
|
||||
}
|
||||
|
||||
fn requires_current(&self, field: &'static str, lookback: usize) -> bool {
|
||||
let field = match field {
|
||||
"close" => StockRollingField::Close,
|
||||
"volume" => StockRollingField::Volume,
|
||||
_ => return false,
|
||||
};
|
||||
self.current_fields.contains(&(field, lookback))
|
||||
}
|
||||
|
||||
fn has_current(&self) -> bool {
|
||||
!self.current_fields.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -4012,12 +3943,6 @@ impl PlatformExprStrategy {
|
||||
.then_some(lookback)
|
||||
.unwrap_or(0)
|
||||
};
|
||||
let required_current_rolling = |field: &'static str, lookback: usize| {
|
||||
self.stock_rolling_requirements
|
||||
.requires_current(field, lookback)
|
||||
.then_some(lookback)
|
||||
.unwrap_or(0)
|
||||
};
|
||||
let close_lookbacks = [
|
||||
required_rolling("close", self.config.stock_short_ma_days),
|
||||
required_rolling("close", self.config.stock_mid_ma_days),
|
||||
@@ -4034,22 +3959,6 @@ impl PlatformExprStrategy {
|
||||
required_rolling("volume", 60),
|
||||
required_rolling("volume", 100),
|
||||
];
|
||||
let current_close_lookbacks = [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
required_current_rolling("close", 5),
|
||||
required_current_rolling("close", 10),
|
||||
required_current_rolling("close", 20),
|
||||
required_current_rolling("close", 30),
|
||||
];
|
||||
let current_volume_lookbacks = [
|
||||
required_current_rolling("volume", 5),
|
||||
required_current_rolling("volume", 10),
|
||||
required_current_rolling("volume", 20),
|
||||
required_current_rolling("volume", 60),
|
||||
required_current_rolling("volume", 100),
|
||||
];
|
||||
let rolling_means = ctx.data.market_standard_rolling_means_by_symbol_id(
|
||||
date,
|
||||
symbol_id,
|
||||
@@ -4057,26 +3966,8 @@ impl PlatformExprStrategy {
|
||||
&volume_lookbacks,
|
||||
false,
|
||||
);
|
||||
let current_rolling_means = if self.stock_rolling_requirements.has_current() {
|
||||
ctx.data.market_standard_rolling_means_by_symbol_id(
|
||||
date,
|
||||
symbol_id,
|
||||
¤t_close_lookbacks,
|
||||
¤t_volume_lookbacks,
|
||||
true,
|
||||
)
|
||||
} else {
|
||||
crate::data::StandardRollingMeans {
|
||||
close: [None; 7],
|
||||
volume: [None; 5],
|
||||
}
|
||||
};
|
||||
let close_rolling = |index: usize| rolling_means.close[index].unwrap_or(f64::NAN);
|
||||
let volume_rolling = |index: usize| rolling_means.volume[index].unwrap_or(f64::NAN);
|
||||
let current_close_rolling =
|
||||
|index: usize| current_rolling_means.close[index].unwrap_or(f64::NAN);
|
||||
let current_volume_rolling =
|
||||
|index: usize| current_rolling_means.volume[index].unwrap_or(f64::NAN);
|
||||
let stock_ma_short = close_rolling(0);
|
||||
let stock_ma_mid = close_rolling(1);
|
||||
let stock_ma_long = close_rolling(2);
|
||||
@@ -4089,15 +3980,6 @@ impl PlatformExprStrategy {
|
||||
let stock_volume_ma20 = volume_rolling(2);
|
||||
let stock_volume_ma60 = volume_rolling(3);
|
||||
let stock_volume_ma100 = volume_rolling(4);
|
||||
let stock_current_ma5 = current_close_rolling(3);
|
||||
let stock_current_ma10 = current_close_rolling(4);
|
||||
let stock_current_ma20 = current_close_rolling(5);
|
||||
let stock_current_ma30 = current_close_rolling(6);
|
||||
let stock_current_volume_ma5 = current_volume_rolling(0);
|
||||
let stock_current_volume_ma10 = current_volume_rolling(1);
|
||||
let stock_current_volume_ma20 = current_volume_rolling(2);
|
||||
let stock_current_volume_ma60 = current_volume_rolling(3);
|
||||
let stock_current_volume_ma100 = current_volume_rolling(4);
|
||||
let touched_upper_limit = if intraday_same_day_factor {
|
||||
!market.paused
|
||||
&& (market.is_at_upper_limit_price(market.close)
|
||||
@@ -4244,15 +4126,6 @@ impl PlatformExprStrategy {
|
||||
stock_volume_ma20,
|
||||
stock_volume_ma60,
|
||||
stock_volume_ma100,
|
||||
stock_current_ma5,
|
||||
stock_current_ma10,
|
||||
stock_current_ma20,
|
||||
stock_current_ma30,
|
||||
stock_current_volume_ma5,
|
||||
stock_current_volume_ma10,
|
||||
stock_current_volume_ma20,
|
||||
stock_current_volume_ma60,
|
||||
stock_current_volume_ma100,
|
||||
extra_factors,
|
||||
extra_text_factors: if self.stock_text_factors_required {
|
||||
ctx.data
|
||||
@@ -6827,16 +6700,14 @@ impl PlatformExprStrategy {
|
||||
"rolling_mean(\"{other}\", {lookback}) requires stock context"
|
||||
))
|
||||
})?;
|
||||
stock.standard_rolling_mean(other, lookback, false).or_else(|| {
|
||||
self.stock_decision_rolling_mean(
|
||||
ctx,
|
||||
day.date,
|
||||
stock.symbol_id,
|
||||
&stock.symbol,
|
||||
other,
|
||||
lookback,
|
||||
)
|
||||
})
|
||||
self.stock_decision_rolling_mean(
|
||||
ctx,
|
||||
day.date,
|
||||
stock.symbol_id,
|
||||
&stock.symbol,
|
||||
other,
|
||||
lookback,
|
||||
)
|
||||
}
|
||||
};
|
||||
value.ok_or_else(|| {
|
||||
@@ -6878,16 +6749,14 @@ impl PlatformExprStrategy {
|
||||
"rolling_mean_current(\"{other}\", {lookback}) requires stock context"
|
||||
))
|
||||
})?;
|
||||
stock.standard_rolling_mean(other, lookback, true).or_else(|| {
|
||||
self.stock_current_rolling_mean(
|
||||
ctx,
|
||||
day.date,
|
||||
stock.symbol_id,
|
||||
&stock.symbol,
|
||||
other,
|
||||
lookback,
|
||||
)
|
||||
})
|
||||
self.stock_current_rolling_mean(
|
||||
ctx,
|
||||
day.date,
|
||||
stock.symbol_id,
|
||||
&stock.symbol,
|
||||
other,
|
||||
lookback,
|
||||
)
|
||||
}
|
||||
};
|
||||
value.ok_or_else(|| {
|
||||
@@ -9983,32 +9852,9 @@ impl PlatformExprStrategy {
|
||||
expr: &str,
|
||||
) {
|
||||
let compact = Self::compact_expr(expr);
|
||||
Self::require_stock_rollings_for_named_helper(
|
||||
requirements,
|
||||
&compact,
|
||||
"rolling_mean",
|
||||
false,
|
||||
);
|
||||
Self::require_stock_rollings_for_named_helper(requirements, &compact, "sma", false);
|
||||
Self::require_stock_rollings_for_named_helper(requirements, &compact, "ma", false);
|
||||
Self::require_stock_rollings_for_named_helper(
|
||||
requirements,
|
||||
&compact,
|
||||
"rolling_mean_current",
|
||||
true,
|
||||
);
|
||||
Self::require_stock_rollings_for_named_helper(
|
||||
requirements,
|
||||
&compact,
|
||||
"rolling_max_current",
|
||||
true,
|
||||
);
|
||||
Self::require_stock_rollings_for_named_helper(
|
||||
requirements,
|
||||
&compact,
|
||||
"rolling_return_stddev_current",
|
||||
true,
|
||||
);
|
||||
Self::require_stock_rollings_for_named_helper(requirements, &compact, "rolling_mean");
|
||||
Self::require_stock_rollings_for_named_helper(requirements, &compact, "sma");
|
||||
Self::require_stock_rollings_for_named_helper(requirements, &compact, "ma");
|
||||
Self::require_stock_rollings_for_vma_helper(requirements, &compact);
|
||||
}
|
||||
|
||||
@@ -10016,7 +9862,6 @@ impl PlatformExprStrategy {
|
||||
requirements: &mut StockRollingRequirements,
|
||||
compact: &str,
|
||||
helper: &str,
|
||||
current: bool,
|
||||
) {
|
||||
let needle = format!("{helper}(");
|
||||
let mut offset = 0usize;
|
||||
@@ -10038,18 +9883,10 @@ impl PlatformExprStrategy {
|
||||
let normalized_field = field.trim().to_ascii_lowercase();
|
||||
match normalized_field.as_str() {
|
||||
"close" | "prev_close" | "stock_close" | "price" => {
|
||||
if current {
|
||||
requirements.require_current(StockRollingField::Close, lookback)
|
||||
} else {
|
||||
requirements.require(StockRollingField::Close, lookback)
|
||||
}
|
||||
requirements.require(StockRollingField::Close, lookback)
|
||||
}
|
||||
"volume" | "stock_volume" => {
|
||||
if current {
|
||||
requirements.require_current(StockRollingField::Volume, lookback)
|
||||
} else {
|
||||
requirements.require(StockRollingField::Volume, lookback)
|
||||
}
|
||||
requirements.require(StockRollingField::Volume, lookback)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -13913,7 +13750,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn current_rolling_helpers_load_only_current_standard_rollings() {
|
||||
fn current_rolling_helpers_do_not_load_factor_maps_or_decision_rollings() {
|
||||
let mut cfg = PlatformExprStrategyConfig::microcap_rotation();
|
||||
cfg.stock_filter_expr = concat!(
|
||||
"rolling_mean_current(\"close\", 5) > rolling_mean_current(\"close\", 10)",
|
||||
@@ -13928,18 +13765,6 @@ mod tests {
|
||||
assert!(!strategy.stock_rolling_requirements.requires("close", 10));
|
||||
assert!(!strategy.stock_rolling_requirements.requires("volume", 5));
|
||||
assert!(!strategy.stock_rolling_requirements.requires("volume", 100));
|
||||
assert!(strategy
|
||||
.stock_rolling_requirements
|
||||
.requires_current("close", 5));
|
||||
assert!(strategy
|
||||
.stock_rolling_requirements
|
||||
.requires_current("close", 10));
|
||||
assert!(strategy
|
||||
.stock_rolling_requirements
|
||||
.requires_current("volume", 5));
|
||||
assert!(strategy
|
||||
.stock_rolling_requirements
|
||||
.requires_current("volume", 100));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user