use crate::{OrderId, Price, Quantity, Side, TimeInForce, Timestamp};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum OrderStatus {
#[default]
New,
PartiallyFilled,
Filled,
Cancelled,
}
impl OrderStatus {
#[inline]
pub fn is_active(self) -> bool {
matches!(self, OrderStatus::New | OrderStatus::PartiallyFilled)
}
#[inline]
pub fn is_terminal(self) -> bool {
matches!(self, OrderStatus::Filled | OrderStatus::Cancelled)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Order {
pub id: OrderId,
pub side: Side,
pub price: Price,
pub original_quantity: Quantity,
pub remaining_quantity: Quantity,
pub filled_quantity: Quantity,
pub timestamp: Timestamp,
pub time_in_force: TimeInForce,
pub status: OrderStatus,
pub(crate) position_in_level: usize,
}
impl Order {
pub fn new(
id: OrderId,
side: Side,
price: Price,
quantity: Quantity,
timestamp: Timestamp,
time_in_force: TimeInForce,
) -> Self {
Self {
id,
side,
price,
original_quantity: quantity,
remaining_quantity: quantity,
filled_quantity: 0,
timestamp,
time_in_force,
status: OrderStatus::New,
position_in_level: 0,
}
}
#[inline]
pub fn is_active(&self) -> bool {
self.status.is_active()
}
pub fn fill(&mut self, quantity: Quantity) {
assert!(
quantity <= self.remaining_quantity,
"fill quantity {} exceeds remaining {}",
quantity,
self.remaining_quantity
);
self.remaining_quantity -= quantity;
self.filled_quantity += quantity;
self.status = if self.remaining_quantity == 0 {
OrderStatus::Filled
} else {
OrderStatus::PartiallyFilled
};
}
pub fn cancel(&mut self) -> Quantity {
assert!(
self.is_active(),
"cannot cancel order in terminal state {:?}",
self.status
);
let cancelled = self.remaining_quantity;
self.remaining_quantity = 0;
self.status = OrderStatus::Cancelled;
cancelled
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_order(quantity: Quantity) -> Order {
Order::new(
OrderId(1),
Side::Buy,
Price(100_00),
quantity,
1,
TimeInForce::GTC,
)
}
#[test]
fn new_order_initial_state() {
let order = make_order(100);
assert_eq!(order.original_quantity, 100);
assert_eq!(order.remaining_quantity, 100);
assert_eq!(order.filled_quantity, 0);
assert_eq!(order.status, OrderStatus::New);
assert!(order.is_active());
}
#[test]
fn partial_fill() {
let mut order = make_order(100);
order.fill(30);
assert_eq!(order.remaining_quantity, 70);
assert_eq!(order.filled_quantity, 30);
assert_eq!(order.status, OrderStatus::PartiallyFilled);
assert!(order.is_active());
}
#[test]
fn full_fill() {
let mut order = make_order(100);
order.fill(100);
assert_eq!(order.remaining_quantity, 0);
assert_eq!(order.filled_quantity, 100);
assert_eq!(order.status, OrderStatus::Filled);
assert!(!order.is_active());
}
#[test]
fn multiple_partial_fills() {
let mut order = make_order(100);
order.fill(30);
order.fill(50);
order.fill(20);
assert_eq!(order.remaining_quantity, 0);
assert_eq!(order.filled_quantity, 100);
assert_eq!(order.status, OrderStatus::Filled);
}
#[test]
#[should_panic(expected = "fill quantity 101 exceeds remaining 100")]
fn fill_exceeds_remaining_panics() {
let mut order = make_order(100);
order.fill(101);
}
#[test]
fn cancel_new_order() {
let mut order = make_order(100);
let cancelled = order.cancel();
assert_eq!(cancelled, 100);
assert_eq!(order.remaining_quantity, 0);
assert_eq!(order.status, OrderStatus::Cancelled);
assert!(!order.is_active());
}
#[test]
fn cancel_partially_filled_order() {
let mut order = make_order(100);
order.fill(30);
let cancelled = order.cancel();
assert_eq!(cancelled, 70);
assert_eq!(order.filled_quantity, 30);
assert_eq!(order.remaining_quantity, 0);
assert_eq!(order.status, OrderStatus::Cancelled);
}
#[test]
#[should_panic(expected = "cannot cancel order in terminal state")]
fn cancel_filled_order_panics() {
let mut order = make_order(100);
order.fill(100);
order.cancel();
}
#[test]
#[should_panic(expected = "cannot cancel order in terminal state")]
fn cancel_already_cancelled_panics() {
let mut order = make_order(100);
order.cancel();
order.cancel();
}
#[test]
fn order_status_is_active() {
assert!(OrderStatus::New.is_active());
assert!(OrderStatus::PartiallyFilled.is_active());
assert!(!OrderStatus::Filled.is_active());
assert!(!OrderStatus::Cancelled.is_active());
}
#[test]
fn order_status_is_terminal() {
assert!(!OrderStatus::New.is_terminal());
assert!(!OrderStatus::PartiallyFilled.is_terminal());
assert!(OrderStatus::Filled.is_terminal());
assert!(OrderStatus::Cancelled.is_terminal());
}
#[test]
fn quantity_invariant_holds() {
let mut order = make_order(100);
order.fill(30);
assert_eq!(
order.original_quantity,
order.remaining_quantity + order.filled_quantity
);
order.fill(50);
assert_eq!(
order.original_quantity,
order.remaining_quantity + order.filled_quantity
);
}
}