Support successor conversions for delisted holdings
This commit is contained in:
@@ -6,7 +6,7 @@ use crate::broker::{BrokerExecutionReport, BrokerSimulator};
|
||||
use crate::cost::CostModel;
|
||||
use crate::data::{BenchmarkSnapshot, DataSet, DataSetError, PriceField};
|
||||
use crate::events::{AccountEvent, FillEvent, OrderEvent, OrderSide, OrderStatus, PositionEvent};
|
||||
use crate::metrics::{BacktestMetrics, compute_backtest_metrics};
|
||||
use crate::metrics::{compute_backtest_metrics, BacktestMetrics};
|
||||
use crate::portfolio::{CashReceivable, HoldingSummary, PortfolioState};
|
||||
use crate::rules::EquityRuleHooks;
|
||||
use crate::strategy::{Strategy, StrategyContext};
|
||||
@@ -177,18 +177,18 @@ where
|
||||
&mut corporate_action_notes,
|
||||
)?;
|
||||
self.extend_result(&mut result, receivable_report);
|
||||
let delisting_report = self.settle_delisted_positions(
|
||||
execution_date,
|
||||
&mut portfolio,
|
||||
&mut corporate_action_notes,
|
||||
)?;
|
||||
self.extend_result(&mut result, delisting_report);
|
||||
let corporate_action_report = self.apply_corporate_actions(
|
||||
execution_date,
|
||||
&mut portfolio,
|
||||
&mut corporate_action_notes,
|
||||
)?;
|
||||
self.extend_result(&mut result, corporate_action_report);
|
||||
let delisting_report = self.settle_delisted_positions(
|
||||
execution_date,
|
||||
&mut portfolio,
|
||||
&mut corporate_action_notes,
|
||||
)?;
|
||||
self.extend_result(&mut result, delisting_report);
|
||||
|
||||
let decision = execution_idx
|
||||
.checked_sub(self.config.decision_lag_trading_days)
|
||||
@@ -396,6 +396,58 @@ where
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if action.has_successor_conversion() {
|
||||
let successor_symbol = action
|
||||
.successor_symbol
|
||||
.as_deref()
|
||||
.expect("successor symbol checked");
|
||||
let Some(outcome) = portfolio.apply_successor_conversion(
|
||||
&action.symbol,
|
||||
successor_symbol,
|
||||
action.successor_ratio_value(),
|
||||
action.successor_cash_value(),
|
||||
) else {
|
||||
continue;
|
||||
};
|
||||
let reason = format!(
|
||||
"successor_conversion {}->{} ratio={:.6} cash_per_share={:.6}",
|
||||
outcome.old_symbol,
|
||||
outcome.new_symbol,
|
||||
action.successor_ratio_value(),
|
||||
action.successor_cash_value()
|
||||
);
|
||||
notes.push(reason.clone());
|
||||
report.position_events.push(PositionEvent {
|
||||
date,
|
||||
symbol: outcome.old_symbol.clone(),
|
||||
delta_quantity: -(outcome.old_quantity as i32),
|
||||
quantity_after: 0,
|
||||
average_cost: 0.0,
|
||||
realized_pnl_delta: 0.0,
|
||||
reason: reason.clone(),
|
||||
});
|
||||
report.position_events.push(PositionEvent {
|
||||
date,
|
||||
symbol: outcome.new_symbol.clone(),
|
||||
delta_quantity: outcome.new_quantity_delta,
|
||||
quantity_after: outcome.new_quantity_after,
|
||||
average_cost: outcome.new_average_cost_after,
|
||||
realized_pnl_delta: 0.0,
|
||||
reason: reason.clone(),
|
||||
});
|
||||
if outcome.cash_delta.abs() > f64::EPSILON {
|
||||
let cash_before = portfolio.cash();
|
||||
portfolio.apply_cash_delta(outcome.cash_delta);
|
||||
report.account_events.push(AccountEvent {
|
||||
date,
|
||||
cash_before,
|
||||
cash_after: portfolio.cash(),
|
||||
total_equity: portfolio.total_equity(),
|
||||
note: format!("{} cash={:.2}", reason, outcome.cash_delta),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
portfolio.prune_flat_positions();
|
||||
|
||||
Reference in New Issue
Block a user