移除决策日市值二次推算
This commit is contained in:
@@ -448,49 +448,12 @@ pub struct EligibleUniverseSnapshot {
|
|||||||
pub free_float_cap_bn: f64,
|
pub free_float_cap_bn: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decision_adjusted_cap_bn(
|
pub fn decision_market_cap_bn(factor: &DailyFactorSnapshot) -> f64 {
|
||||||
factor_date: NaiveDate,
|
factor.market_cap_bn
|
||||||
raw_cap_bn: f64,
|
|
||||||
market: &DailyMarketSnapshot,
|
|
||||||
) -> f64 {
|
|
||||||
if !raw_cap_bn.is_finite() || raw_cap_bn <= 0.0 {
|
|
||||||
return f64::NAN;
|
|
||||||
}
|
|
||||||
if factor_date != market.date {
|
|
||||||
return raw_cap_bn;
|
|
||||||
}
|
|
||||||
if !market.close.is_finite()
|
|
||||||
|| market.close <= 0.0
|
|
||||||
|| !market.prev_close.is_finite()
|
|
||||||
|| market.prev_close <= 0.0
|
|
||||||
{
|
|
||||||
return f64::NAN;
|
|
||||||
}
|
|
||||||
raw_cap_bn * market.prev_close / market.close
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn factor_market_cap_is_decision_adjusted(factor: &DailyFactorSnapshot) -> bool {
|
pub fn decision_free_float_cap_bn(factor: &DailyFactorSnapshot) -> f64 {
|
||||||
factor
|
factor.free_float_cap_bn
|
||||||
.extra_factors
|
|
||||||
.get("__market_cap_decision_adjusted")
|
|
||||||
.is_some_and(|value| value.is_finite() && *value > 0.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn decision_market_cap_bn(factor: &DailyFactorSnapshot, market: &DailyMarketSnapshot) -> f64 {
|
|
||||||
if factor_market_cap_is_decision_adjusted(factor) {
|
|
||||||
return factor.market_cap_bn;
|
|
||||||
}
|
|
||||||
decision_adjusted_cap_bn(factor.date, factor.market_cap_bn, market)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn decision_free_float_cap_bn(
|
|
||||||
factor: &DailyFactorSnapshot,
|
|
||||||
market: &DailyMarketSnapshot,
|
|
||||||
) -> f64 {
|
|
||||||
if factor_market_cap_is_decision_adjusted(factor) {
|
|
||||||
return factor.free_float_cap_bn;
|
|
||||||
}
|
|
||||||
decision_adjusted_cap_bn(factor.date, factor.free_float_cap_bn, market)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -3048,20 +3011,21 @@ fn build_fundamental_universe_for_date(
|
|||||||
return rows;
|
return rows;
|
||||||
};
|
};
|
||||||
for factor in factors {
|
for factor in factors {
|
||||||
let Some(market) = market_by_date
|
if market_by_date
|
||||||
.get(&date)
|
.get(&date)
|
||||||
.and_then(|rows| find_arc_by_symbol(rows, &factor.symbol, |row| row.symbol.as_str()))
|
.and_then(|rows| find_arc_by_symbol(rows, &factor.symbol, |row| row.symbol.as_str()))
|
||||||
else {
|
.is_none()
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
};
|
}
|
||||||
let market_cap_bn = decision_market_cap_bn(factor, market);
|
let market_cap_bn = decision_market_cap_bn(factor);
|
||||||
if market_cap_bn <= 0.0 || !market_cap_bn.is_finite() {
|
if market_cap_bn <= 0.0 || !market_cap_bn.is_finite() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
rows.push(EligibleUniverseSnapshot {
|
rows.push(EligibleUniverseSnapshot {
|
||||||
symbol: factor.symbol.clone(),
|
symbol: factor.symbol.clone(),
|
||||||
market_cap_bn,
|
market_cap_bn,
|
||||||
free_float_cap_bn: decision_free_float_cap_bn(factor, market),
|
free_float_cap_bn: decision_free_float_cap_bn(factor),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
rows.sort_by(|left, right| {
|
rows.sort_by(|left, right| {
|
||||||
@@ -3136,11 +3100,11 @@ fn build_eligible_universe_for_date_from_factors(
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let market_cap_bn = decision_market_cap_bn(factor, market);
|
let market_cap_bn = decision_market_cap_bn(factor);
|
||||||
if market_cap_bn <= 0.0 || !market_cap_bn.is_finite() {
|
if market_cap_bn <= 0.0 || !market_cap_bn.is_finite() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let free_float_cap_bn = decision_free_float_cap_bn(factor, market);
|
let free_float_cap_bn = decision_free_float_cap_bn(factor);
|
||||||
rows.push(EligibleUniverseSnapshot {
|
rows.push(EligibleUniverseSnapshot {
|
||||||
symbol: factor.symbol.clone(),
|
symbol: factor.symbol.clone(),
|
||||||
market_cap_bn,
|
market_cap_bn,
|
||||||
@@ -3444,11 +3408,11 @@ mod tests {
|
|||||||
|
|
||||||
let rows = data.eligible_universe_on(date);
|
let rows = data.eligible_universe_on(date);
|
||||||
assert_eq!(rows.len(), 2);
|
assert_eq!(rows.len(), 2);
|
||||||
assert_eq!(rows[0].symbol, "000001.SZ");
|
assert_eq!(rows[0].symbol, "000002.SZ");
|
||||||
assert!((rows[0].market_cap_bn - 6.0).abs() < 1e-9);
|
assert!((rows[0].market_cap_bn - 10.0).abs() < 1e-9);
|
||||||
assert!((rows[0].free_float_cap_bn - 2.0).abs() < 1e-9);
|
assert_eq!(rows[1].symbol, "000001.SZ");
|
||||||
assert_eq!(rows[1].symbol, "000002.SZ");
|
assert!((rows[1].market_cap_bn - 12.0).abs() < 1e-9);
|
||||||
assert!((rows[1].market_cap_bn - 10.0).abs() < 1e-9);
|
assert!((rows[1].free_float_cap_bn - 4.0).abs() < 1e-9);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -3621,33 +3585,8 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn decision_market_cap_keeps_pre_adjusted_factor() {
|
fn decision_market_cap_uses_factor_date_snapshot_without_price_reconstruction() {
|
||||||
let date = NaiveDate::parse_from_str("2025-01-06", "%Y-%m-%d").unwrap();
|
let date = NaiveDate::parse_from_str("2025-01-06", "%Y-%m-%d").unwrap();
|
||||||
let market = DailyMarketSnapshot {
|
|
||||||
date,
|
|
||||||
symbol: "000001.SZ".to_string(),
|
|
||||||
timestamp: Some("2025-01-06 10:18:00".to_string()),
|
|
||||||
day_open: 10.0,
|
|
||||||
open: 10.0,
|
|
||||||
high: 20.0,
|
|
||||||
low: 10.0,
|
|
||||||
close: 20.0,
|
|
||||||
last_price: 10.0,
|
|
||||||
bid1: 10.0,
|
|
||||||
ask1: 10.0,
|
|
||||||
prev_close: 10.0,
|
|
||||||
volume: 100_000,
|
|
||||||
minute_volume: 1_000,
|
|
||||||
bid1_volume: 1_000,
|
|
||||||
ask1_volume: 1_000,
|
|
||||||
trading_phase: Some("continuous".to_string()),
|
|
||||||
paused: false,
|
|
||||||
upper_limit: 11.0,
|
|
||||||
lower_limit: 9.0,
|
|
||||||
price_tick: 0.01,
|
|
||||||
};
|
|
||||||
let mut extra_factors = BTreeMap::new();
|
|
||||||
extra_factors.insert("__market_cap_decision_adjusted".to_string(), 1.0);
|
|
||||||
let factor = DailyFactorSnapshot {
|
let factor = DailyFactorSnapshot {
|
||||||
date,
|
date,
|
||||||
symbol: "000001.SZ".to_string(),
|
symbol: "000001.SZ".to_string(),
|
||||||
@@ -3656,11 +3595,11 @@ mod tests {
|
|||||||
pe_ttm: 10.0,
|
pe_ttm: 10.0,
|
||||||
turnover_ratio: Some(1.0),
|
turnover_ratio: Some(1.0),
|
||||||
effective_turnover_ratio: Some(1.0),
|
effective_turnover_ratio: Some(1.0),
|
||||||
extra_factors,
|
extra_factors: BTreeMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!((decision_market_cap_bn(&factor, &market) - 12.0).abs() < 1e-9);
|
assert!((decision_market_cap_bn(&factor) - 12.0).abs() < 1e-9);
|
||||||
assert!((decision_free_float_cap_bn(&factor, &market) - 4.0).abs() < 1e-9);
|
assert!((decision_free_float_cap_bn(&factor) - 4.0).abs() < 1e-9);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -3123,8 +3123,8 @@ impl PlatformExprStrategy {
|
|||||||
} else {
|
} else {
|
||||||
factor.extra_factors.get("amount").copied().unwrap_or(0.0)
|
factor.extra_factors.get("amount").copied().unwrap_or(0.0)
|
||||||
};
|
};
|
||||||
let market_cap_bn = decision_market_cap_bn(factor, market);
|
let market_cap_bn = decision_market_cap_bn(factor);
|
||||||
let free_float_cap_bn = decision_free_float_cap_bn(factor, market);
|
let free_float_cap_bn = decision_free_float_cap_bn(factor);
|
||||||
let market_cap = Self::market_cap_storage_to_strategy_unit(market_cap_bn);
|
let market_cap = Self::market_cap_storage_to_strategy_unit(market_cap_bn);
|
||||||
let free_float_cap = Self::market_cap_storage_to_strategy_unit(free_float_cap_bn);
|
let free_float_cap = Self::market_cap_storage_to_strategy_unit(free_float_cap_bn);
|
||||||
let expression_high = if intraday_same_day_factor {
|
let expression_high = if intraday_same_day_factor {
|
||||||
@@ -6888,11 +6888,11 @@ impl PlatformExprStrategy {
|
|||||||
if !self.stock_passes_universe_exclude(candidate, market) {
|
if !self.stock_passes_universe_exclude(candidate, market) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let market_cap_bn = decision_market_cap_bn(factor, market);
|
let market_cap_bn = decision_market_cap_bn(factor);
|
||||||
if market_cap_bn <= 0.0 || !market_cap_bn.is_finite() {
|
if market_cap_bn <= 0.0 || !market_cap_bn.is_finite() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let free_float_cap_bn = decision_free_float_cap_bn(factor, market);
|
let free_float_cap_bn = decision_free_float_cap_bn(factor);
|
||||||
rows.push(EligibleUniverseSnapshot {
|
rows.push(EligibleUniverseSnapshot {
|
||||||
symbol: factor.symbol.clone(),
|
symbol: factor.symbol.clone(),
|
||||||
market_cap_bn,
|
market_cap_bn,
|
||||||
@@ -14683,16 +14683,16 @@ mod tests {
|
|||||||
|
|
||||||
let universe = strategy.selectable_universe_on(&ctx, date, date);
|
let universe = strategy.selectable_universe_on(&ctx, date, date);
|
||||||
assert_eq!(universe.len(), 1);
|
assert_eq!(universe.len(), 1);
|
||||||
assert!((universe[0].market_cap_bn - 9.0).abs() < 1e-9);
|
assert!((universe[0].market_cap_bn - 18.0).abs() < 1e-9);
|
||||||
assert!((universe[0].free_float_cap_bn - 4.5).abs() < 1e-9);
|
assert!((universe[0].free_float_cap_bn - 9.0).abs() < 1e-9);
|
||||||
|
|
||||||
let stock = strategy
|
let stock = strategy
|
||||||
.stock_state_with_factor_date(&ctx, date, date, symbol)
|
.stock_state_with_factor_date(&ctx, date, date, symbol)
|
||||||
.expect("stock state");
|
.expect("stock state");
|
||||||
assert!((stock.market_cap - 9.0).abs() < 1e-9);
|
assert!((stock.market_cap - 18.0).abs() < 1e-9);
|
||||||
assert!((stock.market_cap_bn - 9.0).abs() < 1e-9);
|
assert!((stock.market_cap_bn - 18.0).abs() < 1e-9);
|
||||||
assert!((stock.free_float_cap - 4.5).abs() < 1e-9);
|
assert!((stock.free_float_cap - 9.0).abs() < 1e-9);
|
||||||
assert!((stock.free_float_cap_bn - 4.5).abs() < 1e-9);
|
assert!((stock.free_float_cap_bn - 9.0).abs() < 1e-9);
|
||||||
assert_eq!(stock.close, 10.0);
|
assert_eq!(stock.close, 10.0);
|
||||||
assert!(stock.high.is_nan());
|
assert!(stock.high.is_nan());
|
||||||
assert!(stock.low.is_nan());
|
assert!(stock.low.is_nan());
|
||||||
@@ -14702,7 +14702,7 @@ mod tests {
|
|||||||
let (selected, _, _) = strategy
|
let (selected, _, _) = strategy
|
||||||
.select_symbols(&ctx, date, date, date, &day, 10.0, 20.0, 1)
|
.select_symbols(&ctx, date, date, date, &day, 10.0, 20.0, 1)
|
||||||
.expect("selection");
|
.expect("selection");
|
||||||
assert!(selected.is_empty());
|
assert_eq!(selected, vec![symbol.to_string()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -25484,8 +25484,9 @@ mod tests {
|
|||||||
.order_intents
|
.order_intents
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|intent| match intent {
|
.filter_map(|intent| match intent {
|
||||||
OrderIntent::Value { value, reason, .. }
|
OrderIntent::Value { value, reason, .. } if reason == "periodic_rebalance_buy" => {
|
||||||
if reason == "periodic_rebalance_buy" => Some(*value),
|
Some(*value)
|
||||||
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|||||||
Reference in New Issue
Block a user