Add management fee callbacks
This commit is contained in:
@@ -396,6 +396,38 @@ where
|
||||
},
|
||||
)?;
|
||||
}
|
||||
crate::strategy::OrderIntent::SetManagementFeeRate { rate, reason } => {
|
||||
portfolio
|
||||
.set_management_fee_rate(rate)
|
||||
.map_err(BacktestError::Execution)?;
|
||||
decision
|
||||
.diagnostics
|
||||
.push(format!("account_management_fee_rate rate={rate:.6}"));
|
||||
publish_custom_process_event(
|
||||
&mut self.strategy,
|
||||
&mut self.process_event_bus,
|
||||
execution_date,
|
||||
decision_date,
|
||||
decision_index,
|
||||
&self.data,
|
||||
&*portfolio,
|
||||
open_orders,
|
||||
self.dynamic_universe.as_ref(),
|
||||
&self.subscriptions,
|
||||
process_events,
|
||||
ProcessEvent {
|
||||
date: execution_date,
|
||||
kind: ProcessEventKind::AccountManagementFee,
|
||||
order_id: None,
|
||||
symbol: None,
|
||||
side: None,
|
||||
detail: format!(
|
||||
"reason={reason} rate={rate:.6} management_fees={:.2}",
|
||||
portfolio.management_fees()
|
||||
),
|
||||
},
|
||||
)?;
|
||||
}
|
||||
other => retained.push(other),
|
||||
}
|
||||
}
|
||||
@@ -1283,6 +1315,21 @@ where
|
||||
&mut settlement_decision,
|
||||
&mut directive_report,
|
||||
)?;
|
||||
let dynamic_universe_snapshot = self.dynamic_universe.clone();
|
||||
let subscriptions_snapshot = self.subscriptions.clone();
|
||||
let management_fee_report = self.apply_management_fee(
|
||||
execution_date,
|
||||
decision_date,
|
||||
decision_index,
|
||||
&mut portfolio,
|
||||
&post_close_open_orders,
|
||||
dynamic_universe_snapshot.as_ref(),
|
||||
&subscriptions_snapshot,
|
||||
&mut process_events,
|
||||
visible_order_events_after_close.as_slice(),
|
||||
visible_fills_after_close.as_slice(),
|
||||
)?;
|
||||
merge_broker_report(&mut directive_report, management_fee_report);
|
||||
publish_phase_event(
|
||||
&mut self.strategy,
|
||||
&mut self.process_event_bus,
|
||||
@@ -1695,6 +1742,92 @@ where
|
||||
report
|
||||
}
|
||||
|
||||
fn apply_management_fee(
|
||||
&mut self,
|
||||
execution_date: NaiveDate,
|
||||
decision_date: NaiveDate,
|
||||
decision_index: usize,
|
||||
portfolio: &mut PortfolioState,
|
||||
open_orders: &[crate::strategy::OpenOrderView],
|
||||
dynamic_universe: Option<&BTreeSet<String>>,
|
||||
subscriptions: &BTreeSet<String>,
|
||||
process_events: &mut Vec<ProcessEvent>,
|
||||
order_events: &[OrderEvent],
|
||||
fills: &[FillEvent],
|
||||
) -> Result<BrokerExecutionReport, BacktestError> {
|
||||
let rate = portfolio.management_fee_rate();
|
||||
if rate <= 0.0 {
|
||||
return Ok(BrokerExecutionReport::default());
|
||||
}
|
||||
|
||||
let fee = self
|
||||
.strategy
|
||||
.management_fee(
|
||||
&StrategyContext {
|
||||
execution_date,
|
||||
decision_date,
|
||||
decision_index,
|
||||
data: &self.data,
|
||||
portfolio,
|
||||
open_orders,
|
||||
dynamic_universe,
|
||||
subscriptions,
|
||||
process_events: process_events.as_slice(),
|
||||
active_process_event: None,
|
||||
active_datetime: stage_datetime(
|
||||
execution_date,
|
||||
default_stage_time(ScheduleStage::Settlement),
|
||||
),
|
||||
order_events,
|
||||
fills,
|
||||
},
|
||||
rate,
|
||||
)?
|
||||
.unwrap_or_else(|| portfolio.default_management_fee());
|
||||
if fee <= 0.0 {
|
||||
return Ok(BrokerExecutionReport::default());
|
||||
}
|
||||
|
||||
let cash_before = portfolio.cash();
|
||||
portfolio
|
||||
.apply_management_fee(fee)
|
||||
.map_err(BacktestError::Execution)?;
|
||||
let mut report = BrokerExecutionReport::default();
|
||||
report.account_events.push(AccountEvent {
|
||||
date: execution_date,
|
||||
cash_before,
|
||||
cash_after: portfolio.cash(),
|
||||
total_equity: portfolio.total_equity(),
|
||||
note: format!("management_fee rate={rate:.6} fee={fee:.2}"),
|
||||
});
|
||||
publish_custom_process_event(
|
||||
&mut self.strategy,
|
||||
&mut self.process_event_bus,
|
||||
execution_date,
|
||||
decision_date,
|
||||
decision_index,
|
||||
&self.data,
|
||||
&*portfolio,
|
||||
open_orders,
|
||||
dynamic_universe,
|
||||
subscriptions,
|
||||
process_events,
|
||||
ProcessEvent {
|
||||
date: execution_date,
|
||||
kind: ProcessEventKind::AccountManagementFee,
|
||||
order_id: None,
|
||||
symbol: None,
|
||||
side: None,
|
||||
detail: format!(
|
||||
"rate={rate:.6} fee={fee:.2} cash_before={cash_before:.2} cash_after={:.2} management_fees={:.2}",
|
||||
portfolio.cash(),
|
||||
portfolio.management_fees()
|
||||
),
|
||||
},
|
||||
)?;
|
||||
Ok(report)
|
||||
}
|
||||
|
||||
fn settle_delisted_positions(
|
||||
&self,
|
||||
date: NaiveDate,
|
||||
|
||||
Reference in New Issue
Block a user