修正部分卖出持仓成本
This commit is contained in:
@@ -117,6 +117,7 @@ impl Position {
|
|||||||
let mut remaining = quantity;
|
let mut remaining = quantity;
|
||||||
let mut realized = 0.0;
|
let mut realized = 0.0;
|
||||||
let mut realized_entry = 0.0;
|
let mut realized_entry = 0.0;
|
||||||
|
let average_cost_before_sell = self.average_cost;
|
||||||
|
|
||||||
while remaining > 0 {
|
while remaining > 0 {
|
||||||
let Some(first_lot) = self.lots.first_mut() else {
|
let Some(first_lot) = self.lots.first_mut() else {
|
||||||
@@ -141,7 +142,13 @@ impl Position {
|
|||||||
self.day_trade_quantity_delta -= quantity as i32;
|
self.day_trade_quantity_delta -= quantity as i32;
|
||||||
self.day_sell_quantity += quantity;
|
self.day_sell_quantity += quantity;
|
||||||
self.day_sell_value += execution_price * quantity as f64;
|
self.day_sell_value += execution_price * quantity as f64;
|
||||||
self.recalculate_average_cost();
|
if self.quantity == 0 {
|
||||||
|
self.recalculate_average_cost();
|
||||||
|
} else if average_cost_before_sell.is_finite() && average_cost_before_sell > 0.0 {
|
||||||
|
self.average_cost = average_cost_before_sell;
|
||||||
|
} else {
|
||||||
|
self.recalculate_average_cost();
|
||||||
|
}
|
||||||
self.refresh_day_pnl();
|
self.refresh_day_pnl();
|
||||||
Ok(realized)
|
Ok(realized)
|
||||||
}
|
}
|
||||||
@@ -955,6 +962,26 @@ mod tests {
|
|||||||
assert!((position.holding_return(6.06).unwrap() - (6.06 / 5.66 - 1.0)).abs() < 1e-12);
|
assert!((position.holding_return(6.06).unwrap() - (6.06 / 5.66 - 1.0)).abs() < 1e-12);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn partial_sell_preserves_remaining_average_cost() {
|
||||||
|
let date = NaiveDate::from_ymd_opt(2025, 1, 2).unwrap();
|
||||||
|
let mut position = Position::new("603958.SH");
|
||||||
|
position.buy(date, 800, 18.0981);
|
||||||
|
position.record_buy_trade_cost(800, 5.0);
|
||||||
|
position.buy(date, 1700, 19.4694);
|
||||||
|
position.record_buy_trade_cost(1700, 8.27451625);
|
||||||
|
position.buy(date, 200, 18.4584);
|
||||||
|
position.record_buy_trade_cost(200, 5.0);
|
||||||
|
position.buy(date, 100, 17.8378);
|
||||||
|
position.record_buy_trade_cost(100, 5.0);
|
||||||
|
let average_cost_before = position.average_cost;
|
||||||
|
|
||||||
|
position.sell(2700, 16.8331).expect("partial sell");
|
||||||
|
|
||||||
|
assert_eq!(position.quantity, 100);
|
||||||
|
assert!((position.average_cost - average_cost_before).abs() < 1e-12);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn holdings_summary_reports_entry_price_pnl_excluding_buy_commission() {
|
fn holdings_summary_reports_entry_price_pnl_excluding_buy_commission() {
|
||||||
let date = NaiveDate::from_ymd_opt(2025, 1, 2).unwrap();
|
let date = NaiveDate::from_ymd_opt(2025, 1, 2).unwrap();
|
||||||
|
|||||||
Reference in New Issue
Block a user