修复表达式策略next-open风控日期语义
This commit is contained in:
@@ -4915,6 +4915,7 @@ impl PlatformExprStrategy {
|
|||||||
};
|
};
|
||||||
let selection_date = if self.config.aiquant_transaction_cost
|
let selection_date = if self.config.aiquant_transaction_cost
|
||||||
&& self.config.intraday_execution_time.is_some()
|
&& self.config.intraday_execution_time.is_some()
|
||||||
|
&& !ctx.is_lagged_execution()
|
||||||
{
|
{
|
||||||
ctx.execution_date
|
ctx.execution_date
|
||||||
} else {
|
} else {
|
||||||
@@ -5756,9 +5757,10 @@ impl PlatformExprStrategy {
|
|||||||
&self,
|
&self,
|
||||||
ctx: &StrategyContext<'_>,
|
ctx: &StrategyContext<'_>,
|
||||||
) -> Result<StrategyDecision, BacktestError> {
|
) -> Result<StrategyDecision, BacktestError> {
|
||||||
let day = self.day_state(ctx, ctx.execution_date)?;
|
let decision_date = ctx.decision_date;
|
||||||
|
let day = self.day_state(ctx, decision_date)?;
|
||||||
let (order_intents, action_diagnostics) =
|
let (order_intents, action_diagnostics) =
|
||||||
self.explicit_action_intents(ctx, ctx.execution_date, &day)?;
|
self.explicit_action_intents(ctx, decision_date, &day)?;
|
||||||
let mut diagnostics = vec![format!(
|
let mut diagnostics = vec![format!(
|
||||||
"platform_expr signal={} last={:.2} explicit_actions={} stage={}",
|
"platform_expr signal={} last={:.2} explicit_actions={} stage={}",
|
||||||
self.config.signal_symbol,
|
self.config.signal_symbol,
|
||||||
@@ -5921,7 +5923,8 @@ impl PlatformExprStrategy {
|
|||||||
let Some(market) = ctx.data.market(date, &factor.symbol) else {
|
let Some(market) = ctx.data.market(date, &factor.symbol) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if self
|
if !ctx.is_lagged_execution()
|
||||||
|
&& self
|
||||||
.selection_risk_rejection_reason(ctx, date, &factor.symbol, candidate, market)
|
.selection_risk_rejection_reason(ctx, date, &factor.symbol, candidate, market)
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
@@ -5956,6 +5959,9 @@ impl PlatformExprStrategy {
|
|||||||
date: NaiveDate,
|
date: NaiveDate,
|
||||||
factor_date: NaiveDate,
|
factor_date: NaiveDate,
|
||||||
) -> Vec<FidcRiskDecisionAudit> {
|
) -> Vec<FidcRiskDecisionAudit> {
|
||||||
|
if ctx.is_lagged_execution() {
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
let mut decisions = Vec::new();
|
let mut decisions = Vec::new();
|
||||||
for factor in ctx.data.factor_snapshots_on(factor_date) {
|
for factor in ctx.data.factor_snapshots_on(factor_date) {
|
||||||
if ctx.has_dynamic_universe() && !ctx.dynamic_universe_contains(&factor.symbol) {
|
if ctx.has_dynamic_universe() && !ctx.dynamic_universe_contains(&factor.symbol) {
|
||||||
@@ -6401,13 +6407,18 @@ impl PlatformExprStrategy {
|
|||||||
|
|
||||||
let mut selected = Vec::new();
|
let mut selected = Vec::new();
|
||||||
for (candidate, stock, _) in candidates {
|
for (candidate, stock, _) in candidates {
|
||||||
if let Some(reason) = self.stock_selection_limit_rejection_reason(&stock) {
|
if !ctx.is_lagged_execution()
|
||||||
|
&& let Some(reason) = self.stock_selection_limit_rejection_reason(&stock)
|
||||||
|
{
|
||||||
if diagnostics.len() < 12 {
|
if diagnostics.len() < 12 {
|
||||||
diagnostics.push(format!("{} rejected by {}", candidate.symbol, reason));
|
diagnostics.push(format!("{} rejected by {}", candidate.symbol, reason));
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Some(reason) = self.buy_rejection_reason(ctx, date, &candidate.symbol, &stock)? {
|
if !ctx.is_lagged_execution()
|
||||||
|
&& let Some(reason) =
|
||||||
|
self.buy_rejection_reason(ctx, date, &candidate.symbol, &stock)?
|
||||||
|
{
|
||||||
if diagnostics.len() < 12 {
|
if diagnostics.len() < 12 {
|
||||||
diagnostics.push(format!("{} rejected by {}", candidate.symbol, reason));
|
diagnostics.push(format!("{} rejected by {}", candidate.symbol, reason));
|
||||||
}
|
}
|
||||||
@@ -7083,13 +7094,17 @@ impl PlatformExprStrategy {
|
|||||||
if quote_candidate_symbols.len() < quote_candidate_limit {
|
if quote_candidate_symbols.len() < quote_candidate_limit {
|
||||||
quote_candidate_symbols.push(symbol.clone());
|
quote_candidate_symbols.push(symbol.clone());
|
||||||
}
|
}
|
||||||
if let Some(reason) = self.stock_selection_limit_rejection_reason(stock) {
|
if !ctx.is_lagged_execution()
|
||||||
|
&& let Some(reason) = self.stock_selection_limit_rejection_reason(stock)
|
||||||
|
{
|
||||||
if diagnostics.len() < 12 {
|
if diagnostics.len() < 12 {
|
||||||
diagnostics.push(format!("{symbol} quote_plan rejected by {reason}"));
|
diagnostics.push(format!("{symbol} quote_plan rejected by {reason}"));
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Some(reason) = self.buy_rejection_reason(ctx, date, symbol, &stock)? {
|
if !ctx.is_lagged_execution()
|
||||||
|
&& let Some(reason) = self.buy_rejection_reason(ctx, date, symbol, &stock)?
|
||||||
|
{
|
||||||
if diagnostics.len() < 12 {
|
if diagnostics.len() < 12 {
|
||||||
diagnostics.push(format!("{symbol} quote_plan rejected by {reason}"));
|
diagnostics.push(format!("{symbol} quote_plan rejected by {reason}"));
|
||||||
}
|
}
|
||||||
@@ -7123,11 +7138,11 @@ impl PlatformExprStrategy {
|
|||||||
ctx: &StrategyContext<'_>,
|
ctx: &StrategyContext<'_>,
|
||||||
_scope_limit: usize,
|
_scope_limit: usize,
|
||||||
) -> Result<PlatformSelectionQuotePlan, BacktestError> {
|
) -> Result<PlatformSelectionQuotePlan, BacktestError> {
|
||||||
if !self.config.rotation_enabled || self.config.in_skip_window(ctx.execution_date) {
|
if !self.config.rotation_enabled || self.config.in_skip_window(ctx.decision_date) {
|
||||||
return Ok(PlatformSelectionQuotePlan {
|
return Ok(PlatformSelectionQuotePlan {
|
||||||
execution_date: ctx.execution_date,
|
execution_date: ctx.execution_date,
|
||||||
decision_date: ctx.decision_date,
|
decision_date: ctx.decision_date,
|
||||||
selection_date: ctx.execution_date,
|
selection_date: ctx.decision_date,
|
||||||
factor_date: ctx.decision_date,
|
factor_date: ctx.decision_date,
|
||||||
requires_intraday_selection_quotes: false,
|
requires_intraday_selection_quotes: false,
|
||||||
band_low: 0.0,
|
band_low: 0.0,
|
||||||
@@ -7178,21 +7193,21 @@ impl PlatformExprStrategy {
|
|||||||
fn stop_take_action(
|
fn stop_take_action(
|
||||||
&self,
|
&self,
|
||||||
ctx: &StrategyContext<'_>,
|
ctx: &StrategyContext<'_>,
|
||||||
_signal_date: NaiveDate,
|
signal_date: NaiveDate,
|
||||||
execution_date: NaiveDate,
|
_execution_date: NaiveDate,
|
||||||
day: &DayExpressionState,
|
day: &DayExpressionState,
|
||||||
symbol: &str,
|
symbol: &str,
|
||||||
) -> Result<(bool, bool), BacktestError> {
|
) -> Result<(bool, bool), BacktestError> {
|
||||||
let Some(position) = ctx.portfolio.position(symbol) else {
|
let Some(position) = ctx.portfolio.position(symbol) else {
|
||||||
return Ok((false, false));
|
return Ok((false, false));
|
||||||
};
|
};
|
||||||
self.stop_take_action_for_position(ctx, execution_date, day, position)
|
self.stop_take_action_for_position(ctx, signal_date, day, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stop_take_action_for_position(
|
fn stop_take_action_for_position(
|
||||||
&self,
|
&self,
|
||||||
ctx: &StrategyContext<'_>,
|
ctx: &StrategyContext<'_>,
|
||||||
execution_date: NaiveDate,
|
signal_date: NaiveDate,
|
||||||
day: &DayExpressionState,
|
day: &DayExpressionState,
|
||||||
position: &crate::portfolio::Position,
|
position: &crate::portfolio::Position,
|
||||||
) -> Result<(bool, bool), BacktestError> {
|
) -> Result<(bool, bool), BacktestError> {
|
||||||
@@ -7219,7 +7234,7 @@ impl PlatformExprStrategy {
|
|||||||
}
|
}
|
||||||
let stock = match self.stock_state_at_time(
|
let stock = match self.stock_state_at_time(
|
||||||
ctx,
|
ctx,
|
||||||
execution_date,
|
signal_date,
|
||||||
symbol,
|
symbol,
|
||||||
Some(self.intraday_execution_start_time()),
|
Some(self.intraday_execution_start_time()),
|
||||||
) {
|
) {
|
||||||
@@ -7249,9 +7264,9 @@ impl PlatformExprStrategy {
|
|||||||
prev_close: stock.prev_close,
|
prev_close: stock.prev_close,
|
||||||
holding_return,
|
holding_return,
|
||||||
quantity: position.quantity as i64,
|
quantity: position.quantity as i64,
|
||||||
sellable_qty: position.sellable_qty(execution_date) as i64,
|
sellable_qty: position.sellable_qty(signal_date) as i64,
|
||||||
sellable: position.sellable_qty(execution_date) as i64,
|
sellable: position.sellable_qty(signal_date) as i64,
|
||||||
closable: position.sellable_qty(execution_date) as i64,
|
closable: position.sellable_qty(signal_date) as i64,
|
||||||
old_quantity: position.day_start_quantity() as i64,
|
old_quantity: position.day_start_quantity() as i64,
|
||||||
bought_quantity: position.bought_quantity() as i64,
|
bought_quantity: position.bought_quantity() as i64,
|
||||||
sold_quantity: position.sold_quantity() as i64,
|
sold_quantity: position.sold_quantity() as i64,
|
||||||
@@ -7352,7 +7367,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
.filter(|symbol| !symbol.trim().is_empty())
|
.filter(|symbol| !symbol.trim().is_empty())
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect::<BTreeSet<_>>();
|
.collect::<BTreeSet<_>>();
|
||||||
if self.config.rotation_enabled && !self.config.in_skip_window(ctx.execution_date) {
|
if self.config.rotation_enabled && !self.config.in_skip_window(ctx.decision_date) {
|
||||||
let plan = self.selection_quote_plan(ctx, 0)?;
|
let plan = self.selection_quote_plan(ctx, 0)?;
|
||||||
symbols.extend(plan.order_symbols);
|
symbols.extend(plan.order_symbols);
|
||||||
symbols.extend(plan.candidate_symbols);
|
symbols.extend(plan.candidate_symbols);
|
||||||
@@ -7366,7 +7381,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
) -> Result<StrategyDecision, BacktestError> {
|
) -> Result<StrategyDecision, BacktestError> {
|
||||||
if self.config.explicit_action_stage == PlatformExplicitActionStage::OpenAuction
|
if self.config.explicit_action_stage == PlatformExplicitActionStage::OpenAuction
|
||||||
&& !self.config.explicit_actions.is_empty()
|
&& !self.config.explicit_actions.is_empty()
|
||||||
&& self.explicit_actions_active(ctx.data.calendar(), ctx.execution_date)
|
&& self.explicit_actions_active(ctx.data.calendar(), ctx.decision_date)
|
||||||
{
|
{
|
||||||
return self.explicit_action_decision(ctx);
|
return self.explicit_action_decision(ctx);
|
||||||
}
|
}
|
||||||
@@ -7376,14 +7391,21 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
fn on_day(&mut self, ctx: &StrategyContext<'_>) -> Result<StrategyDecision, BacktestError> {
|
fn on_day(&mut self, ctx: &StrategyContext<'_>) -> Result<StrategyDecision, BacktestError> {
|
||||||
let execution_date = ctx.execution_date;
|
let execution_date = ctx.execution_date;
|
||||||
let decision_date = ctx.decision_date;
|
let decision_date = ctx.decision_date;
|
||||||
let in_skip_window = self.config.in_skip_window(execution_date);
|
let defer_execution_risk = ctx.is_lagged_execution();
|
||||||
|
let signal_date = decision_date;
|
||||||
|
let projection_date = if defer_execution_risk {
|
||||||
|
signal_date
|
||||||
|
} else {
|
||||||
|
execution_date
|
||||||
|
};
|
||||||
|
let in_skip_window = self.config.in_skip_window(signal_date);
|
||||||
|
|
||||||
let day = self.day_state(ctx, decision_date)?;
|
let day = self.day_state(ctx, decision_date)?;
|
||||||
let (selection_market_date, selection_universe_factor_date, selection_factor_date) =
|
let (selection_market_date, selection_universe_factor_date, selection_factor_date) =
|
||||||
self.selection_dates(ctx);
|
self.selection_dates(ctx);
|
||||||
let (explicit_action_intents, explicit_action_diagnostics) = if !in_skip_window
|
let (explicit_action_intents, explicit_action_diagnostics) = if !in_skip_window
|
||||||
&& self.config.explicit_action_stage == PlatformExplicitActionStage::OnDay
|
&& self.config.explicit_action_stage == PlatformExplicitActionStage::OnDay
|
||||||
&& self.explicit_actions_active(ctx.data.calendar(), execution_date)
|
&& self.explicit_actions_active(ctx.data.calendar(), signal_date)
|
||||||
{
|
{
|
||||||
self.explicit_action_intents(ctx, decision_date, &day)?
|
self.explicit_action_intents(ctx, decision_date, &day)?
|
||||||
} else {
|
} else {
|
||||||
@@ -7398,7 +7420,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
let previous_trading_ratio = self.last_trading_ratio.unwrap_or(1.0);
|
let previous_trading_ratio = self.last_trading_ratio.unwrap_or(1.0);
|
||||||
let weak_market_shrink_due =
|
let weak_market_shrink_due =
|
||||||
trading_ratio.is_finite() && trading_ratio < previous_trading_ratio - 1e-9;
|
trading_ratio.is_finite() && trading_ratio < previous_trading_ratio - 1e-9;
|
||||||
let marked_total_value = self.marked_total_value(ctx, execution_date);
|
let marked_total_value = self.marked_total_value(ctx, projection_date);
|
||||||
let mut aiquant_total_value = if marked_total_value.is_finite() && marked_total_value > 0.0
|
let mut aiquant_total_value = if marked_total_value.is_finite() && marked_total_value > 0.0
|
||||||
{
|
{
|
||||||
marked_total_value
|
marked_total_value
|
||||||
@@ -7445,14 +7467,14 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
} else if let Some(schedule) = &self.config.rebalance_schedule {
|
} else if let Some(schedule) = &self.config.rebalance_schedule {
|
||||||
schedule.matches(
|
schedule.matches(
|
||||||
ctx.data.calendar(),
|
ctx.data.calendar(),
|
||||||
execution_date,
|
signal_date,
|
||||||
ScheduleStage::OnDay,
|
ScheduleStage::OnDay,
|
||||||
default_stage_time(ScheduleStage::OnDay),
|
default_stage_time(ScheduleStage::OnDay),
|
||||||
) || empty_rebalance_retry
|
) || empty_rebalance_retry
|
||||||
} else if self.config.calendar_rebalance_interval {
|
} else if self.config.calendar_rebalance_interval {
|
||||||
self.last_rebalance_date
|
self.last_rebalance_date
|
||||||
.map(|last| {
|
.map(|last| {
|
||||||
execution_date.signed_duration_since(last).num_days()
|
signal_date.signed_duration_since(last).num_days()
|
||||||
>= effective_refresh_rate as i64
|
>= effective_refresh_rate as i64
|
||||||
})
|
})
|
||||||
.unwrap_or(true)
|
.unwrap_or(true)
|
||||||
@@ -7489,7 +7511,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
}
|
}
|
||||||
match self.stock_is_at_upper_limit_at_time(
|
match self.stock_is_at_upper_limit_at_time(
|
||||||
ctx,
|
ctx,
|
||||||
execution_date,
|
projection_date,
|
||||||
&position.symbol,
|
&position.symbol,
|
||||||
delayed_limit_exit_time,
|
delayed_limit_exit_time,
|
||||||
)? {
|
)? {
|
||||||
@@ -7517,7 +7539,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
if self
|
if self
|
||||||
.aiquant_scheduled_quote_at_time(
|
.aiquant_scheduled_quote_at_time(
|
||||||
ctx,
|
ctx,
|
||||||
execution_date,
|
projection_date,
|
||||||
&symbol,
|
&symbol,
|
||||||
Some(delayed_limit_exit_time),
|
Some(delayed_limit_exit_time),
|
||||||
)
|
)
|
||||||
@@ -7527,7 +7549,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
}
|
}
|
||||||
let stock = match self.stock_state_at_time(
|
let stock = match self.stock_state_at_time(
|
||||||
ctx,
|
ctx,
|
||||||
execution_date,
|
projection_date,
|
||||||
&symbol,
|
&symbol,
|
||||||
Some(delayed_limit_exit_time),
|
Some(delayed_limit_exit_time),
|
||||||
) {
|
) {
|
||||||
@@ -7559,7 +7581,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
});
|
});
|
||||||
let projected_sold = if ctx
|
let projected_sold = if ctx
|
||||||
.data
|
.data
|
||||||
.execution_quotes_on(execution_date, &symbol)
|
.execution_quotes_on(projection_date, &symbol)
|
||||||
.is_empty()
|
.is_empty()
|
||||||
{
|
{
|
||||||
false
|
false
|
||||||
@@ -7567,7 +7589,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
self.project_target_zero_at_time(
|
self.project_target_zero_at_time(
|
||||||
ctx,
|
ctx,
|
||||||
&mut projected,
|
&mut projected,
|
||||||
execution_date,
|
projection_date,
|
||||||
&symbol,
|
&symbol,
|
||||||
&mut projected_execution_state,
|
&mut projected_execution_state,
|
||||||
Some(delayed_limit_exit_time),
|
Some(delayed_limit_exit_time),
|
||||||
@@ -7583,7 +7605,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
}
|
}
|
||||||
if !delayed_sold_symbols.is_empty() {
|
if !delayed_sold_symbols.is_empty() {
|
||||||
let projected_total =
|
let projected_total =
|
||||||
self.marked_total_value_for_portfolio(ctx, &projected, execution_date);
|
self.marked_total_value_for_portfolio(ctx, &projected, projection_date);
|
||||||
if projected_total.is_finite() && projected_total > 0.0 {
|
if projected_total.is_finite() && projected_total > 0.0 {
|
||||||
aiquant_total_value = projected_total;
|
aiquant_total_value = projected_total;
|
||||||
}
|
}
|
||||||
@@ -7608,7 +7630,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
reason: "seasonal_stop_window".to_string(),
|
reason: "seasonal_stop_window".to_string(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let mut notes = vec![format!("seasonal stop window on {}", execution_date)];
|
let mut notes = vec![format!("seasonal stop window on {}", signal_date)];
|
||||||
if !delayed_sold_symbols.is_empty() {
|
if !delayed_sold_symbols.is_empty() {
|
||||||
notes.push(format!(
|
notes.push(format!(
|
||||||
"delayed limit open sells before seasonal stop: {}",
|
"delayed limit open sells before seasonal stop: {}",
|
||||||
@@ -7659,7 +7681,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
}
|
}
|
||||||
if self.regular_sell_should_wait_due_to_highlimit(
|
if self.regular_sell_should_wait_due_to_highlimit(
|
||||||
ctx,
|
ctx,
|
||||||
execution_date,
|
projection_date,
|
||||||
&position.symbol,
|
&position.symbol,
|
||||||
self.intraday_execution_start_time(),
|
self.intraday_execution_start_time(),
|
||||||
)? {
|
)? {
|
||||||
@@ -7667,7 +7689,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
}
|
}
|
||||||
if self.candidate_requires_risk_level_forced_exit(
|
if self.candidate_requires_risk_level_forced_exit(
|
||||||
ctx,
|
ctx,
|
||||||
execution_date,
|
projection_date,
|
||||||
&position.symbol,
|
&position.symbol,
|
||||||
risk_level_forced_exit_time,
|
risk_level_forced_exit_time,
|
||||||
)? {
|
)? {
|
||||||
@@ -7676,7 +7698,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let (stop_hit, profit_hit) =
|
let (stop_hit, profit_hit) =
|
||||||
self.stop_take_action_for_position(ctx, execution_date, &day, position)?;
|
self.stop_take_action_for_position(ctx, signal_date, &day, position)?;
|
||||||
if stop_hit {
|
if stop_hit {
|
||||||
pending_full_close_symbols.insert(position.symbol.clone());
|
pending_full_close_symbols.insert(position.symbol.clone());
|
||||||
continue;
|
continue;
|
||||||
@@ -7685,14 +7707,15 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
pending_full_close_symbols.insert(position.symbol.clone());
|
pending_full_close_symbols.insert(position.symbol.clone());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let can_sell = self.can_sell_position(ctx, execution_date, &position.symbol);
|
let can_sell = defer_execution_risk
|
||||||
|
|| self.can_sell_position(ctx, execution_date, &position.symbol);
|
||||||
if !can_sell {
|
if !can_sell {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if self.config.delayed_limit_open_exit_enabled {
|
if self.config.delayed_limit_open_exit_enabled {
|
||||||
let stock = match self.stock_state_at_time(
|
let stock = match self.stock_state_at_time(
|
||||||
ctx,
|
ctx,
|
||||||
execution_date,
|
projection_date,
|
||||||
&position.symbol,
|
&position.symbol,
|
||||||
Some(self.intraday_execution_start_time()),
|
Some(self.intraday_execution_start_time()),
|
||||||
) {
|
) {
|
||||||
@@ -7726,7 +7749,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
.project_target_zero_at_time(
|
.project_target_zero_at_time(
|
||||||
ctx,
|
ctx,
|
||||||
&mut projected,
|
&mut projected,
|
||||||
execution_date,
|
projection_date,
|
||||||
&symbol,
|
&symbol,
|
||||||
&mut projected_execution_state,
|
&mut projected_execution_state,
|
||||||
Some(risk_level_forced_exit_time),
|
Some(risk_level_forced_exit_time),
|
||||||
@@ -7755,7 +7778,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
}
|
}
|
||||||
if self.regular_sell_should_wait_due_to_highlimit(
|
if self.regular_sell_should_wait_due_to_highlimit(
|
||||||
ctx,
|
ctx,
|
||||||
execution_date,
|
projection_date,
|
||||||
&position.symbol,
|
&position.symbol,
|
||||||
self.intraday_execution_start_time(),
|
self.intraday_execution_start_time(),
|
||||||
)? {
|
)? {
|
||||||
@@ -7767,7 +7790,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
let current_value = self.projected_position_value_at_execution_price(
|
let current_value = self.projected_position_value_at_execution_price(
|
||||||
ctx,
|
ctx,
|
||||||
&projected,
|
&projected,
|
||||||
execution_date,
|
projection_date,
|
||||||
&position.symbol,
|
&position.symbol,
|
||||||
);
|
);
|
||||||
if let Some(threshold) = self
|
if let Some(threshold) = self
|
||||||
@@ -7788,7 +7811,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
self.project_target_value(
|
self.project_target_value(
|
||||||
ctx,
|
ctx,
|
||||||
&mut trial_projected,
|
&mut trial_projected,
|
||||||
execution_date,
|
projection_date,
|
||||||
&position.symbol,
|
&position.symbol,
|
||||||
target_value,
|
target_value,
|
||||||
&mut trial_execution_state,
|
&mut trial_execution_state,
|
||||||
@@ -7834,15 +7857,16 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
}
|
}
|
||||||
if self.regular_sell_should_wait_due_to_highlimit(
|
if self.regular_sell_should_wait_due_to_highlimit(
|
||||||
ctx,
|
ctx,
|
||||||
execution_date,
|
projection_date,
|
||||||
&position.symbol,
|
&position.symbol,
|
||||||
self.intraday_execution_start_time(),
|
self.intraday_execution_start_time(),
|
||||||
)? {
|
)? {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let (stop_hit, profit_hit) =
|
let (stop_hit, profit_hit) =
|
||||||
self.stop_take_action_for_position(ctx, execution_date, &day, projected_position)?;
|
self.stop_take_action_for_position(ctx, signal_date, &day, projected_position)?;
|
||||||
let can_sell = self.can_sell_position(ctx, execution_date, &position.symbol);
|
let can_sell = defer_execution_risk
|
||||||
|
|| self.can_sell_position(ctx, execution_date, &position.symbol);
|
||||||
if stop_hit {
|
if stop_hit {
|
||||||
exit_symbols.insert(position.symbol.clone());
|
exit_symbols.insert(position.symbol.clone());
|
||||||
order_intents.push(OrderIntent::TargetValue {
|
order_intents.push(OrderIntent::TargetValue {
|
||||||
@@ -7855,7 +7879,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
.project_target_zero(
|
.project_target_zero(
|
||||||
ctx,
|
ctx,
|
||||||
&mut projected,
|
&mut projected,
|
||||||
execution_date,
|
projection_date,
|
||||||
&position.symbol,
|
&position.symbol,
|
||||||
&mut projected_execution_state,
|
&mut projected_execution_state,
|
||||||
)
|
)
|
||||||
@@ -7883,7 +7907,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
.project_target_zero(
|
.project_target_zero(
|
||||||
ctx,
|
ctx,
|
||||||
&mut projected,
|
&mut projected,
|
||||||
execution_date,
|
projection_date,
|
||||||
&position.symbol,
|
&position.symbol,
|
||||||
&mut projected_execution_state,
|
&mut projected_execution_state,
|
||||||
)
|
)
|
||||||
@@ -7919,7 +7943,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
self.remaining_buy_cash_active_value(
|
self.remaining_buy_cash_active_value(
|
||||||
ctx,
|
ctx,
|
||||||
&projected,
|
&projected,
|
||||||
execution_date,
|
projection_date,
|
||||||
&working_symbols,
|
&working_symbols,
|
||||||
&value_symbols,
|
&value_symbols,
|
||||||
)
|
)
|
||||||
@@ -7929,7 +7953,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
let slot_buy_cash = self.remaining_buy_cash_per_slot(
|
let slot_buy_cash = self.remaining_buy_cash_per_slot(
|
||||||
ctx,
|
ctx,
|
||||||
&projected,
|
&projected,
|
||||||
execution_date,
|
projection_date,
|
||||||
target_budget,
|
target_budget,
|
||||||
selection_limit,
|
selection_limit,
|
||||||
&working_symbols,
|
&working_symbols,
|
||||||
@@ -7966,9 +7990,14 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let execution_stock = self.stock_state(ctx, execution_date, symbol)?;
|
if !defer_execution_risk
|
||||||
if self
|
&& self
|
||||||
.buy_rejection_reason(ctx, execution_date, symbol, &execution_stock)?
|
.buy_rejection_reason(
|
||||||
|
ctx,
|
||||||
|
execution_date,
|
||||||
|
symbol,
|
||||||
|
&self.stock_state(ctx, execution_date, symbol)?,
|
||||||
|
)?
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@@ -7990,7 +8019,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
let filled_qty = self.project_order_value(
|
let filled_qty = self.project_order_value(
|
||||||
ctx,
|
ctx,
|
||||||
&mut projected,
|
&mut projected,
|
||||||
execution_date,
|
projection_date,
|
||||||
symbol,
|
symbol,
|
||||||
buy_cash,
|
buy_cash,
|
||||||
&mut projected_execution_state,
|
&mut projected_execution_state,
|
||||||
@@ -8041,13 +8070,13 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
}
|
}
|
||||||
if self.regular_sell_should_wait_due_to_highlimit(
|
if self.regular_sell_should_wait_due_to_highlimit(
|
||||||
ctx,
|
ctx,
|
||||||
execution_date,
|
projection_date,
|
||||||
symbol,
|
symbol,
|
||||||
self.intraday_execution_start_time(),
|
self.intraday_execution_start_time(),
|
||||||
)? {
|
)? {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if !self.can_sell_position(ctx, execution_date, symbol) {
|
if !defer_execution_risk && !self.can_sell_position(ctx, execution_date, symbol) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
order_intents.push(OrderIntent::TargetValue {
|
order_intents.push(OrderIntent::TargetValue {
|
||||||
@@ -8059,7 +8088,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
.project_target_zero(
|
.project_target_zero(
|
||||||
ctx,
|
ctx,
|
||||||
&mut projected,
|
&mut projected,
|
||||||
execution_date,
|
projection_date,
|
||||||
symbol,
|
symbol,
|
||||||
&mut projected_execution_state,
|
&mut projected_execution_state,
|
||||||
)
|
)
|
||||||
@@ -8109,16 +8138,21 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
let current_value = self.projected_position_value_at_execution_price(
|
let current_value = self.projected_position_value_at_execution_price(
|
||||||
ctx,
|
ctx,
|
||||||
&projected,
|
&projected,
|
||||||
execution_date,
|
projection_date,
|
||||||
symbol,
|
symbol,
|
||||||
);
|
);
|
||||||
let buy_cash = (target_cash - current_value).min(aiquant_available_cash);
|
let buy_cash = (target_cash - current_value).min(aiquant_available_cash);
|
||||||
if buy_cash <= 0.0 {
|
if buy_cash <= 0.0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let execution_stock = self.stock_state(ctx, execution_date, symbol)?;
|
if !defer_execution_risk
|
||||||
if self
|
&& self
|
||||||
.buy_rejection_reason(ctx, execution_date, symbol, &execution_stock)?
|
.buy_rejection_reason(
|
||||||
|
ctx,
|
||||||
|
execution_date,
|
||||||
|
symbol,
|
||||||
|
&self.stock_state(ctx, execution_date, symbol)?,
|
||||||
|
)?
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@@ -8130,7 +8164,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
let filled_qty = self.project_order_value(
|
let filled_qty = self.project_order_value(
|
||||||
ctx,
|
ctx,
|
||||||
&mut projected,
|
&mut projected,
|
||||||
execution_date,
|
projection_date,
|
||||||
symbol,
|
symbol,
|
||||||
buy_cash,
|
buy_cash,
|
||||||
&mut projected_execution_state,
|
&mut projected_execution_state,
|
||||||
@@ -8164,7 +8198,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
let slot_buy_cash = self.remaining_buy_cash_per_slot(
|
let slot_buy_cash = self.remaining_buy_cash_per_slot(
|
||||||
ctx,
|
ctx,
|
||||||
&projected,
|
&projected,
|
||||||
execution_date,
|
projection_date,
|
||||||
target_budget,
|
target_budget,
|
||||||
selection_limit,
|
selection_limit,
|
||||||
&rebalance_working_symbols,
|
&rebalance_working_symbols,
|
||||||
@@ -8180,9 +8214,14 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
if self.config.aiquant_transaction_cost && buy_cash < target_cash * 0.5 {
|
if self.config.aiquant_transaction_cost && buy_cash < target_cash * 0.5 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let execution_stock = self.stock_state(ctx, execution_date, symbol)?;
|
if !defer_execution_risk
|
||||||
if self
|
&& self
|
||||||
.buy_rejection_reason(ctx, execution_date, symbol, &execution_stock)?
|
.buy_rejection_reason(
|
||||||
|
ctx,
|
||||||
|
execution_date,
|
||||||
|
symbol,
|
||||||
|
&self.stock_state(ctx, execution_date, symbol)?,
|
||||||
|
)?
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@@ -8194,7 +8233,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
let filled_qty = self.project_order_value(
|
let filled_qty = self.project_order_value(
|
||||||
ctx,
|
ctx,
|
||||||
&mut projected,
|
&mut projected,
|
||||||
execution_date,
|
projection_date,
|
||||||
symbol,
|
symbol,
|
||||||
buy_cash,
|
buy_cash,
|
||||||
&mut projected_execution_state,
|
&mut projected_execution_state,
|
||||||
@@ -8216,7 +8255,7 @@ impl Strategy for PlatformExprStrategy {
|
|||||||
if self.config.rotation_enabled && self.config.rebalance_schedule.is_none() {
|
if self.config.rotation_enabled && self.config.rebalance_schedule.is_none() {
|
||||||
if self.config.calendar_rebalance_interval {
|
if self.config.calendar_rebalance_interval {
|
||||||
if periodic_rebalance {
|
if periodic_rebalance {
|
||||||
self.last_rebalance_date = Some(execution_date);
|
self.last_rebalance_date = Some(signal_date);
|
||||||
}
|
}
|
||||||
} else if periodic_rebalance {
|
} else if periodic_rebalance {
|
||||||
self.rebalance_day_counter = 1;
|
self.rebalance_day_counter = 1;
|
||||||
@@ -13945,7 +13984,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn platform_aiquant_intraday_selection_filters_limits_on_execution_date() {
|
fn platform_next_open_selection_ignores_execution_day_limit_state() {
|
||||||
let factor_date = d(2023, 11, 10);
|
let factor_date = d(2023, 11, 10);
|
||||||
let decision_date = d(2023, 11, 13);
|
let decision_date = d(2023, 11, 13);
|
||||||
let execution_date = d(2023, 11, 14);
|
let execution_date = d(2023, 11, 14);
|
||||||
@@ -14186,7 +14225,7 @@ mod tests {
|
|||||||
decision
|
decision
|
||||||
.diagnostics
|
.diagnostics
|
||||||
.iter()
|
.iter()
|
||||||
.any(|item| item.contains("selection_market_date=2023-11-14")),
|
.any(|item| item.contains("selection_market_date=2023-11-13")),
|
||||||
"{:?}",
|
"{:?}",
|
||||||
decision.diagnostics
|
decision.diagnostics
|
||||||
);
|
);
|
||||||
@@ -14202,7 +14241,7 @@ mod tests {
|
|||||||
decision
|
decision
|
||||||
.diagnostics
|
.diagnostics
|
||||||
.iter()
|
.iter()
|
||||||
.any(|item| item == "selected_symbols=000002.SZ"),
|
.any(|item| item == "selected_symbols=600462.SH"),
|
||||||
"{:?}",
|
"{:?}",
|
||||||
decision.diagnostics
|
decision.diagnostics
|
||||||
);
|
);
|
||||||
@@ -14210,7 +14249,7 @@ mod tests {
|
|||||||
decision
|
decision
|
||||||
.diagnostics
|
.diagnostics
|
||||||
.iter()
|
.iter()
|
||||||
.any(|item| item == "600462.SH rejected by upper_limit"),
|
.all(|item| item != "600462.SH rejected by upper_limit"),
|
||||||
"{:?}",
|
"{:?}",
|
||||||
decision.diagnostics
|
decision.diagnostics
|
||||||
);
|
);
|
||||||
@@ -14218,9 +14257,9 @@ mod tests {
|
|||||||
decision.order_intents.iter().any(|intent| match intent {
|
decision.order_intents.iter().any(|intent| match intent {
|
||||||
OrderIntent::Shares {
|
OrderIntent::Shares {
|
||||||
symbol, quantity, ..
|
symbol, quantity, ..
|
||||||
} => symbol == fallback_symbol && *quantity > 0,
|
} => symbol == limit_symbol && *quantity > 0,
|
||||||
OrderIntent::Value { symbol, value, .. } => {
|
OrderIntent::Value { symbol, value, .. } => {
|
||||||
symbol == fallback_symbol && *value > 0.0
|
symbol == limit_symbol && *value > 0.0
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
}),
|
}),
|
||||||
|
|||||||
Reference in New Issue
Block a user