对齐模型轮动目标调仓语义

This commit is contained in:
boris
2026-07-12 00:31:00 +08:00
parent 942ba84ca5
commit 30a4071ee0
2 changed files with 97 additions and 0 deletions
@@ -229,6 +229,8 @@ pub struct PlatformExprStrategyConfig {
pub signal_rebalance_dates: BTreeSet<NaiveDate>, pub signal_rebalance_dates: BTreeSet<NaiveDate>,
pub rotation_enabled: bool, pub rotation_enabled: bool,
pub daily_top_up_enabled: bool, pub daily_top_up_enabled: bool,
pub daily_position_target_adjust_enabled: bool,
pub rebalance_existing_positions: bool,
pub retry_empty_rebalance: bool, pub retry_empty_rebalance: bool,
pub calendar_rebalance_interval: bool, pub calendar_rebalance_interval: bool,
pub max_holding_days: Option<i64>, pub max_holding_days: Option<i64>,
@@ -299,6 +301,8 @@ fn band_low(index_close) {
signal_rebalance_dates: BTreeSet::new(), signal_rebalance_dates: BTreeSet::new(),
rotation_enabled: true, rotation_enabled: true,
daily_top_up_enabled: false, daily_top_up_enabled: false,
daily_position_target_adjust_enabled: true,
rebalance_existing_positions: false,
retry_empty_rebalance: false, retry_empty_rebalance: false,
calendar_rebalance_interval: false, calendar_rebalance_interval: false,
max_holding_days: None, max_holding_days: None,
@@ -8881,6 +8885,7 @@ impl Strategy for PlatformExprStrategy {
if self.config.aiquant_transaction_cost if self.config.aiquant_transaction_cost
&& self.config.rotation_enabled && self.config.rotation_enabled
&& self.config.daily_position_target_adjust_enabled
&& trading_ratio > 0.0 && trading_ratio > 0.0
&& trading_ratio < 1.0 && trading_ratio < 1.0
&& selection_limit > 0 && selection_limit > 0
@@ -9370,7 +9375,44 @@ impl Strategy for PlatformExprStrategy {
&& !same_day_sold_symbols.contains(symbol) && !same_day_sold_symbols.contains(symbol)
&& !pending_full_close_symbols.contains(symbol); && !pending_full_close_symbols.contains(symbol);
if projected.positions().contains_key(symbol) && !released_exit_position { if projected.positions().contains_key(symbol) && !released_exit_position {
if self.config.aiquant_transaction_cost
&& !self.config.rebalance_existing_positions
{
continue;
}
if self.config.aiquant_transaction_cost { if self.config.aiquant_transaction_cost {
let target_value = target_budget / selection_limit as f64 * stock_scale;
let before_qty = projected
.position(symbol)
.map(|position| position.quantity)
.unwrap_or(0);
let mut trial_projected = projected.clone();
let mut trial_execution_state = projected_execution_state.clone();
self.project_target_value(
ctx,
&mut trial_projected,
projection_date,
symbol,
target_value,
&mut trial_execution_state,
);
let after_qty = trial_projected
.position(symbol)
.map(|position| position.quantity)
.unwrap_or(0);
if after_qty != before_qty {
projected = trial_projected;
projected_execution_state = trial_execution_state;
order_intents.push(OrderIntent::TargetValue {
symbol: symbol.clone(),
target_value,
reason: "periodic_rebalance_target_adjust".to_string(),
});
if after_qty > before_qty {
intraday_attempted_buys.insert(symbol.clone());
}
aiquant_available_cash = projected.cash().max(0.0);
}
continue; continue;
} }
if same_day_sold_symbols.contains(symbol) if same_day_sold_symbols.contains(symbol)
@@ -25690,6 +25732,47 @@ mod tests {
&& reason == "periodic_rebalance_buy" && reason == "periodic_rebalance_buy"
&& (*value - 499_000.0).abs() < 1e-6 && (*value - 499_000.0).abs() < 1e-6
)); ));
let mut target_value_cfg = PlatformExprStrategyConfig::microcap_rotation();
target_value_cfg.signal_symbol = symbol.to_string();
target_value_cfg.refresh_rate = 20;
target_value_cfg.max_positions = 1;
target_value_cfg.benchmark_short_ma_days = 1;
target_value_cfg.benchmark_long_ma_days = 1;
target_value_cfg.market_cap_lower_expr = "0".to_string();
target_value_cfg.market_cap_upper_expr = "100".to_string();
target_value_cfg.selection_limit_expr = "1".to_string();
target_value_cfg.stock_filter_expr = "close > 0".to_string();
target_value_cfg.take_profit_expr.clear();
target_value_cfg.stop_loss_expr.clear();
target_value_cfg.aiquant_transaction_cost = true;
target_value_cfg.rebalance_existing_positions = true;
target_value_cfg.intraday_execution_time =
Some(NaiveTime::from_hms_opt(10, 18, 0).unwrap());
let mut target_value_strategy = PlatformExprStrategy::new(target_value_cfg);
target_value_strategy.rebalance_day_counter = 20;
let target_value_decision = target_value_strategy
.on_day(&ctx)
.expect("target-value platform decision");
assert!(
target_value_decision
.order_intents
.iter()
.any(|intent| matches!(
intent,
OrderIntent::TargetValue {
symbol: intent_symbol,
target_value,
reason,
} if intent_symbol == symbol
&& reason == "periodic_rebalance_target_adjust"
&& (*target_value - 500_000.0).abs() < 1e-6
)),
"{:?}",
target_value_decision.order_intents
);
} }
#[test] #[test]
@@ -691,6 +691,10 @@ pub struct StrategyExpressionTradingConfig {
#[serde(default)] #[serde(default)]
pub daily_top_up: Option<bool>, pub daily_top_up: Option<bool>,
#[serde(default)] #[serde(default)]
pub daily_position_target_adjust: Option<bool>,
#[serde(default)]
pub rebalance_existing_positions: Option<bool>,
#[serde(default)]
pub retry_empty_rebalance: Option<bool>, pub retry_empty_rebalance: Option<bool>,
#[serde(default)] #[serde(default)]
pub weak_market_shrink_overweight_threshold: Option<f64>, pub weak_market_shrink_overweight_threshold: Option<f64>,
@@ -1628,6 +1632,12 @@ pub fn platform_expr_config_from_spec(
if let Some(enabled) = trading.daily_top_up { if let Some(enabled) = trading.daily_top_up {
cfg.daily_top_up_enabled = enabled; cfg.daily_top_up_enabled = enabled;
} }
if let Some(enabled) = trading.daily_position_target_adjust {
cfg.daily_position_target_adjust_enabled = enabled;
}
if let Some(enabled) = trading.rebalance_existing_positions {
cfg.rebalance_existing_positions = enabled;
}
if let Some(enabled) = trading.retry_empty_rebalance { if let Some(enabled) = trading.retry_empty_rebalance {
cfg.retry_empty_rebalance = enabled; cfg.retry_empty_rebalance = enabled;
} }
@@ -2945,6 +2955,8 @@ mod tests {
"runtimeExpressions": { "runtimeExpressions": {
"trading": { "trading": {
"dailyTopUp": false, "dailyTopUp": false,
"dailyPositionTargetAdjust": false,
"rebalanceExistingPositions": true,
"retryEmptyRebalance": false "retryEmptyRebalance": false
} }
} }
@@ -2953,6 +2965,8 @@ mod tests {
let cfg = platform_expr_config_from_value("", "", &explicit_off).expect("config"); let cfg = platform_expr_config_from_value("", "", &explicit_off).expect("config");
assert!(!cfg.daily_top_up_enabled); assert!(!cfg.daily_top_up_enabled);
assert!(!cfg.daily_position_target_adjust_enabled);
assert!(cfg.rebalance_existing_positions);
assert!(!cfg.retry_empty_rebalance); assert!(!cfg.retry_empty_rebalance);
} }