对齐模型轮动目标调仓语义
This commit is contained in:
@@ -229,6 +229,8 @@ pub struct PlatformExprStrategyConfig {
|
||||
pub signal_rebalance_dates: BTreeSet<NaiveDate>,
|
||||
pub rotation_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 calendar_rebalance_interval: bool,
|
||||
pub max_holding_days: Option<i64>,
|
||||
@@ -299,6 +301,8 @@ fn band_low(index_close) {
|
||||
signal_rebalance_dates: BTreeSet::new(),
|
||||
rotation_enabled: true,
|
||||
daily_top_up_enabled: false,
|
||||
daily_position_target_adjust_enabled: true,
|
||||
rebalance_existing_positions: false,
|
||||
retry_empty_rebalance: false,
|
||||
calendar_rebalance_interval: false,
|
||||
max_holding_days: None,
|
||||
@@ -8881,6 +8885,7 @@ impl Strategy for PlatformExprStrategy {
|
||||
|
||||
if self.config.aiquant_transaction_cost
|
||||
&& self.config.rotation_enabled
|
||||
&& self.config.daily_position_target_adjust_enabled
|
||||
&& trading_ratio > 0.0
|
||||
&& trading_ratio < 1.0
|
||||
&& selection_limit > 0
|
||||
@@ -9370,7 +9375,44 @@ impl Strategy for PlatformExprStrategy {
|
||||
&& !same_day_sold_symbols.contains(symbol)
|
||||
&& !pending_full_close_symbols.contains(symbol);
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
if same_day_sold_symbols.contains(symbol)
|
||||
@@ -25690,6 +25732,47 @@ mod tests {
|
||||
&& reason == "periodic_rebalance_buy"
|
||||
&& (*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]
|
||||
|
||||
@@ -691,6 +691,10 @@ pub struct StrategyExpressionTradingConfig {
|
||||
#[serde(default)]
|
||||
pub daily_top_up: Option<bool>,
|
||||
#[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>,
|
||||
#[serde(default)]
|
||||
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 {
|
||||
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 {
|
||||
cfg.retry_empty_rebalance = enabled;
|
||||
}
|
||||
@@ -2945,6 +2955,8 @@ mod tests {
|
||||
"runtimeExpressions": {
|
||||
"trading": {
|
||||
"dailyTopUp": false,
|
||||
"dailyPositionTargetAdjust": false,
|
||||
"rebalanceExistingPositions": true,
|
||||
"retryEmptyRebalance": false
|
||||
}
|
||||
}
|
||||
@@ -2953,6 +2965,8 @@ mod tests {
|
||||
let cfg = platform_expr_config_from_value("", "", &explicit_off).expect("config");
|
||||
|
||||
assert!(!cfg.daily_top_up_enabled);
|
||||
assert!(!cfg.daily_position_target_adjust_enabled);
|
||||
assert!(cfg.rebalance_existing_positions);
|
||||
assert!(!cfg.retry_empty_rebalance);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user