统一策略成交保护与锁定周期并修正日期条件覆盖
This commit is contained in:
@@ -60,6 +60,8 @@ pub struct PositionLot {
|
||||
pub struct Position {
|
||||
pub symbol: String,
|
||||
pub quantity: u32,
|
||||
opened_date: Option<NaiveDate>,
|
||||
last_buy_date: Option<NaiveDate>,
|
||||
// ALV-compatible moving average execution price; partial sells do not rebase it.
|
||||
pub average_price: f64,
|
||||
// ALV-compatible moving average including buy costs; partial sells do not rebase it.
|
||||
@@ -88,6 +90,8 @@ impl Position {
|
||||
Self {
|
||||
symbol: symbol.into(),
|
||||
quantity: 0,
|
||||
opened_date: None,
|
||||
last_buy_date: None,
|
||||
average_price: 0.0,
|
||||
average_cost: 0.0,
|
||||
last_price: 0.0,
|
||||
@@ -114,6 +118,12 @@ impl Position {
|
||||
self.quantity == 0
|
||||
}
|
||||
|
||||
pub fn opened_date(&self) -> Option<NaiveDate> {
|
||||
self.opened_date
|
||||
}
|
||||
|
||||
pub fn last_buy_date(&self) -> Option<NaiveDate> { self.last_buy_date }
|
||||
|
||||
pub fn buy(&mut self, date: NaiveDate, quantity: u32, price: f64) {
|
||||
self.buy_with_mark_price(date, quantity, price, price);
|
||||
}
|
||||
@@ -130,6 +140,10 @@ impl Position {
|
||||
}
|
||||
|
||||
let previous_quantity = self.quantity;
|
||||
self.last_buy_date = Some(self.last_buy_date.map_or(date, |previous| previous.max(date)));
|
||||
if previous_quantity == 0 {
|
||||
self.opened_date = Some(date);
|
||||
}
|
||||
let previous_average_price = self.average_price;
|
||||
let previous_average_cost = self.average_cost;
|
||||
let gross_amount = fixed_money_or_panic(
|
||||
@@ -267,6 +281,7 @@ impl Position {
|
||||
.checked_add(total_proceeds)
|
||||
.ok_or_else(|| "fixed-point day sell value overflow".to_string())?;
|
||||
if self.quantity == 0 {
|
||||
self.opened_date = None;
|
||||
self.average_price = 0.0;
|
||||
self.recalculate_average_cost();
|
||||
} else {
|
||||
@@ -1224,6 +1239,8 @@ impl PortfolioState {
|
||||
}
|
||||
|
||||
let old_quantity = old_position.quantity;
|
||||
let old_opened_date = old_position.opened_date;
|
||||
let old_last_buy_date = old_position.last_buy_date;
|
||||
let last_price = old_position.last_price;
|
||||
let old_average_price = old_position.average_price;
|
||||
let old_average_cost = old_position.average_cost;
|
||||
@@ -1263,6 +1280,14 @@ impl PortfolioState {
|
||||
.entry(new_symbol.to_string())
|
||||
.or_insert_with(|| Position::new(new_symbol));
|
||||
let successor_quantity_before = successor.quantity;
|
||||
successor.opened_date = match (successor.opened_date, old_opened_date) {
|
||||
(Some(current), Some(previous)) => Some(current.min(previous)),
|
||||
(current, previous) => current.or(previous),
|
||||
};
|
||||
successor.last_buy_date = match (successor.last_buy_date, old_last_buy_date) {
|
||||
(Some(current), Some(previous)) => Some(current.max(previous)),
|
||||
(current, previous) => current.or(previous),
|
||||
};
|
||||
let successor_average_price_before = successor.average_price;
|
||||
let successor_average_cost_before = successor.average_cost;
|
||||
successor.lots.extend(converted_lots);
|
||||
|
||||
Reference in New Issue
Block a user