Rust 有 enum,正如您所期望的那样,但功能更强大,对于类,您可以为其创建 struct 和 impl 函数。还有Traits,就像结构可以坚持的接口。
所以你的 Order 结构可以实现 new(典型的非平凡结构),它返回一个新的 Order,并有一个名为 fill 的 fn,例如,它可以在任何订单上调用。因此,您可以为每个工具拥有一个 Market/Engine 结构,该结构具有 2 个 Orderbook 结构(bid + ask),它们有许多 Level 结构,每个 Level 都有许多 Order 结构。
例如,Order 结构可能看起来像:
use super::{OrderSide, OrderStatus};
use std::time::{SystemTime, UNIX_EPOCH};
#[derive(Debug)]
pub struct Order {
pub order_id: u64,
pub side: OrderSide,
pub price: u64,
pub quantity: u64,
pub quantity_left: u64,
pub quantity_filled: u64,
pub status: OrderStatus,
pub acum_amount: u64,
pub avg_fill_price: f64,
pub timestamp: u128,
}
impl Order {
pub fn new(order_id: u64, side: OrderSide, price: u64, quantity: u64) -> Self {
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("SystemTime before UNIX EPOCH!")
.as_millis();
Self {
order_id,
side,
price,
quantity,
quantity_left: quantity,
quantity_filled: 0,
status: OrderStatus::Open,
acum_amount: 0,
avg_fill_price: 0.0,
timestamp,
}
}
// Note fill should never be called with a quantity
// > quantity_left. We omit it here as an order is
// only intended for a level which does the check
// already.
pub fn fill(&mut self, quantity: u64, price: u64) -> &OrderStatus {
self.acum_amount += quantity * price;
self.quantity_left -= quantity;
self.quantity_filled += quantity;
self.avg_fill_price = (self.acum_amount / self.quantity_filled) as f64;
if self.quantity_left == 0 {
self.status = OrderStatus::Filled;
} else {
self.status = OrderStatus::PartialFill;
}
&self.status
}
pub fn finished(&self) -> bool {
self.status == OrderStatus::Cancelled || self.status == OrderStatus::Filled
}
}
#[cfg(test)]
mod order_test {
use super::{Order, OrderSide, OrderStatus};
const FAKE_ID: u64 = 553311;
#[test]
fn test_order() {
// Order 1000 lots for $2 per unit.
let mut order = Order::new(FAKE_ID, OrderSide::Bid, 200_000_000, 1000);
// Side should be bid with an open status.
assert_eq!(order.side, OrderSide::Bid);
assert_eq!(order.status, OrderStatus::Open);
// Fill 500 lots at $1.9 per unit.
order.fill(500, 190_000_000);
assert_eq!(order.quantity_left, 500, "Should have 500 units left.");
assert_eq!(
order.avg_fill_price, 190_000_000.0,
"Average fill price should be $1.9"
);
assert_eq!(
order.status,
OrderStatus::PartialFill,
"Status should be PartialFill."
);
// Fill up the order. NOTE that we cannot overfill
// the order otherwise it will break the u64 type
// on quantity_left. Since Level ensures we don't
// overfill this is OK and more efficient.
order.fill(500, 200_000_000);
assert_eq!(order.quantity_left, 0, "quantity_left should be 0");
assert_eq!(order.status, OrderStatus::Filled, "Status should be Filled")
}
}
P.S 在 Rust 中设计匹配引擎时会遇到一些有趣的问题,因为为了最有效地进行添加/更新/取消/修改,许多算法使用两个指向相同订单的数据结构,即使匹配引擎几乎总是单线程 Rust 将迫使您编写线程安全代码,通过胖/智能指针访问底层订单(不适合高频交易,但像 Rc 这样的简单代码应该可以)