mod primitives;
use itertools::Itertools;
use stable_vec::StableVec;
use std::collections::{HashMap, VecDeque};
use thiserror::Error;
pub use primitives::{Oid, OrderSide, OrderType, Price, Timestamp, Volume};
type LevelIndex = usize;
type LevelVec = StableVec<Level>;
type LevelMap = HashMap<Price, LevelIndex>;
type OrderMap = HashMap<Oid, LimitOrder>;
#[derive(Debug, PartialEq, PartialOrd, Clone)]
pub struct Order {
id: Oid,
pub side: OrderSide,
kind: OrderType,
pub price: Option<Price>,
pub volume: Volume,
timestamp: Timestamp,
}
impl Order {
pub fn new_limit(
id: Oid,
side: OrderSide,
timestamp: Timestamp,
price: Price,
volume: Volume,
) -> Self {
Order {
id,
side,
kind: OrderType::Limit,
timestamp,
price: Some(price),
volume,
}
}
pub fn new_market(id: Oid, side: OrderSide, timestamp: Timestamp, volume: Volume) -> Self {
Order {
id,
side,
kind: OrderType::Market,
timestamp,
price: None,
volume,
}
}
}
#[derive(Debug, PartialEq, PartialOrd, Clone)]
pub struct LimitOrder {
id: Oid,
side: OrderSide,
timestamp: Timestamp,
price: Price,
volume: Volume,
filled_volume: Option<Volume>,
}
pub enum TryFromOrderError {
OrderTypeNotLimit,
}
impl TryFrom<&Order> for LimitOrder {
type Error = TryFromOrderError;
fn try_from(order: &Order) -> Result<Self, Self::Error> {
match order.kind {
OrderType::Limit => Ok(LimitOrder {
id: order.id,
side: order.side,
timestamp: order.timestamp,
price: order.price.unwrap(), volume: order.volume,
filled_volume: None,
}),
_ => Err(TryFromOrderError::OrderTypeNotLimit),
}
}
}
impl LimitOrder {
pub fn new(
id: Oid,
side: OrderSide,
timestamp: Timestamp,
price: Price,
volume: Volume,
) -> Self {
LimitOrder {
id,
side,
timestamp,
price,
volume,
filled_volume: None,
}
}
}
#[derive(Debug, Clone)]
pub struct Level {
price: Price,
total_volume: Volume,
orders: VecDeque<Oid>,
depth: usize, }
impl Eq for Level {}
impl PartialEq for Level {
fn eq(&self, other: &Self) -> bool {
self.price == other.price
}
}
impl PartialOrd for Level {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Level {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.price.cmp(&other.price)
}
}
impl Level {
pub fn new(price: Price) -> Level {
Level {
price,
total_volume: 0.into(),
orders: VecDeque::new(),
depth: 0,
}
}
pub fn add_order(&mut self, order: &LimitOrder) {
{
self.total_volume += order.volume;
self.depth += 1;
}
self.orders.push_back(order.id);
}
pub fn cancell_order(&mut self, order: &LimitOrder) {
self.total_volume -= order.volume;
self.depth -= 1;
}
}
#[derive(Debug, Default)]
pub struct Limits {
levels: LevelVec,
level_map: LevelMap,
best: Option<LevelIndex>,
}
impl Limits {
pub fn get_best_limit(&self) -> Option<Price> {
if let Some(index) = self.best {
self.levels.get(index).map(|l| l.price)
} else {
None
}
}
pub fn add_order(&mut self, order: &LimitOrder) {
let price = &order.price;
match self.level_map.get(price) {
None => {
let mut level = Level::new(*price);
level.add_order(order);
let index = self.levels.push(level);
self.level_map.insert(*price, index);
}
Some(index) => {
if let Some(level) = self.levels.get_mut(*index) {
level.add_order(order);
}
}
}
}
pub fn cancel_order(&mut self, order: &LimitOrder) {
if let Some(index) = self.level_map.get(&order.price) {
if let Some(level) = self.levels.get_mut(*index) {
level.cancell_order(order);
}
}
}
}
#[derive(Error, Debug, PartialEq, PartialOrd, Clone)]
pub enum PlaceOrderError {
#[error("Order cannot be placed: {0}")]
OrderCannotBePlaced(String),
}
#[derive(Debug)]
#[allow(dead_code)]
pub struct Trade {
order_id: Oid,
volume: Volume,
filled_volume: Volume,
executions: Vec<Execution>,
}
impl Trade {
pub fn new(order_id: Oid, volume: Volume) -> Self {
Trade {
order_id,
volume,
filled_volume: Volume::ZERO,
executions: Vec::new(),
}
}
pub fn add_execution(&mut self, execution: Execution) {
self.filled_volume += execution.volume;
self.executions.push(execution)
}
}
#[derive(Debug, PartialEq, PartialOrd, Clone)]
pub struct Execution {
order_id: Oid,
price: Price,
volume: Volume,
}
impl Execution {
pub fn new(order_id: Oid, price: Price, volume: Volume) -> Self {
Execution {
order_id,
price,
volume,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CancellationStatus {
Cancelled,
NotCancelled(String),
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct CancellationReport {
order_id: Oid,
status: CancellationStatus,
}
#[derive(Error, Debug, PartialEq, PartialOrd, Clone)]
pub enum CancelOrderError {
#[error("Order {0} not found")]
NotFound(Oid),
#[error("Order {0} already cancelled")]
AlreadyCancelled(Oid),
}
#[derive(Debug, PartialEq, PartialOrd, Clone)]
pub struct Spread(f64);
#[derive(Debug, Default)]
pub struct OrderBook {
bids: Limits,
asks: Limits,
orders: OrderMap,
spread: Option<Spread>,
}
impl OrderBook {
pub fn execute(&mut self, order: &Order) -> Result<Trade, PlaceOrderError> {
let trade = match order.side {
OrderSide::Buy => self.execute_buy(order),
OrderSide::Sell => self.execute_sell(order),
}?;
self.update_best_limit();
self.update_spreads();
Ok(trade)
}
fn update_spreads(&mut self) {
let ask_best_limit = self.asks.get_best_limit();
let bid_best_limit = self.bids.get_best_limit();
match (ask_best_limit, bid_best_limit) {
(Some(ask_limit), Some(bid_limit)) => {
self.spread = Some(Spread((ask_limit - bid_limit).into()));
}
_ => {
self.spread = None;
}
}
}
fn execute_buy(&mut self, order: &Order) -> Result<Trade, PlaceOrderError> {
let trade = self.fill_buy_order(order)?;
match order.kind {
OrderType::Market => {
}
OrderType::Limit => {
if trade.filled_volume < order.volume {
let limit_order = LimitOrder::try_from(order).map_err(|_| {
PlaceOrderError::OrderCannotBePlaced("not an market order".to_string())
})?;
self.bids.add_order(&limit_order);
self.orders.insert(limit_order.id, limit_order);
}
}
}
Ok(trade)
}
fn update_best_limit(&mut self) {
if let Some(max) = self
.bids
.levels
.values()
.filter(|l| l.total_volume > 0.into())
.max()
{
self.bids.best = self.bids.level_map.get(&max.price).copied();
}
if let Some(min) = self
.asks
.levels
.values()
.filter(|l| l.total_volume > 0.into())
.min()
{
self.asks.best = self.asks.level_map.get(&min.price).copied();
}
}
fn execute_sell(&mut self, order: &Order) -> Result<Trade, PlaceOrderError> {
let trade = self.fill_sell_order(order)?;
match order.kind {
OrderType::Market => {
}
OrderType::Limit => {
if trade.filled_volume < order.volume {
let limit_order = LimitOrder::try_from(order).map_err(|_| {
PlaceOrderError::OrderCannotBePlaced("not an market order".to_string())
})?;
self.asks.add_order(&limit_order);
self.orders.insert(limit_order.id, limit_order);
}
}
}
Ok(trade)
}
fn fill_buy_order(&mut self, buy_order: &Order) -> Result<Trade, PlaceOrderError> {
let mut buy_volume = buy_order.volume;
let mut trade = Trade::new(buy_order.id, buy_order.volume);
let sorted = self
.asks
.levels
.values_mut()
.filter(|l| filter_limit_for_buy(l, &buy_order.price))
.sorted();
'top: for l in sorted {
while let Some(oid) = l.orders.front() {
let Some(mut sell_order) = self.orders.remove(oid) else {
l.orders.pop_front();
continue;
};
let sell_volume = sell_order.volume;
if sell_volume <= buy_volume {
trade.add_execution(Execution::new(
sell_order.id,
sell_order.price,
sell_volume,
));
l.orders.pop_front();
l.cancell_order(&sell_order);
sell_order.volume = Volume::ZERO;
buy_volume -= sell_volume;
} else {
let execution = Execution::new(sell_order.id, sell_order.price, buy_volume);
trade.add_execution(execution);
sell_order.volume -= buy_volume;
buy_volume = Volume::ZERO;
}
if !sell_order.volume.is_zero() {
self.orders.insert(sell_order.id, sell_order);
}
if buy_volume.is_zero() {
break 'top;
}
} }
Ok(trade)
}
fn fill_sell_order(&mut self, sell_order: &Order) -> Result<Trade, PlaceOrderError> {
let mut sell_volume = sell_order.volume;
let mut trade = Trade::new(sell_order.id, sell_order.volume);
let sorted = self
.bids
.levels
.values_mut()
.filter(|l| filter_limit_for_sell(l, &sell_order.price))
.sorted_by(sort_limit_descending);
'top: for l in sorted {
while let Some(oid) = l.orders.front() {
let Some(mut buy_order) = self.orders.remove(oid) else {
l.orders.pop_front();
continue;
};
let buy_volume = buy_order.volume;
if buy_volume <= sell_volume {
trade.add_execution(Execution::new(buy_order.id, buy_order.price, buy_volume));
l.orders.pop_front();
l.cancell_order(&buy_order);
buy_order.volume = Volume::ZERO;
sell_volume -= buy_volume;
} else {
let execution = Execution::new(buy_order.id, buy_order.price, sell_volume);
trade.add_execution(execution);
buy_order.volume -= sell_volume;
sell_volume = Volume::ZERO;
}
if !buy_order.volume.is_zero() {
self.orders.insert(buy_order.id, buy_order);
}
if sell_volume.is_zero() {
break 'top;
}
}
}
Ok(trade)
}
pub fn cancel_order(&mut self, order_id: Oid) -> Result<CancellationReport, CancelOrderError> {
match self.orders.remove(&order_id) {
None => return Err(CancelOrderError::NotFound(order_id)),
Some(order) => {
match order.side {
OrderSide::Buy => self.bids.cancel_order(&order),
OrderSide::Sell => self.asks.cancel_order(&order),
}
}
}
Ok(CancellationReport {
order_id,
status: CancellationStatus::Cancelled,
})
}
pub fn get_volume_at_limit(&self, limit: Price, side: OrderSide) -> Option<Volume> {
let limit_map = match side {
OrderSide::Buy => &self.bids,
OrderSide::Sell => &self.asks,
};
limit_map
.level_map
.get(&limit)
.map(|index| limit_map.levels[*index].total_volume)
}
}
#[inline]
#[allow(clippy::needless_lifetimes)]
fn sort_limit_descending<'a, 'b>(l: &'a &mut Level, r: &'b &mut Level) -> std::cmp::Ordering {
l.price.cmp(&r.price).reverse()
}
#[inline]
#[allow(clippy::needless_lifetimes)]
fn filter_limit_for_buy<'a>(l: &'a &mut Level, price: &Option<Price>) -> bool {
if l.total_volume > 0.into() {
return price.map(|p| l.price <= p).unwrap_or(true);
}
false
}
#[inline]
#[allow(clippy::needless_lifetimes)]
fn filter_limit_for_sell<'a>(l: &'a &mut Level, price: &Option<Price>) -> bool {
if l.total_volume > 0.into() {
return price.map(|p| l.price >= p).unwrap_or(true);
}
false
}
mod tests_limit_map {
#[test]
fn test_limit_map() {
let mut limit_map = crate::Limits::default();
let order = crate::LimitOrder::new(
crate::primitives::Oid::new(1),
crate::OrderSide::Buy,
crate::primitives::Timestamp::new(1),
21.0453.into(),
100.into(),
);
limit_map.add_order(&order);
}
}
mod tests_order_book {
#[test]
fn test_order_book_new() {
let order_book = crate::OrderBook::default();
assert_eq!(order_book.bids.best, None);
assert_eq!(order_book.asks.best, None);
assert_eq!(order_book.orders.len(), 0);
assert_eq!(order_book.spread, None);
}
#[test]
fn test_cancel_order() {
let mut order_book = crate::OrderBook::default();
let order = &crate::Order::new_limit(
crate::primitives::Oid::new(1),
crate::OrderSide::Buy,
chrono::Utc::now().into(),
21.0453.into(),
100.into(),
);
let _ = order_book.execute(order);
assert_eq!(order_book.orders.len(), 1);
let order = order_book
.cancel_order(crate::primitives::Oid::new(1))
.unwrap();
assert_eq!(order_book.orders.len(), 0);
assert_eq!(order.order_id, crate::primitives::Oid::new(1));
assert_eq!(order.status, crate::CancellationStatus::Cancelled);
let order = &crate::Order::new_limit(
crate::primitives::Oid::new(2),
crate::OrderSide::Buy,
chrono::Utc::now().into(),
21.0453.into(),
50.into(),
);
let _ = order_book.execute(order);
assert_eq!(order_book.orders.len(), 1);
let order = order_book
.cancel_order(crate::primitives::Oid::new(2))
.unwrap();
assert_eq!(order_book.orders.len(), 0);
assert_eq!(order.order_id, crate::primitives::Oid::new(2));
assert_eq!(order.status, crate::CancellationStatus::Cancelled);
}
#[test]
fn test_execute_buy_order() {
let mut order_book = crate::OrderBook::default();
let order = &crate::Order::new_limit(
crate::primitives::Oid::new(1),
crate::OrderSide::Sell,
chrono::Utc::now().into(),
21.0453.into(),
100.into(),
);
let trade = order_book.execute(order).unwrap();
assert_eq!(trade.order_id, crate::primitives::Oid::new(1));
assert_eq!(trade.volume, 100.into());
assert_eq!(trade.filled_volume, 0.into());
assert_eq!(trade.executions.len(), 0);
let order = &crate::Order::new_limit(
crate::primitives::Oid::new(3),
crate::OrderSide::Sell,
chrono::Utc::now().into(),
21.0454.into(),
50.into(),
);
let trade = order_book.execute(order).unwrap();
assert_eq!(trade.order_id, crate::primitives::Oid::new(3));
assert_eq!(trade.volume, 50.into());
assert_eq!(trade.filled_volume, 0.into());
assert_eq!(trade.executions.len(), 0);
let order = &crate::Order::new_limit(
crate::primitives::Oid::new(2),
crate::OrderSide::Buy,
chrono::Utc::now().into(),
21.0455.into(),
125.into(),
);
let trade = order_book.execute(order).unwrap();
assert_eq!(trade.order_id, crate::primitives::Oid::new(2));
assert_eq!(trade.volume, 125.into());
assert_eq!(trade.filled_volume, 125.into());
assert_eq!(trade.executions.len(), 2);
let execution = &trade.executions[0];
assert_eq!(execution.order_id, crate::primitives::Oid::new(1));
assert_eq!(execution.price, 21.0453.into());
assert_eq!(execution.volume, 100.into());
let execution = &trade.executions[1];
assert_eq!(execution.order_id, crate::primitives::Oid::new(3));
assert_eq!(execution.price, 21.0454.into());
assert_eq!(execution.volume, 25.into());
}
#[test]
fn test_market_order_should_result_in_empty_order_book() {
let mut order_book = crate::OrderBook::default();
let order = &crate::Order::new_limit(
crate::primitives::Oid::new(1),
crate::OrderSide::Sell,
chrono::Utc::now().into(),
21.0453.into(),
100.into(),
);
let _ = order_book.execute(order);
let order = &crate::Order::new_limit(
crate::primitives::Oid::new(2),
crate::OrderSide::Sell,
chrono::Utc::now().into(),
21.0454.into(),
50.into(),
);
let _ = order_book.execute(order);
let order = &crate::Order::new_market(
crate::primitives::Oid::new(3),
crate::OrderSide::Buy,
chrono::Utc::now().into(),
150.into(),
);
let trade = order_book.execute(order).unwrap();
assert_eq!(trade.order_id, crate::primitives::Oid::new(3));
assert_eq!(trade.volume, 150.into());
assert_eq!(trade.filled_volume, 150.into());
assert_eq!(trade.executions.len(), 2);
let execution = &trade.executions[0];
assert_eq!(execution.order_id, crate::primitives::Oid::new(1));
assert_eq!(execution.price, 21.0453.into());
assert_eq!(execution.volume, 100.into());
let execution = &trade.executions[1];
assert_eq!(execution.order_id, crate::primitives::Oid::new(2));
assert_eq!(execution.price, 21.0454.into());
assert_eq!(execution.volume, 50.into());
assert_eq!(order_book.orders.len(), 0);
}
#[test]
fn test_sell_market_order_should_result_in_empty_order_book() {
let mut order_book = crate::OrderBook::default();
let order = &crate::Order::new_limit(
crate::primitives::Oid::new(1),
crate::OrderSide::Buy,
chrono::Utc::now().into(),
21.0453.into(),
100.into(),
);
let _ = order_book.execute(order);
let order = &crate::Order::new_limit(
crate::primitives::Oid::new(2),
crate::OrderSide::Buy,
chrono::Utc::now().into(),
21.0454.into(),
50.into(),
);
let _ = order_book.execute(order);
let order = &crate::Order::new_market(
crate::primitives::Oid::new(3),
crate::OrderSide::Sell,
chrono::Utc::now().into(),
150.into(),
);
let trade = order_book.execute(order).unwrap();
assert_eq!(trade.order_id, crate::primitives::Oid::new(3));
assert_eq!(trade.volume, 150.into());
assert_eq!(trade.filled_volume, 150.into());
assert_eq!(trade.executions.len(), 2);
let execution = &trade.executions[0];
assert_eq!(execution.order_id, crate::primitives::Oid::new(2));
assert_eq!(execution.price, 21.0454.into());
assert_eq!(execution.volume, 50.into());
let execution = &trade.executions[1];
assert_eq!(execution.order_id, crate::primitives::Oid::new(1));
assert_eq!(execution.price, 21.0453.into());
assert_eq!(execution.volume, 100.into());
assert_eq!(order_book.orders.len(), 0);
}
}