diff --git a/crates/fidc-core/src/strategy.rs b/crates/fidc-core/src/strategy.rs index 1af2fb6..f302f2b 100644 --- a/crates/fidc-core/src/strategy.rs +++ b/crates/fidc-core/src/strategy.rs @@ -1570,13 +1570,13 @@ impl OmniMicroCapConfig { strategy_name: "aiquant-v1.0.4".to_string(), refresh_rate: 120, stocknum: 5, - xs: 3.0 / 500.0, + xs: 4.0 / 500.0, base_index_level: 2000.0, base_cap_floor: 7.0, - cap_span: 25.0, - padding_ratio: 0.5, - min_padding: 12.5, - max_padding: 30.0, + cap_span: 10.0, + padding_ratio: 1.2, + min_padding: 29.5, + max_padding: 50.0, benchmark_signal_symbol: "000852.SH".to_string(), benchmark_short_ma_days: 5, benchmark_long_ma_days: 20, @@ -2141,7 +2141,8 @@ impl OmniMicroCapStrategy { &self, ctx: &StrategyContext<'_>, date: NaiveDate, - ) -> Result<(f64, f64, f64, f64), BacktestError> { + ) -> Result<(f64, f64, f64, f64, f64), BacktestError> { + // 当前交易日的指数价格(用于MA计算和仓位控制) let current_level = ctx .data .market_decision_close(date, &self.config.benchmark_signal_symbol) @@ -2150,6 +2151,16 @@ impl OmniMicroCapStrategy { symbol: self.config.benchmark_signal_symbol.clone(), field: "decision_close", })?; + + // 前一交易日的指数价格(用于市值区间计算,模拟实盘场景) + let prev_level = if let Some(prev_date) = ctx.data.previous_trading_date(date, 1) { + ctx.data + .market_decision_close(prev_date, &self.config.benchmark_signal_symbol) + .unwrap_or(current_level) + } else { + current_level + }; + let ma_short = ctx .data .market_decision_close_moving_average( @@ -2181,7 +2192,7 @@ impl OmniMicroCapStrategy { } else { 1.0 }; - Ok((current_level, ma_short, ma_long, trading_ratio)) + Ok((current_level, prev_level, ma_short, ma_long, trading_ratio)) } fn market_cap_band(&self, index_level: f64) -> (f64, f64) { @@ -2233,14 +2244,25 @@ impl OmniMicroCapStrategy { // MA filter: ma_short > ma_mid * rsi_rate && ma_mid * rsi_rate > ma_long let ma_pass = ma_short > ma_mid * self.config.rsi_rate && ma_mid * self.config.rsi_rate > ma_long; - // Debug logging for first few stocks - static DEBUG_COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0); - let count = DEBUG_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed); - if count < 10 { - eprintln!("[DEBUG MA] {} date={} ma5={:.4} ma10={:.4} ma30={:.4} rsi_rate={:.6} pass={} (ma5 > ma10*rsi={:.4}? {} && ma10*rsi > ma30={:.4}? {})", - symbol, date, ma_short, ma_mid, ma_long, self.config.rsi_rate, ma_pass, - ma_mid * self.config.rsi_rate, ma_short > ma_mid * self.config.rsi_rate, - ma_long, ma_mid * self.config.rsi_rate > ma_long); + // Debug logging for ALL stocks on first decision date + static DEBUG_DATE: std::sync::Mutex> = std::sync::Mutex::new(None); + let mut debug_date = DEBUG_DATE.lock().unwrap(); + let should_debug = if let Some(d) = *debug_date { + d == date + } else { + *debug_date = Some(date); + true + }; + + if should_debug { + eprintln!("[MA_FILTER] {} cap={:.2} ma5={:.4} ma10={:.4} ma30={:.4} ma10*rsi={:.4} pass={} ({}>{:.4}? {} && {:.4}>{}? {})", + symbol, + ctx.data.market_decision_close(date, symbol).unwrap_or(0.0), + ma_short, ma_mid, ma_long, + ma_mid * self.config.rsi_rate, + ma_pass, + ma_short, ma_mid * self.config.rsi_rate, ma_short > ma_mid * self.config.rsi_rate, + ma_mid * self.config.rsi_rate, ma_long, ma_mid * self.config.rsi_rate > ma_long); } if !ma_pass { @@ -2640,7 +2662,7 @@ impl Strategy for OmniMicroCapStrategy { }); } - let (index_level, ma_short, ma_long, trading_ratio) = match self.trading_ratio(ctx, date) { + let (index_level, prev_index_level, ma_short, ma_long, trading_ratio) = match self.trading_ratio(ctx, date) { Ok(value) => value, Err(BacktestError::Execution(message)) if message.contains("insufficient benchmark") => @@ -2658,7 +2680,10 @@ impl Strategy for OmniMicroCapStrategy { } Err(err) => return Err(err), }; - let (band_low, band_high) = self.market_cap_band(index_level); + // 使用前一交易日的指数价格计算市值区间(模拟实盘场景) + let (band_low, band_high) = self.market_cap_band(prev_index_level); + eprintln!("[DEBUG] date={} current_index={:.2} prev_index={:.2} band=[{:.0}, {:.0}]", + date, index_level, prev_index_level, band_low, band_high); let (stock_list, selection_notes) = self.select_symbols(ctx, date, band_low, band_high)?; let periodic_rebalance = ctx.decision_index % self.config.refresh_rate == 0; let mut projected = ctx.portfolio.clone();