修正滑点成交后的持仓估值
This commit is contained in:
@@ -31,6 +31,7 @@ pub struct BrokerExecutionReport {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct ExecutionLeg {
|
||||
price: f64,
|
||||
mark_price: f64,
|
||||
quantity: u32,
|
||||
}
|
||||
|
||||
@@ -401,19 +402,37 @@ where
|
||||
side: OrderSide,
|
||||
quantity: Option<u32>,
|
||||
) -> f64 {
|
||||
let raw_price = if self.execution_price_field == PriceField::Last
|
||||
let raw_price = self.snapshot_raw_execution_price(snapshot, side);
|
||||
self.apply_slippage(snapshot, side, raw_price, quantity)
|
||||
}
|
||||
|
||||
fn snapshot_raw_execution_price(
|
||||
&self,
|
||||
snapshot: &crate::data::DailyMarketSnapshot,
|
||||
side: OrderSide,
|
||||
) -> f64 {
|
||||
if self.execution_price_field == PriceField::Last
|
||||
&& self.intraday_execution_start_time.is_some()
|
||||
{
|
||||
let _ = side;
|
||||
snapshot.price(PriceField::Last)
|
||||
} else {
|
||||
match side {
|
||||
OrderSide::Buy => self.buy_price(snapshot),
|
||||
OrderSide::Sell => self.sell_price(snapshot),
|
||||
}
|
||||
};
|
||||
return snapshot.price(PriceField::Last);
|
||||
}
|
||||
match side {
|
||||
OrderSide::Buy => self.buy_price(snapshot),
|
||||
OrderSide::Sell => self.sell_price(snapshot),
|
||||
}
|
||||
}
|
||||
|
||||
self.apply_slippage(snapshot, side, raw_price, quantity)
|
||||
fn snapshot_mark_price(
|
||||
&self,
|
||||
snapshot: &crate::data::DailyMarketSnapshot,
|
||||
side: OrderSide,
|
||||
) -> f64 {
|
||||
let price = snapshot.price(self.execution_price_field);
|
||||
if price.is_finite() && price > 0.0 {
|
||||
price
|
||||
} else {
|
||||
self.snapshot_raw_execution_price(snapshot, side)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_open_auction_matching(&self) -> bool {
|
||||
@@ -573,6 +592,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn quote_mark_price(&self, quote: &IntradayExecutionQuote, fallback: f64) -> f64 {
|
||||
if quote.last_price.is_finite() && quote.last_price > 0.0 {
|
||||
quote.last_price
|
||||
} else {
|
||||
fallback
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute(
|
||||
&self,
|
||||
date: NaiveDate,
|
||||
@@ -2503,6 +2530,7 @@ where
|
||||
fillable_qty,
|
||||
vec![ExecutionLeg {
|
||||
price: execution_price,
|
||||
mark_price: self.snapshot_mark_price(snapshot, OrderSide::Sell),
|
||||
quantity: fillable_qty,
|
||||
}],
|
||||
)
|
||||
@@ -2588,7 +2616,7 @@ where
|
||||
let net_cash = gross_amount - cost.total();
|
||||
let realized_pnl = portfolio
|
||||
.position_mut(symbol)
|
||||
.sell(leg.quantity, leg.price)
|
||||
.sell_with_mark_price(leg.quantity, leg.price, leg.mark_price)
|
||||
.map_err(BacktestError::Execution)?;
|
||||
if let Some(position) = portfolio.position_mut_if_exists(symbol) {
|
||||
position.record_trade_cost(cost.total());
|
||||
@@ -3909,6 +3937,7 @@ where
|
||||
filled_qty,
|
||||
vec![ExecutionLeg {
|
||||
price: execution_price,
|
||||
mark_price: self.snapshot_mark_price(snapshot, OrderSide::Buy),
|
||||
quantity: filled_qty,
|
||||
}],
|
||||
)
|
||||
@@ -3995,9 +4024,12 @@ where
|
||||
let cash_out = gross_amount + cost.total();
|
||||
|
||||
portfolio.apply_cash_delta(-cash_out);
|
||||
portfolio
|
||||
.position_mut(symbol)
|
||||
.buy(date, leg.quantity, leg.price);
|
||||
portfolio.position_mut(symbol).buy_with_mark_price(
|
||||
date,
|
||||
leg.quantity,
|
||||
leg.price,
|
||||
leg.mark_price,
|
||||
);
|
||||
if let Some(position) = portfolio.position_mut_if_exists(symbol) {
|
||||
position.record_buy_trade_cost(leg.quantity, cost.total());
|
||||
}
|
||||
@@ -4700,6 +4732,7 @@ where
|
||||
};
|
||||
let mut filled_qty = 0_u32;
|
||||
let mut gross_amount = 0.0_f64;
|
||||
let mut mark_amount = 0.0_f64;
|
||||
let mut last_timestamp = None;
|
||||
let mut legs = Vec::new();
|
||||
let mut budget_block_reason = None;
|
||||
@@ -4717,6 +4750,7 @@ where
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let mark_price = self.quote_mark_price(quote, raw_quote_price);
|
||||
let remaining_qty = requested_qty.saturating_sub(filled_qty);
|
||||
if remaining_qty == 0 {
|
||||
break;
|
||||
@@ -4815,10 +4849,12 @@ where
|
||||
quote_price = self.execution_price_with_limit_slippage(quote_price, limit_price);
|
||||
|
||||
gross_amount += quote_price * take_qty as f64;
|
||||
mark_amount += mark_price * take_qty as f64;
|
||||
filled_qty += take_qty;
|
||||
last_timestamp = Some(quote.timestamp);
|
||||
legs.push(ExecutionLeg {
|
||||
price: quote_price,
|
||||
mark_price,
|
||||
quantity: take_qty,
|
||||
});
|
||||
|
||||
@@ -4849,6 +4885,7 @@ where
|
||||
legs: if matching_type == MatchingType::Vwap {
|
||||
vec![ExecutionLeg {
|
||||
price: gross_amount / filled_qty as f64,
|
||||
mark_price: mark_amount / filled_qty as f64,
|
||||
quantity: filled_qty,
|
||||
}]
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user