Lets write an orderbook in rust.
cargo init
cargo add actix_web
use actix_web::{delete, get, post, Responder};
#[get("/depth")]
pub async fn get_depth() -> impl Responder {
"Hello, world!"
}
#[post("/order")]
pub async fn create_order() -> impl Responder {
"Hello, world!"
}
#[delete("/order")]
pub async fn delete_order() -> impl Responder {
"Hello, world!"
}
cargo add serde serde_json
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct CreateOrder {
pub price: f64,
pub quantity: f64,
pub user_id: String,
pub side: Side,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum Side {
Buy,
Sell
}
#[derive(Debug, Serialize, Deserialize)]
pub struct DeleteOrder {
pub order_id: String,
pub user_id: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CreateOrderResponse {
pub order_id: String,
pub filled_quantity: f64,
pub remaining_quantity: f64,
pub average_price: f64,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct DeleteOrderResponse {
pub success: bool,
pub remaining_quantity: f64,
pub filled_quantity: f64,
pub average_price: f64,
}
use std::collections::{BTreeMap, HashMap};
use serde::{Deserialize, Serialize};
use crate::types::{CreateOrder, DeleteOrder, Side};
#[derive(Clone)]
pub struct OpenOrder {
pub price: f64,
pub quantity: f64,
pub side: Side,
pub user_id: String,
pub order_id: String,
pub filled_quantity: f64,
}
pub struct Orderbook {
pub bids: HashMap<String, Vec<OpenOrder>>,
pub asks: HashMap<String, Vec<OpenOrder>>,
pub order_id_index: u64,
}
#[derive(Clone, Serialize, Deserialize)]
pub struct Depth {
pub price: f64,
pub quantity: f64,
}
pub struct DepthResponse {
pub bids: Vec<Depth>,
pub asks: Vec<Depth>,
}
impl Default for Orderbook {
fn default() -> Self {
Self {
bids: HashMap::new(),
asks: HashMap::new(),
order_id_index: 0
}
}
}
impl Orderbook {
pub fn create_order(&mut self, order: CreateOrder) {
let order_id = self.order_id_index.to_string();
self.order_id_index += 1;
match order.side {
Side::Buy => {
let open_order = OpenOrder {
price: order.price,
quantity: order.quantity,
side: order.side,
user_id: order.user_id,
order_id: order_id,
filled_quantity: 0.0,
};
self.bids.entry(order.price.to_string()).or_insert(Vec::new()).push(open_order);
}
Side::Sell => {
let open_order = OpenOrder {
price: order.price,
quantity: order.quantity,
side: order.side,
user_id: order.user_id,
order_id: order_id,
filled_quantity: 0.0,
};
self.asks.entry(order.price.to_string()).or_insert(Vec::new()).push(open_order);
}
}
}
pub fn delete_order(&mut self, order: DeleteOrder) {
// Find and remove from bids
if let Some(price) = self.bids.iter().find_map(|(price, orders)| {
if orders.iter().any(|o| o.order_id == order.order_id) {
Some(price.clone())
} else {
None
}
}) {
if let Some(orders) = self.bids.get_mut(&price) {
orders.retain(|o| o.order_id != order.order_id);
}
}
// Find and remove from asks
if let Some(price) = self.asks.iter().find_map(|(price, orders)| {
if orders.iter().any(|o| o.order_id == order.order_id) {
Some(price.clone())
} else {
None
}
}) {
if let Some(orders) = self.asks.get_mut(&price) {
orders.retain(|o| o.order_id != order.order_id);
}
}
}
pub fn get_depth(&self) -> DepthResponse {
let mut bids = Vec::new();
let mut asks = Vec::new();
for (price, orders) in self.bids.iter() {
bids.push(Depth { price: price.parse().unwrap(), quantity: orders.iter().map(|o| o.quantity).sum() });
}
for (price, orders) in self.asks.iter() {
asks.push(Depth { price: price.parse().unwrap(), quantity: orders.iter().map(|o| o.quantity).sum() });
}
DepthResponse { bids, asks }
}
}
use std::sync::{Arc, Mutex};
use actix_web::{delete, get, post, web::{Data, Json}, HttpResponse, Responder};
use crate::{orderbook::Orderbook, types::{CreateOrder, DeleteOrder}};
#[get("/depth")]
pub async fn get_depth(orderbook: Data<Arc<Mutex<Orderbook>>>) -> impl Responder {
let orderbook = orderbook.lock().unwrap();
let depth = orderbook.get_depth();
HttpResponse::Ok().json(depth)
}
#[post("/order")]
pub async fn create_order(orderbook: Data<Arc<Mutex<Orderbook>>>, order: Json<CreateOrder>) -> impl Responder {
let mut orderbook = orderbook.lock().unwrap();
let orderbook = orderbook.create_order(order.0);
HttpResponse::Ok().json(orderbook)
}
#[delete("/order")]
pub async fn delete_order(orderbook: Data<Arc<Mutex<Orderbook>>>, order: Json<DeleteOrder>) -> impl Responder {
let mut orderbook = orderbook.lock().unwrap();
let orderbook = orderbook.delete_order(order.0);
HttpResponse::Ok().json(orderbook)
}