matchcore 0.4.0

A high-performance order book and price-time matching engine implemented as a single-threaded, deterministic, in-memory state machine
Documentation
use super::OrderBook;
use crate::{OrderId, OrderKind, SequenceNumber, command::*, outcome::*};

impl OrderBook {
    /// Execute a cancel command against the order book and return the execution outcome
    pub(super) fn execute_cancel(
        &mut self,
        sequence_number: SequenceNumber,
        cmd: &CancelCmd,
    ) -> CommandOutcome {
        let result = match &cmd.order_kind {
            OrderKind::Limit => self.cancel_limit_order(sequence_number, cmd.order_id),
            OrderKind::Pegged => self.cancel_pegged_order(cmd.order_id),
            OrderKind::PriceConditional => self.cancel_price_conditional_order(cmd.order_id),
        };

        match result {
            Ok(_) => CommandOutcome::Applied(CommandReport::Cancel),
            Err(reason) => CommandOutcome::Rejected(reason),
        }
    }

    /// Cancel a limit order
    fn cancel_limit_order(
        &mut self,
        sequence_number: SequenceNumber,
        id: OrderId,
    ) -> Result<(), CommandFailure> {
        self.remove_limit_order(sequence_number, id)
            .ok_or(CommandFailure::OrderNotFound)?;

        Ok(())
    }

    /// Cancel a pegged order
    fn cancel_pegged_order(&mut self, id: OrderId) -> Result<(), CommandFailure> {
        self.remove_pegged_order(id)
            .ok_or(CommandFailure::OrderNotFound)?;

        Ok(())
    }

    /// Cancel a price-conditional order
    fn cancel_price_conditional_order(&mut self, id: OrderId) -> Result<(), CommandFailure> {
        self.remove_price_conditional_order(id)
            .ok_or(CommandFailure::OrderNotFound)?;

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::*;

    fn cancel(
        book: &mut OrderBook,
        sequence_number: SequenceNumber,
        order_id: OrderId,
        order_kind: OrderKind,
    ) -> CommandOutcome {
        book.execute_cancel(
            sequence_number,
            &CancelCmd {
                order_id,
                order_kind,
            },
        )
    }

    #[test]
    fn cancel_limit_order_success() {
        let mut book = OrderBook::new("TEST");
        book.add_limit_order(
            SequenceNumber(0),
            OrderId(0),
            LimitOrder::new(
                Price(100),
                QuantityPolicy::Standard {
                    quantity: Quantity(10),
                },
                OrderFlags::new(Side::Buy, false, TimeInForce::Gtc),
            ),
        );

        let outcome = cancel(&mut book, SequenceNumber(1), OrderId(0), OrderKind::Limit);

        match outcome {
            CommandOutcome::Applied(CommandReport::Cancel) => {}
            other => panic!("expected applied cancel, got: {other:?}"),
        }
        assert!(!book.limit.orders.contains_key(&OrderId(0)));
        assert!(!book.limit.bids.contains_key(&Price(100)));
    }

    #[test]
    fn cancel_limit_order_not_found() {
        let mut book = OrderBook::new("TEST");

        let outcome = cancel(
            &mut book,
            SequenceNumber(1000),
            OrderId(999),
            OrderKind::Limit,
        );

        match outcome {
            CommandOutcome::Rejected(CommandFailure::OrderNotFound) => {}
            other => panic!("expected order not found, got: {other:?}"),
        }
    }

    #[test]
    fn cancel_pegged_order_success() {
        let mut book = OrderBook::new("TEST");
        book.add_pegged_order(
            SequenceNumber(0),
            OrderId(0),
            PeggedOrder::new(
                PegReference::Primary,
                Quantity(10),
                OrderFlags::new(Side::Buy, false, TimeInForce::Gtc),
            ),
        );

        let outcome = cancel(&mut book, SequenceNumber(1), OrderId(0), OrderKind::Pegged);

        match outcome {
            CommandOutcome::Applied(CommandReport::Cancel) => {}
            other => panic!("expected applied cancel, got: {other:?}"),
        }
        assert!(!book.pegged.orders.contains_key(&OrderId(0)));
    }

    #[test]
    fn cancel_pegged_order_not_found() {
        let mut book = OrderBook::new("TEST");

        let outcome = cancel(
            &mut book,
            SequenceNumber(1000),
            OrderId(999),
            OrderKind::Pegged,
        );

        match outcome {
            CommandOutcome::Rejected(CommandFailure::OrderNotFound) => {}
            other => panic!("expected order not found, got: {other:?}"),
        }
    }

    #[test]
    fn cancel_price_conditional_order_success() {
        let mut book = OrderBook::new("TEST");
        book.add_price_conditional_order(
            SequenceNumber(0),
            OrderId(0),
            PriceConditionalOrder::new(
                PriceCondition::new(Price(100), TriggerDirection::AtOrAbove),
                TriggerOrder::Market(MarketOrder::new(Quantity(10), Side::Buy, false)),
            ),
        );

        let outcome = cancel(
            &mut book,
            SequenceNumber(1),
            OrderId(0),
            OrderKind::PriceConditional,
        );

        match outcome {
            CommandOutcome::Applied(CommandReport::Cancel) => {}
            other => panic!("expected applied cancel, got: {other:?}"),
        }
        assert!(!book.price_conditional.orders.contains_key(&OrderId(0)));
    }

    #[test]
    fn cancel_price_conditional_order_not_found() {
        let mut book = OrderBook::new("TEST");

        let outcome = cancel(
            &mut book,
            SequenceNumber(1000),
            OrderId(999),
            OrderKind::PriceConditional,
        );

        match outcome {
            CommandOutcome::Rejected(CommandFailure::OrderNotFound) => {}
            other => panic!("expected order not found, got: {other:?}"),
        }
    }

    #[test]
    fn cancel_limit_order_with_wrong_kind_returns_not_found() {
        let mut book = OrderBook::new("TEST");
        book.add_limit_order(
            SequenceNumber(0),
            OrderId(0),
            LimitOrder::new(
                Price(100),
                QuantityPolicy::Standard {
                    quantity: Quantity(10),
                },
                OrderFlags::new(Side::Buy, false, TimeInForce::Gtc),
            ),
        );

        // Requesting Pegged for a Limit order looks in pegged book, finds nothing
        let outcome = cancel(&mut book, SequenceNumber(1), OrderId(0), OrderKind::Pegged);

        match outcome {
            CommandOutcome::Rejected(CommandFailure::OrderNotFound) => {}
            other => panic!("expected order not found, got: {other:?}"),
        }
        // Limit order should still exist
        assert!(book.limit.orders.contains_key(&OrderId(0)));
    }
}