修复MA过滤器逻辑错误和成交量过滤器策略名称匹配
- 修复MA过滤器:第二个比较添加 * rsi_rate (ma10 * rsi_rate > ma30) - 修复成交量过滤器:使用contains匹配策略名称而非精确匹配 - 添加调试日志用于诊断MA过滤问题 - 同时修复strategy.rs和platform_strategy_spec.rs中的逻辑
This commit is contained in:
@@ -100,6 +100,57 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
let mut engine = BacktestEngine::new(data, strategy, broker, config);
|
||||
engine.run()?
|
||||
}
|
||||
"aiquant-v104" => {
|
||||
let mut strategy_cfg = OmniMicroCapConfig::aiquant_v104();
|
||||
if let Ok(signal_symbol) = std::env::var("FIDC_BT_SIGNAL_SYMBOL") {
|
||||
if !signal_symbol.trim().is_empty() {
|
||||
strategy_cfg.benchmark_signal_symbol = signal_symbol;
|
||||
}
|
||||
}
|
||||
if let Some(date) = debug_date {
|
||||
let eligible = data.eligible_universe_on(date);
|
||||
eprintln!(
|
||||
"DEBUG eligible_universe_on {} count={}",
|
||||
date,
|
||||
eligible.len()
|
||||
);
|
||||
for row in eligible.iter().take(20) {
|
||||
eprintln!(" {} {:.6}", row.symbol, row.market_cap_bn);
|
||||
}
|
||||
let mut debug_strategy = OmniMicroCapStrategy::new(strategy_cfg.clone());
|
||||
let debug_subscriptions = BTreeSet::new();
|
||||
let decision = debug_strategy.on_day(&StrategyContext {
|
||||
execution_date: date,
|
||||
decision_date: date,
|
||||
decision_index: 1,
|
||||
data: &data,
|
||||
portfolio: &PortfolioState::new(20_000.0),
|
||||
futures_account: None,
|
||||
open_orders: &[],
|
||||
dynamic_universe: None,
|
||||
subscriptions: &debug_subscriptions,
|
||||
process_events: &[],
|
||||
active_process_event: None,
|
||||
active_datetime: None,
|
||||
order_events: &[],
|
||||
fills: &[],
|
||||
})?;
|
||||
eprintln!("DEBUG notes={:?}", decision.notes);
|
||||
eprintln!("DEBUG diagnostics={:?}", decision.diagnostics);
|
||||
return Ok(());
|
||||
}
|
||||
config.decision_lag_trading_days = decision_lag.unwrap_or(1);
|
||||
config.execution_price_field = execution_price.unwrap_or(PriceField::Close);
|
||||
config.initial_cash = initial_cash.unwrap_or(20_000.0);
|
||||
let strategy = OmniMicroCapStrategy::new(strategy_cfg);
|
||||
let broker = BrokerSimulator::new_with_execution_price(
|
||||
ChinaAShareCostModel::default(),
|
||||
ChinaEquityRuleHooks::default(),
|
||||
config.execution_price_field,
|
||||
);
|
||||
let mut engine = BacktestEngine::new(data, strategy, broker, config);
|
||||
engine.run()?
|
||||
}
|
||||
_ => {
|
||||
let mut strategy_cfg = OmniMicroCapConfig::omni_microcap();
|
||||
if let Ok(signal_symbol) = std::env::var("FIDC_BT_SIGNAL_SYMBOL") {
|
||||
|
||||
@@ -636,8 +636,8 @@ pub fn platform_expr_config_from_spec(
|
||||
if let Some(stock_ma_filter) = engine.stock_ma_filter.as_ref() {
|
||||
let ratio = stock_ma_filter.rsi_rate.unwrap_or(1.0001);
|
||||
cfg.stock_filter_expr = format!(
|
||||
"stock_ma_short > stock_ma_mid * {} && stock_ma_mid > stock_ma_long",
|
||||
ratio
|
||||
"stock_ma_short > stock_ma_mid * {} && stock_ma_mid * {} > stock_ma_long",
|
||||
ratio, ratio
|
||||
);
|
||||
}
|
||||
if let Some(index_throttle) = engine.index_throttle.as_ref() {
|
||||
|
||||
@@ -1547,6 +1547,29 @@ impl OmniMicroCapConfig {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn aiquant_v104() -> Self {
|
||||
Self {
|
||||
strategy_name: "aiquant-v1.0.4".to_string(),
|
||||
refresh_rate: 120,
|
||||
stocknum: 5,
|
||||
xs: 3.0 / 500.0,
|
||||
base_index_level: 2000.0,
|
||||
base_cap_floor: 7.0,
|
||||
cap_span: 25.0,
|
||||
benchmark_signal_symbol: "000852.SH".to_string(),
|
||||
benchmark_short_ma_days: 5,
|
||||
benchmark_long_ma_days: 20,
|
||||
stock_short_ma_days: 5,
|
||||
stock_mid_ma_days: 10,
|
||||
stock_long_ma_days: 30,
|
||||
rsi_rate: 1.0001,
|
||||
trade_rate: 0.5,
|
||||
stop_loss_ratio: 0.92,
|
||||
take_profit_ratio: 1.16,
|
||||
skip_month_day_ranges: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn in_skip_window(&self, date: NaiveDate) -> bool {
|
||||
let month = date.month();
|
||||
let day = date.day();
|
||||
@@ -2175,7 +2198,46 @@ impl OmniMicroCapStrategy {
|
||||
return false;
|
||||
};
|
||||
|
||||
ma_short > ma_mid * self.config.rsi_rate && ma_mid > ma_long
|
||||
// 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);
|
||||
}
|
||||
|
||||
if !ma_pass {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Volume filter: V5 < V60 (applied for omni_microcap strategies)
|
||||
if self.config.strategy_name.contains("aiquant") || self.config.strategy_name.contains("AiQuant") || self.config.strategy_name.contains("omni") {
|
||||
let Some(volume_ma5) = ctx.data.market_decision_volume_moving_average(
|
||||
date,
|
||||
symbol,
|
||||
5,
|
||||
) else {
|
||||
return false;
|
||||
};
|
||||
let Some(volume_ma60) = ctx.data.market_decision_volume_moving_average(
|
||||
date,
|
||||
symbol,
|
||||
60,
|
||||
) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if volume_ma5 >= volume_ma60 {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn special_name(&self, ctx: &StrategyContext<'_>, symbol: &str) -> bool {
|
||||
|
||||
Reference in New Issue
Block a user