修正回测部分成交终态合同
This commit is contained in:
@@ -106,6 +106,49 @@ pub struct OrderEvent {
|
||||
pub reason: String,
|
||||
}
|
||||
|
||||
impl OrderEvent {
|
||||
pub fn validate(&self) -> Result<(), String> {
|
||||
if self.symbol.trim().is_empty() || self.requested_quantity == 0 {
|
||||
return Err(format!(
|
||||
"invalid order identity/quantity order_id={:?} symbol={} requested={}",
|
||||
self.order_id, self.symbol, self.requested_quantity
|
||||
));
|
||||
}
|
||||
if self.filled_quantity > self.requested_quantity {
|
||||
return Err(format!(
|
||||
"order overfill order_id={:?} requested={} filled={}",
|
||||
self.order_id, self.requested_quantity, self.filled_quantity
|
||||
));
|
||||
}
|
||||
let quantity_valid = match self.status {
|
||||
OrderStatus::Pending => self.filled_quantity < self.requested_quantity,
|
||||
OrderStatus::Filled => self.filled_quantity == self.requested_quantity,
|
||||
OrderStatus::PartiallyFilled => {
|
||||
self.filled_quantity > 0 && self.filled_quantity < self.requested_quantity
|
||||
}
|
||||
OrderStatus::Canceled => self.filled_quantity < self.requested_quantity,
|
||||
OrderStatus::Rejected => self.filled_quantity == 0,
|
||||
};
|
||||
if !quantity_valid {
|
||||
return Err(format!(
|
||||
"order status/quantity mismatch order_id={:?} status={} requested={} filled={}",
|
||||
self.order_id,
|
||||
self.status.as_str(),
|
||||
self.requested_quantity,
|
||||
self.filled_quantity
|
||||
));
|
||||
}
|
||||
if self.reason.trim().is_empty() {
|
||||
return Err(format!(
|
||||
"order reason is empty order_id={:?} status={}",
|
||||
self.order_id,
|
||||
self.status.as_str()
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct FillEvent {
|
||||
#[serde(with = "date_format")]
|
||||
@@ -250,3 +293,44 @@ pub struct ProcessEvent {
|
||||
pub side: Option<OrderSide>,
|
||||
pub detail: String,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use chrono::NaiveDate;
|
||||
|
||||
use super::{OrderEvent, OrderSide, OrderStatus};
|
||||
|
||||
fn order_event(status: OrderStatus, filled_quantity: u32) -> OrderEvent {
|
||||
OrderEvent {
|
||||
date: NaiveDate::from_ymd_opt(2025, 1, 2).unwrap(),
|
||||
decision_date: None,
|
||||
order_created_date: None,
|
||||
execution_date: None,
|
||||
order_id: Some(1),
|
||||
symbol: "600000.SH".to_string(),
|
||||
side: OrderSide::Buy,
|
||||
requested_quantity: 100,
|
||||
filled_quantity,
|
||||
status,
|
||||
reason: "test".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn order_event_status_quantity_contract_is_explicit() {
|
||||
assert!(order_event(OrderStatus::Pending, 0).validate().is_ok());
|
||||
assert!(
|
||||
order_event(OrderStatus::PartiallyFilled, 40)
|
||||
.validate()
|
||||
.is_ok()
|
||||
);
|
||||
assert!(order_event(OrderStatus::Filled, 100).validate().is_ok());
|
||||
assert!(order_event(OrderStatus::Canceled, 40).validate().is_ok());
|
||||
assert!(order_event(OrderStatus::Rejected, 0).validate().is_ok());
|
||||
|
||||
assert!(order_event(OrderStatus::PartiallyFilled, 0).validate().is_err());
|
||||
assert!(order_event(OrderStatus::Filled, 99).validate().is_err());
|
||||
assert!(order_event(OrderStatus::Canceled, 100).validate().is_err());
|
||||
assert!(order_event(OrderStatus::Rejected, 1).validate().is_err());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user