@@ -706,59 +706,10 @@ struct StockExpressionState {
|
|||||||
stock_volume_ma20: f64,
|
stock_volume_ma20: f64,
|
||||||
stock_volume_ma60: f64,
|
stock_volume_ma60: f64,
|
||||||
stock_volume_ma100: 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_factors: BTreeMap<String, f64>,
|
||||||
extra_text_factors: BTreeMap<String, String>,
|
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)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
enum StockFilterQuoteUsage {
|
enum StockFilterQuoteUsage {
|
||||||
DailyOnly,
|
DailyOnly,
|
||||||
@@ -776,7 +727,6 @@ enum StockRollingField {
|
|||||||
struct StockRollingRequirements {
|
struct StockRollingRequirements {
|
||||||
all: bool,
|
all: bool,
|
||||||
fields: BTreeSet<(StockRollingField, usize)>,
|
fields: BTreeSet<(StockRollingField, usize)>,
|
||||||
current_fields: BTreeSet<(StockRollingField, usize)>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StockRollingRequirements {
|
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) {
|
fn require_all(&mut self) {
|
||||||
self.all = true;
|
self.all = true;
|
||||||
}
|
}
|
||||||
@@ -807,19 +751,6 @@ impl StockRollingRequirements {
|
|||||||
};
|
};
|
||||||
self.fields.contains(&(field, lookback))
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -4012,12 +3943,6 @@ impl PlatformExprStrategy {
|
|||||||
.then_some(lookback)
|
.then_some(lookback)
|
||||||
.unwrap_or(0)
|
.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 = [
|
let close_lookbacks = [
|
||||||
required_rolling("close", self.config.stock_short_ma_days),
|
required_rolling("close", self.config.stock_short_ma_days),
|
||||||
required_rolling("close", self.config.stock_mid_ma_days),
|
required_rolling("close", self.config.stock_mid_ma_days),
|
||||||
@@ -4034,22 +3959,6 @@ impl PlatformExprStrategy {
|
|||||||
required_rolling("volume", 60),
|
required_rolling("volume", 60),
|
||||||
required_rolling("volume", 100),
|
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(
|
let rolling_means = ctx.data.market_standard_rolling_means_by_symbol_id(
|
||||||
date,
|
date,
|
||||||
symbol_id,
|
symbol_id,
|
||||||
@@ -4057,26 +3966,8 @@ impl PlatformExprStrategy {
|
|||||||
&volume_lookbacks,
|
&volume_lookbacks,
|
||||||
false,
|
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 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 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_short = close_rolling(0);
|
||||||
let stock_ma_mid = close_rolling(1);
|
let stock_ma_mid = close_rolling(1);
|
||||||
let stock_ma_long = close_rolling(2);
|
let stock_ma_long = close_rolling(2);
|
||||||
@@ -4089,15 +3980,6 @@ impl PlatformExprStrategy {
|
|||||||
let stock_volume_ma20 = volume_rolling(2);
|
let stock_volume_ma20 = volume_rolling(2);
|
||||||
let stock_volume_ma60 = volume_rolling(3);
|
let stock_volume_ma60 = volume_rolling(3);
|
||||||
let stock_volume_ma100 = volume_rolling(4);
|
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 {
|
let touched_upper_limit = if intraday_same_day_factor {
|
||||||
!market.paused
|
!market.paused
|
||||||
&& (market.is_at_upper_limit_price(market.close)
|
&& (market.is_at_upper_limit_price(market.close)
|
||||||
@@ -4244,15 +4126,6 @@ impl PlatformExprStrategy {
|
|||||||
stock_volume_ma20,
|
stock_volume_ma20,
|
||||||
stock_volume_ma60,
|
stock_volume_ma60,
|
||||||
stock_volume_ma100,
|
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_factors,
|
||||||
extra_text_factors: if self.stock_text_factors_required {
|
extra_text_factors: if self.stock_text_factors_required {
|
||||||
ctx.data
|
ctx.data
|
||||||
@@ -6827,16 +6700,14 @@ impl PlatformExprStrategy {
|
|||||||
"rolling_mean(\"{other}\", {lookback}) requires stock context"
|
"rolling_mean(\"{other}\", {lookback}) requires stock context"
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
stock.standard_rolling_mean(other, lookback, false).or_else(|| {
|
self.stock_decision_rolling_mean(
|
||||||
self.stock_decision_rolling_mean(
|
ctx,
|
||||||
ctx,
|
day.date,
|
||||||
day.date,
|
stock.symbol_id,
|
||||||
stock.symbol_id,
|
&stock.symbol,
|
||||||
&stock.symbol,
|
other,
|
||||||
other,
|
lookback,
|
||||||
lookback,
|
)
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
value.ok_or_else(|| {
|
value.ok_or_else(|| {
|
||||||
@@ -6878,16 +6749,14 @@ impl PlatformExprStrategy {
|
|||||||
"rolling_mean_current(\"{other}\", {lookback}) requires stock context"
|
"rolling_mean_current(\"{other}\", {lookback}) requires stock context"
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
stock.standard_rolling_mean(other, lookback, true).or_else(|| {
|
self.stock_current_rolling_mean(
|
||||||
self.stock_current_rolling_mean(
|
ctx,
|
||||||
ctx,
|
day.date,
|
||||||
day.date,
|
stock.symbol_id,
|
||||||
stock.symbol_id,
|
&stock.symbol,
|
||||||
&stock.symbol,
|
other,
|
||||||
other,
|
lookback,
|
||||||
lookback,
|
)
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
value.ok_or_else(|| {
|
value.ok_or_else(|| {
|
||||||
@@ -9983,32 +9852,9 @@ impl PlatformExprStrategy {
|
|||||||
expr: &str,
|
expr: &str,
|
||||||
) {
|
) {
|
||||||
let compact = Self::compact_expr(expr);
|
let compact = Self::compact_expr(expr);
|
||||||
Self::require_stock_rollings_for_named_helper(
|
Self::require_stock_rollings_for_named_helper(requirements, &compact, "rolling_mean");
|
||||||
requirements,
|
Self::require_stock_rollings_for_named_helper(requirements, &compact, "sma");
|
||||||
&compact,
|
Self::require_stock_rollings_for_named_helper(requirements, &compact, "ma");
|
||||||
"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_vma_helper(requirements, &compact);
|
Self::require_stock_rollings_for_vma_helper(requirements, &compact);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10016,7 +9862,6 @@ impl PlatformExprStrategy {
|
|||||||
requirements: &mut StockRollingRequirements,
|
requirements: &mut StockRollingRequirements,
|
||||||
compact: &str,
|
compact: &str,
|
||||||
helper: &str,
|
helper: &str,
|
||||||
current: bool,
|
|
||||||
) {
|
) {
|
||||||
let needle = format!("{helper}(");
|
let needle = format!("{helper}(");
|
||||||
let mut offset = 0usize;
|
let mut offset = 0usize;
|
||||||
@@ -10038,18 +9883,10 @@ impl PlatformExprStrategy {
|
|||||||
let normalized_field = field.trim().to_ascii_lowercase();
|
let normalized_field = field.trim().to_ascii_lowercase();
|
||||||
match normalized_field.as_str() {
|
match normalized_field.as_str() {
|
||||||
"close" | "prev_close" | "stock_close" | "price" => {
|
"close" | "prev_close" | "stock_close" | "price" => {
|
||||||
if current {
|
requirements.require(StockRollingField::Close, lookback)
|
||||||
requirements.require_current(StockRollingField::Close, lookback)
|
|
||||||
} else {
|
|
||||||
requirements.require(StockRollingField::Close, lookback)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
"volume" | "stock_volume" => {
|
"volume" | "stock_volume" => {
|
||||||
if current {
|
requirements.require(StockRollingField::Volume, lookback)
|
||||||
requirements.require_current(StockRollingField::Volume, lookback)
|
|
||||||
} else {
|
|
||||||
requirements.require(StockRollingField::Volume, lookback)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@@ -13913,7 +13750,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[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();
|
let mut cfg = PlatformExprStrategyConfig::microcap_rotation();
|
||||||
cfg.stock_filter_expr = concat!(
|
cfg.stock_filter_expr = concat!(
|
||||||
"rolling_mean_current(\"close\", 5) > rolling_mean_current(\"close\", 10)",
|
"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("close", 10));
|
||||||
assert!(!strategy.stock_rolling_requirements.requires("volume", 5));
|
assert!(!strategy.stock_rolling_requirements.requires("volume", 5));
|
||||||
assert!(!strategy.stock_rolling_requirements.requires("volume", 100));
|
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]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user