perf: project stock snapshot fields by strategy
This commit is contained in:
@@ -742,6 +742,13 @@ struct StockRollingRequirements {
|
||||
fields: BTreeSet<(StockRollingField, usize)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||
struct StockSnapshotFieldRequirements {
|
||||
amount: bool,
|
||||
touched_upper_limit: bool,
|
||||
touched_lower_limit: bool,
|
||||
}
|
||||
|
||||
impl StockRollingRequirements {
|
||||
fn require(&mut self, field: StockRollingField, lookback: usize) {
|
||||
if lookback > 0 {
|
||||
@@ -1046,6 +1053,7 @@ pub struct PlatformExprStrategy {
|
||||
selection_quote_usage: StockFilterQuoteUsage,
|
||||
stock_rolling_requirements: StockRollingRequirements,
|
||||
stock_current_rolling_mean_required: bool,
|
||||
stock_snapshot_field_requirements: StockSnapshotFieldRequirements,
|
||||
stock_extra_factors_required: bool,
|
||||
stock_extra_factor_identifiers: BTreeSet<String>,
|
||||
stock_extra_factor_map_required: bool,
|
||||
@@ -1325,6 +1333,8 @@ impl PlatformExprStrategy {
|
||||
let stock_rolling_requirements = Self::stock_rolling_requirements_for_config(&config);
|
||||
let stock_current_rolling_mean_required =
|
||||
Self::stock_current_rolling_mean_required_for_config(&config);
|
||||
let stock_snapshot_field_requirements =
|
||||
Self::stock_snapshot_field_requirements_for_config(&config);
|
||||
let stock_extra_factors_required =
|
||||
Self::stock_extra_factors_required_for_config(&config, &prelude_declared_identifiers);
|
||||
let stock_extra_factor_identifiers =
|
||||
@@ -1368,6 +1378,7 @@ impl PlatformExprStrategy {
|
||||
selection_quote_usage,
|
||||
stock_rolling_requirements,
|
||||
stock_current_rolling_mean_required,
|
||||
stock_snapshot_field_requirements,
|
||||
stock_extra_factors_required,
|
||||
stock_extra_factor_identifiers,
|
||||
stock_extra_factor_map_required,
|
||||
@@ -4137,7 +4148,9 @@ impl PlatformExprStrategy {
|
||||
let stock_volume_ma20 = volume_rolling(2);
|
||||
let stock_volume_ma60 = volume_rolling(3);
|
||||
let stock_volume_ma100 = volume_rolling(4);
|
||||
let touched_upper_limit = if intraday_same_day_factor {
|
||||
let touched_upper_limit = if !self.stock_snapshot_field_requirements.touched_upper_limit {
|
||||
false
|
||||
} else if intraday_same_day_factor {
|
||||
!market.paused
|
||||
&& (market.is_at_upper_limit_price(market.close)
|
||||
|| market.is_at_upper_limit_price(market.open)
|
||||
@@ -4156,7 +4169,9 @@ impl PlatformExprStrategy {
|
||||
|| feature_market.is_at_upper_limit_price(feature_market.open)
|
||||
|| feature_market.is_at_upper_limit_price(feature_market.day_open))
|
||||
};
|
||||
let touched_lower_limit = if intraday_same_day_factor {
|
||||
let touched_lower_limit = if !self.stock_snapshot_field_requirements.touched_lower_limit {
|
||||
false
|
||||
} else if intraday_same_day_factor {
|
||||
!market.paused
|
||||
&& (market.is_at_lower_limit_price(market.close)
|
||||
|| market.is_at_lower_limit_price(market.open)
|
||||
@@ -4175,7 +4190,9 @@ impl PlatformExprStrategy {
|
||||
|| feature_market.is_at_lower_limit_price(feature_market.open)
|
||||
|| feature_market.is_at_lower_limit_price(feature_market.day_open))
|
||||
};
|
||||
let amount = if intraday_same_day_factor {
|
||||
let amount = if !self.stock_snapshot_field_requirements.amount {
|
||||
0.0
|
||||
} else if intraday_same_day_factor {
|
||||
f64::NAN
|
||||
} else {
|
||||
factor.extra_factors.get("amount").copied().unwrap_or(0.0)
|
||||
@@ -10052,6 +10069,43 @@ impl PlatformExprStrategy {
|
||||
false
|
||||
}
|
||||
|
||||
fn stock_snapshot_field_requirements_for_config(
|
||||
config: &PlatformExprStrategyConfig,
|
||||
) -> StockSnapshotFieldRequirements {
|
||||
if Self::has_stock_explicit_actions(config)
|
||||
|| Self::stock_extra_factor_map_required_for_config(config)
|
||||
{
|
||||
return StockSnapshotFieldRequirements {
|
||||
amount: true,
|
||||
touched_upper_limit: true,
|
||||
touched_lower_limit: true,
|
||||
};
|
||||
}
|
||||
|
||||
let mut identifiers = BTreeSet::new();
|
||||
for expr in [
|
||||
config.prelude.as_str(),
|
||||
config.stock_filter_expr.as_str(),
|
||||
config.buy_scale_expr.as_str(),
|
||||
config.stop_loss_expr.as_str(),
|
||||
config.take_profit_expr.as_str(),
|
||||
config.rank_expr.as_str(),
|
||||
config.market_cap_field.as_str(),
|
||||
config.rank_by.as_str(),
|
||||
] {
|
||||
identifiers.extend(Self::extract_identifier_candidates(&Self::normalize_expr(
|
||||
expr,
|
||||
)));
|
||||
}
|
||||
StockSnapshotFieldRequirements {
|
||||
amount: identifiers.contains("amount"),
|
||||
touched_upper_limit: identifiers.contains("touched_upper_limit")
|
||||
|| identifiers.contains("hit_upper_limit"),
|
||||
touched_lower_limit: identifiers.contains("touched_lower_limit")
|
||||
|| identifiers.contains("hit_lower_limit"),
|
||||
}
|
||||
}
|
||||
|
||||
fn stock_extra_factors_required_for_config(
|
||||
config: &PlatformExprStrategyConfig,
|
||||
prelude_declared_identifiers: &BTreeSet<String>,
|
||||
@@ -12622,7 +12676,8 @@ mod tests {
|
||||
PlatformPortfolioDrawdownController, PlatformRebalanceSchedule, PlatformScheduleFrequency,
|
||||
PlatformStopTakeReferencePriceMode, PlatformTradeAction, PlatformUniverseActionKind,
|
||||
RuntimeHelperResolution, SelectionRiskDeferral, StockFilterQuoteUsage,
|
||||
framework_stock_rolling_factor_requirement, scheduled_position_exposure,
|
||||
StockSnapshotFieldRequirements, framework_stock_rolling_factor_requirement,
|
||||
scheduled_position_exposure,
|
||||
};
|
||||
use crate::{
|
||||
AlgoOrderStyle, BenchmarkSnapshot, CandidateEligibility, CorporateAction,
|
||||
@@ -14460,6 +14515,38 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stock_snapshot_fields_are_loaded_only_when_the_strategy_uses_them() {
|
||||
let base = PlatformExprStrategy::new(PlatformExprStrategyConfig::microcap_rotation());
|
||||
assert_eq!(
|
||||
base.stock_snapshot_field_requirements,
|
||||
StockSnapshotFieldRequirements::default()
|
||||
);
|
||||
|
||||
let mut direct = PlatformExprStrategyConfig::microcap_rotation();
|
||||
direct.stock_filter_expr =
|
||||
"amount > 0.0 && hit_upper_limit && !touched_lower_limit".to_string();
|
||||
assert_eq!(
|
||||
PlatformExprStrategy::new(direct).stock_snapshot_field_requirements,
|
||||
StockSnapshotFieldRequirements {
|
||||
amount: true,
|
||||
touched_upper_limit: true,
|
||||
touched_lower_limit: true,
|
||||
}
|
||||
);
|
||||
|
||||
let mut dynamic_map = PlatformExprStrategyConfig::microcap_rotation();
|
||||
dynamic_map.stock_filter_expr = "factors[\"custom_alpha\"] > 0.0".to_string();
|
||||
assert_eq!(
|
||||
PlatformExprStrategy::new(dynamic_map).stock_snapshot_field_requirements,
|
||||
StockSnapshotFieldRequirements {
|
||||
amount: true,
|
||||
touched_upper_limit: true,
|
||||
touched_lower_limit: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn platform_expr_missing_requested_factor_does_not_default_to_zero() {
|
||||
let date = d(2025, 5, 19);
|
||||
@@ -17918,7 +18005,9 @@ mod tests {
|
||||
order_events: &[],
|
||||
fills: &[],
|
||||
};
|
||||
let strategy = PlatformExprStrategy::new(PlatformExprStrategyConfig::microcap_rotation());
|
||||
let mut cfg = PlatformExprStrategyConfig::microcap_rotation();
|
||||
cfg.stock_filter_expr = "touched_upper_limit || touched_lower_limit".to_string();
|
||||
let strategy = PlatformExprStrategy::new(cfg);
|
||||
|
||||
let stock = strategy
|
||||
.stock_state_with_factor_date(&ctx, date, date, symbol)
|
||||
@@ -18054,7 +18143,9 @@ mod tests {
|
||||
order_events: &[],
|
||||
fills: &[],
|
||||
};
|
||||
let strategy = PlatformExprStrategy::new(PlatformExprStrategyConfig::microcap_rotation());
|
||||
let mut cfg = PlatformExprStrategyConfig::microcap_rotation();
|
||||
cfg.stock_filter_expr = "touched_upper_limit".to_string();
|
||||
let strategy = PlatformExprStrategy::new(cfg);
|
||||
|
||||
let stock = strategy
|
||||
.stock_state_with_factor_date(&ctx, date, factor_date, symbol)
|
||||
@@ -18159,7 +18250,7 @@ mod tests {
|
||||
};
|
||||
let mut cfg = PlatformExprStrategyConfig::microcap_rotation();
|
||||
cfg.signal_symbol = symbol.to_string();
|
||||
cfg.stock_filter_expr = "true".to_string();
|
||||
cfg.stock_filter_expr = "touched_upper_limit".to_string();
|
||||
cfg.benchmark_short_ma_days = 1;
|
||||
cfg.benchmark_long_ma_days = 1;
|
||||
let strategy = PlatformExprStrategy::new(cfg);
|
||||
@@ -21829,7 +21920,7 @@ mod tests {
|
||||
cfg.market_cap_upper_expr = "100".to_string();
|
||||
cfg.selection_limit_expr = "1".to_string();
|
||||
cfg.stock_filter_expr =
|
||||
"last_price <= 0 || (!at_upper_limit && !at_lower_limit)".to_string();
|
||||
"amount >= 0 && (last_price <= 0 || (!at_upper_limit && !at_lower_limit))".to_string();
|
||||
cfg.exposure_expr = "1.0".to_string();
|
||||
let mut strategy = PlatformExprStrategy::new(cfg);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user