共享固定数值因子字段名
This commit is contained in:
@@ -161,6 +161,8 @@ impl DailyMarketSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
pub type NumericFactorMap = BTreeMap<Cow<'static, str>, f64>;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DailyFactorSnapshot {
|
||||
#[serde(with = "date_format")]
|
||||
@@ -172,7 +174,7 @@ pub struct DailyFactorSnapshot {
|
||||
pub turnover_ratio: Option<f64>,
|
||||
pub effective_turnover_ratio: Option<f64>,
|
||||
#[serde(default)]
|
||||
pub extra_factors: BTreeMap<String, f64>,
|
||||
pub extra_factors: NumericFactorMap,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -3264,11 +3266,15 @@ fn normalize_factor_snapshots(factors: Vec<DailyFactorSnapshot>) -> Vec<DailyFac
|
||||
.extra_factors
|
||||
.into_iter()
|
||||
.filter_map(|(field, value)| {
|
||||
let normalized = normalize_field(&field);
|
||||
if normalized.is_empty() || !value.is_finite() {
|
||||
let trimmed = field.as_ref().trim().trim_matches('"').trim_matches('\'');
|
||||
if trimmed.is_empty() || !value.is_finite() {
|
||||
None
|
||||
} else if trimmed == field.as_ref()
|
||||
&& trimmed.bytes().all(|byte| !byte.is_ascii_uppercase())
|
||||
{
|
||||
Some((field, value))
|
||||
} else {
|
||||
Some((normalized, value))
|
||||
Some((Cow::Owned(trimmed.to_ascii_lowercase()), value))
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
@@ -3962,10 +3968,9 @@ mod tests {
|
||||
.map(|(index, date)| {
|
||||
let mut extra_factors = BTreeMap::new();
|
||||
if let Some(values) = availability {
|
||||
extra_factors
|
||||
.insert("source_daily_volume_available".to_string(), values[index]);
|
||||
extra_factors.insert("source_daily_volume_available".into(), values[index]);
|
||||
if values[index] >= 0.5 {
|
||||
extra_factors.insert("daily_volume".to_string(), volumes[index] as f64);
|
||||
extra_factors.insert("daily_volume".into(), volumes[index] as f64);
|
||||
}
|
||||
}
|
||||
DailyFactorSnapshot {
|
||||
@@ -4140,10 +4145,7 @@ mod tests {
|
||||
pe_ttm: 10.0,
|
||||
turnover_ratio: None,
|
||||
effective_turnover_ratio: None,
|
||||
extra_factors: BTreeMap::from([(
|
||||
"adjustment_factor_backward1".to_string(),
|
||||
factor,
|
||||
)]),
|
||||
extra_factors: BTreeMap::from([("adjustment_factor_backward1".into(), factor)]),
|
||||
})
|
||||
.collect(),
|
||||
Vec::new(),
|
||||
@@ -4243,7 +4245,7 @@ mod tests {
|
||||
extra_factors: if *date == dates[3] {
|
||||
BTreeMap::new()
|
||||
} else {
|
||||
BTreeMap::from([("adjustment_factor_backward1".to_string(), 1.0)])
|
||||
BTreeMap::from([("adjustment_factor_backward1".into(), 1.0)])
|
||||
},
|
||||
})
|
||||
.collect(),
|
||||
|
||||
@@ -31,8 +31,8 @@ pub use data::{
|
||||
BenchmarkSnapshot, CandidateEligibility, CorporateAction, DailyFactorSnapshot,
|
||||
DailyMarketSnapshot, DailySnapshotBundle, DataSet, DataSetError, DividendRecord,
|
||||
EligibleUniverseSnapshot, FactorTextValue, FactorValue, IntradayExecutionQuote,
|
||||
IntradayOrderBookDepthLevel, PriceBar, PriceField, SecuritiesMarginRecord, SplitRecord,
|
||||
YieldCurvePoint,
|
||||
IntradayOrderBookDepthLevel, NumericFactorMap, PriceBar, PriceField, SecuritiesMarginRecord,
|
||||
SplitRecord, YieldCurvePoint,
|
||||
};
|
||||
pub use engine::{
|
||||
AnalyzerMonthlyReturnRow, AnalyzerPositionRow, AnalyzerReport, AnalyzerRiskSummary,
|
||||
|
||||
@@ -3669,7 +3669,7 @@ impl PlatformExprStrategy {
|
||||
ctx.data
|
||||
.factor_snapshots_on(date)
|
||||
.into_iter()
|
||||
.flat_map(|row| row.extra_factors.keys().cloned())
|
||||
.flat_map(|row| row.extra_factors.keys().map(|key| key.to_string()))
|
||||
.collect()
|
||||
} else {
|
||||
BTreeSet::new()
|
||||
@@ -3993,7 +3993,11 @@ impl PlatformExprStrategy {
|
||||
};
|
||||
|
||||
let extra_factors = if self.stock_extra_factors_required {
|
||||
factor.extra_factors.clone()
|
||||
factor
|
||||
.extra_factors
|
||||
.iter()
|
||||
.map(|(field, value)| (field.to_string(), *value))
|
||||
.collect()
|
||||
} else {
|
||||
BTreeMap::new()
|
||||
};
|
||||
@@ -13686,7 +13690,7 @@ mod tests {
|
||||
pe_ttm: 8.0,
|
||||
turnover_ratio: Some(1.0),
|
||||
effective_turnover_ratio: Some(1.0),
|
||||
extra_factors: BTreeMap::from([("model_score".to_string(), 2.0)]),
|
||||
extra_factors: BTreeMap::from([("model_score".into(), 2.0)]),
|
||||
},
|
||||
],
|
||||
symbols
|
||||
@@ -16173,10 +16177,7 @@ mod tests {
|
||||
pe_ttm: 8.0,
|
||||
turnover_ratio: Some(3.0),
|
||||
effective_turnover_ratio: Some(3.0),
|
||||
extra_factors: BTreeMap::from([(
|
||||
"adjustment_factor_backward1".to_string(),
|
||||
1.0,
|
||||
)]),
|
||||
extra_factors: BTreeMap::from([("adjustment_factor_backward1".into(), 1.0)]),
|
||||
})
|
||||
.collect(),
|
||||
vec![CandidateEligibility {
|
||||
@@ -17125,7 +17126,7 @@ mod tests {
|
||||
pe_ttm: 8.0,
|
||||
turnover_ratio: Some(1.2),
|
||||
effective_turnover_ratio: Some(1.2),
|
||||
extra_factors: BTreeMap::from([("touched_upper_limit".to_string(), 1.0)]),
|
||||
extra_factors: BTreeMap::from([("touched_upper_limit".into(), 1.0)]),
|
||||
},
|
||||
DailyFactorSnapshot {
|
||||
date,
|
||||
@@ -17239,7 +17240,7 @@ mod tests {
|
||||
pe_ttm: 8.0,
|
||||
turnover_ratio: Some(1.0),
|
||||
effective_turnover_ratio: Some(1.0),
|
||||
extra_factors: BTreeMap::from([("touched_upper_limit".to_string(), 1.0)]),
|
||||
extra_factors: BTreeMap::from([("touched_upper_limit".into(), 1.0)]),
|
||||
}],
|
||||
vec![CandidateEligibility {
|
||||
date,
|
||||
@@ -17976,7 +17977,7 @@ mod tests {
|
||||
pe_ttm: 8.0,
|
||||
turnover_ratio: Some(1.0),
|
||||
effective_turnover_ratio: Some(1.0),
|
||||
extra_factors: BTreeMap::from([("adjustment_factor_backward1".to_string(), 1.0)]),
|
||||
extra_factors: BTreeMap::from([("adjustment_factor_backward1".into(), 1.0)]),
|
||||
})
|
||||
.collect();
|
||||
let benchmark_rows: Vec<BenchmarkSnapshot> = market_rows
|
||||
@@ -20825,7 +20826,7 @@ mod tests {
|
||||
pe_ttm: 8.0,
|
||||
turnover_ratio: Some(1.0),
|
||||
effective_turnover_ratio: Some(1.0),
|
||||
extra_factors: BTreeMap::from([("amount".to_string(), 30_000_000.0)]),
|
||||
extra_factors: BTreeMap::from([("amount".into(), 30_000_000.0)]),
|
||||
},
|
||||
DailyFactorSnapshot {
|
||||
date: decision_date,
|
||||
@@ -22054,17 +22055,17 @@ mod tests {
|
||||
turnover_ratio: Some(22.0),
|
||||
effective_turnover_ratio: Some(18.0),
|
||||
extra_factors: BTreeMap::from([
|
||||
("custom_alpha".to_string(), 7.0),
|
||||
("margin_all".to_string(), 1.0),
|
||||
("yield_curve_1y".to_string(), 0.02),
|
||||
("total_shares".to_string(), 123.0),
|
||||
("stock_connect_north_bound".to_string(), 1.0),
|
||||
("industry_citics_l1".to_string(), 10.0),
|
||||
("fundamental_net_profit".to_string(), 99.0),
|
||||
("financial_revenue".to_string(), 188.0),
|
||||
("pit_financial_eps".to_string(), 0.88),
|
||||
("current_performance_roe".to_string(), 12.0),
|
||||
("amount".to_string(), 12_345_678.0),
|
||||
("custom_alpha".into(), 7.0),
|
||||
("margin_all".into(), 1.0),
|
||||
("yield_curve_1y".into(), 0.02),
|
||||
("total_shares".into(), 123.0),
|
||||
("stock_connect_north_bound".into(), 1.0),
|
||||
("industry_citics_l1".into(), 10.0),
|
||||
("fundamental_net_profit".into(), 99.0),
|
||||
("financial_revenue".into(), 188.0),
|
||||
("pit_financial_eps".into(), 0.88),
|
||||
("current_performance_roe".into(), 12.0),
|
||||
("amount".into(), 12_345_678.0),
|
||||
]),
|
||||
}],
|
||||
vec![CandidateEligibility {
|
||||
@@ -22835,8 +22836,8 @@ mod tests {
|
||||
turnover_ratio: Some(1.0),
|
||||
effective_turnover_ratio: Some(1.0),
|
||||
extra_factors: BTreeMap::from([
|
||||
("Mixed_Factor".to_string(), index as f64 + 5.0),
|
||||
("adjustment_factor_backward1".to_string(), 1.0),
|
||||
("Mixed_Factor".into(), index as f64 + 5.0),
|
||||
("adjustment_factor_backward1".into(), 1.0),
|
||||
]),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
@@ -22989,7 +22990,7 @@ mod tests {
|
||||
pe_ttm: 8.0,
|
||||
turnover_ratio: Some(1.0),
|
||||
effective_turnover_ratio: Some(1.0),
|
||||
extra_factors: BTreeMap::from([("adjustment_factor_backward1".to_string(), 1.0)]),
|
||||
extra_factors: BTreeMap::from([("adjustment_factor_backward1".into(), 1.0)]),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let candidate_rows = dates
|
||||
@@ -25352,7 +25353,7 @@ mod tests {
|
||||
pe_ttm: 8.0,
|
||||
turnover_ratio: Some(1.0),
|
||||
effective_turnover_ratio: Some(1.0),
|
||||
extra_factors: BTreeMap::from([("adjustment_factor_backward1".to_string(), 1.0)]),
|
||||
extra_factors: BTreeMap::from([("adjustment_factor_backward1".into(), 1.0)]),
|
||||
})
|
||||
.chain(symbols.iter().map(|symbol| DailyFactorSnapshot {
|
||||
date,
|
||||
@@ -25368,7 +25369,7 @@ mod tests {
|
||||
pe_ttm: 8.0,
|
||||
turnover_ratio: Some(1.0),
|
||||
effective_turnover_ratio: Some(1.0),
|
||||
extra_factors: BTreeMap::from([("adjustment_factor_backward1".to_string(), 1.0)]),
|
||||
extra_factors: BTreeMap::from([("adjustment_factor_backward1".into(), 1.0)]),
|
||||
}))
|
||||
.collect(),
|
||||
symbols
|
||||
@@ -26020,7 +26021,7 @@ mod tests {
|
||||
pe_ttm: 8.0,
|
||||
turnover_ratio: Some(1.0),
|
||||
effective_turnover_ratio: Some(1.0),
|
||||
extra_factors: BTreeMap::from([("adjustment_factor_backward1".to_string(), 1.0)]),
|
||||
extra_factors: BTreeMap::from([("adjustment_factor_backward1".into(), 1.0)]),
|
||||
};
|
||||
let data = DataSet::from_components(
|
||||
vec![Instrument {
|
||||
@@ -29884,7 +29885,7 @@ mod tests {
|
||||
let date = dates[5];
|
||||
let symbol = "300001.SZ";
|
||||
let mut extra_factors = BTreeMap::new();
|
||||
extra_factors.insert("adjustment_factor_backward1".to_string(), 1.0);
|
||||
extra_factors.insert("adjustment_factor_backward1".into(), 1.0);
|
||||
let data = DataSet::from_components(
|
||||
vec![Instrument {
|
||||
symbol: symbol.to_string(),
|
||||
@@ -29934,7 +29935,7 @@ mod tests {
|
||||
extra_factors: if factor_date == date {
|
||||
extra_factors.clone()
|
||||
} else {
|
||||
BTreeMap::from([("adjustment_factor_backward1".to_string(), 1.0)])
|
||||
BTreeMap::from([("adjustment_factor_backward1".into(), 1.0)])
|
||||
},
|
||||
})
|
||||
.collect(),
|
||||
@@ -30088,9 +30089,9 @@ mod tests {
|
||||
turnover_ratio: None,
|
||||
effective_turnover_ratio: None,
|
||||
extra_factors: BTreeMap::from([
|
||||
("adjustment_factor_backward1".to_string(), 1.0),
|
||||
("ma5_current_back_adjusted_close".to_string(), current_close),
|
||||
("avg_volume5_current".to_string(), current_volume),
|
||||
("adjustment_factor_backward1".into(), 1.0),
|
||||
("ma5_current_back_adjusted_close".into(), current_close),
|
||||
("avg_volume5_current".into(), current_volume),
|
||||
]),
|
||||
},
|
||||
)
|
||||
@@ -30239,7 +30240,7 @@ mod tests {
|
||||
pe_ttm: 0.0,
|
||||
turnover_ratio: None,
|
||||
effective_turnover_ratio: None,
|
||||
extra_factors: BTreeMap::from([("ma5_prev_close".to_string(), 10.0)]),
|
||||
extra_factors: BTreeMap::from([("ma5_prev_close".into(), 10.0)]),
|
||||
}],
|
||||
vec![CandidateEligibility {
|
||||
date: current,
|
||||
@@ -32539,10 +32540,7 @@ let target_exposure = csi_ready ? dynamic_exposure : 0.0;
|
||||
pe_ttm: 8.0,
|
||||
turnover_ratio: Some(22.0),
|
||||
effective_turnover_ratio: Some(18.0),
|
||||
extra_factors: BTreeMap::from([(
|
||||
"adjustment_factor_backward1".to_string(),
|
||||
1.0,
|
||||
)]),
|
||||
extra_factors: BTreeMap::from([("adjustment_factor_backward1".into(), 1.0)]),
|
||||
})
|
||||
.collect(),
|
||||
dates
|
||||
@@ -32755,7 +32753,7 @@ let target_exposure = csi_ready ? dynamic_exposure : 0.0;
|
||||
extra_factors: if *symbol == "000002.SZ" {
|
||||
BTreeMap::new()
|
||||
} else {
|
||||
BTreeMap::from([("model_score".to_string(), 4.0 - index as f64)])
|
||||
BTreeMap::from([("model_score".into(), 4.0 - index as f64)])
|
||||
},
|
||||
})
|
||||
.collect(),
|
||||
|
||||
@@ -10,11 +10,11 @@ use fidc_core::{
|
||||
ChinaEquityRuleHooks, DailyFactorSnapshot, DailyMarketSnapshot, DataSet, ExecutionQuoteRequest,
|
||||
FuturesAccountState, FuturesCommissionType, FuturesContractSpec, FuturesDirection,
|
||||
FuturesOrderIntent, FuturesPositionEffect, FuturesTradingParameter, FuturesValidationConfig,
|
||||
Instrument, IntradayExecutionQuote, IntradayOrderBookDepthLevel, MatchingType, OpenOrderView,
|
||||
OrderIntent, OrderSide, OrderStatus, PlatformExprStrategy, PlatformExprStrategyConfig,
|
||||
PlatformTradeAction, PortfolioState, PriceField, ProcessEvent, ProcessEventBus,
|
||||
ProcessEventKind, ScheduleRule, ScheduleStage, ScheduleTimeRule, Strategy, StrategyContext,
|
||||
StrategyDecision,
|
||||
Instrument, IntradayExecutionQuote, IntradayOrderBookDepthLevel, MatchingType,
|
||||
NumericFactorMap, OpenOrderView, OrderIntent, OrderSide, OrderStatus, PlatformExprStrategy,
|
||||
PlatformExprStrategyConfig, PlatformTradeAction, PortfolioState, PriceField, ProcessEvent,
|
||||
ProcessEventBus, ProcessEventKind, ScheduleRule, ScheduleStage, ScheduleTimeRule, Strategy,
|
||||
StrategyContext, StrategyDecision,
|
||||
};
|
||||
|
||||
fn d(year: i32, month: u32, day: u32) -> NaiveDate {
|
||||
@@ -133,7 +133,7 @@ fn market_row(date: NaiveDate, symbol: &str, open: f64, close: f64) -> DailyMark
|
||||
fn factor_row(
|
||||
date: NaiveDate,
|
||||
symbol: &str,
|
||||
extra_factors: BTreeMap<String, f64>,
|
||||
extra_factors: NumericFactorMap,
|
||||
) -> DailyFactorSnapshot {
|
||||
DailyFactorSnapshot {
|
||||
date,
|
||||
@@ -209,26 +209,26 @@ fn two_day_futures_data() -> DataSet {
|
||||
d1,
|
||||
"000001.SZ",
|
||||
BTreeMap::from([
|
||||
("custom_alpha".to_string(), 7.0),
|
||||
("margin_all".to_string(), 1.0),
|
||||
("yield_curve_1y".to_string(), 0.02),
|
||||
("total_shares".to_string(), 123.0),
|
||||
("stock_connect_north_bound".to_string(), 1.0),
|
||||
("industry_citics_l1".to_string(), 10.0),
|
||||
("fundamental_net_profit".to_string(), 99.0),
|
||||
("custom_alpha".into(), 7.0),
|
||||
("margin_all".into(), 1.0),
|
||||
("yield_curve_1y".into(), 0.02),
|
||||
("total_shares".into(), 123.0),
|
||||
("stock_connect_north_bound".into(), 1.0),
|
||||
("industry_citics_l1".into(), 10.0),
|
||||
("fundamental_net_profit".into(), 99.0),
|
||||
]),
|
||||
),
|
||||
factor_row(
|
||||
d2,
|
||||
"000001.SZ",
|
||||
BTreeMap::from([
|
||||
("custom_alpha".to_string(), 8.0),
|
||||
("margin_all".to_string(), 1.0),
|
||||
("yield_curve_1y".to_string(), 0.021),
|
||||
("total_shares".to_string(), 124.0),
|
||||
("stock_connect_north_bound".to_string(), 1.0),
|
||||
("industry_citics_l1".to_string(), 10.0),
|
||||
("fundamental_net_profit".to_string(), 101.0),
|
||||
("custom_alpha".into(), 8.0),
|
||||
("margin_all".into(), 1.0),
|
||||
("yield_curve_1y".into(), 0.021),
|
||||
("total_shares".into(), 124.0),
|
||||
("stock_connect_north_bound".into(), 1.0),
|
||||
("industry_citics_l1".into(), 10.0),
|
||||
("fundamental_net_profit".into(), 101.0),
|
||||
]),
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user