【问题标题】:How would you do this in Rust? [closed]你会如何在 Rust 中做到这一点? [关闭]
【发布时间】:2021-08-03 21:13:43
【问题描述】:

我对 Rust 非常非常陌生,我正在努力理解它。对我来说,最好有人通过示例向我展示如何做这样的事情:

让我们有 OrderBook 以及 AsksBids。 让我们的 Market 拥有它的 OrderBook。 让我们的 Order 可以是以下四种类型之一:BuyForAmountBuyForTotalSellForAmountSellForTotal 。 让 OrderMarket 将被放置。 让 Order 具有计算方法,该方法从 Market 获取 OrderBook 进行计算(假设 Order 可以重新计算自身使用来自 Market 的新 OrderBook 数据)。

很简单,不是吗? ;-)

在某种伪代码中,我可能会这样写:

class OrderBook {}

class Market { order_book: OrderBook }

class Order {
  virtual calculate()

  calc_from_amount(ref order_book)
  calc_from_total(ref order_book)
}

class BuyForAmount : Order {
  calculate() { 
    base.calc_from_amount(market.order_book.asks)
  }
}

class SellForAmount : Order {
  calculate() { 
    base.calc_from_amount(market.order_book.bids)
  }
}

class BuyForTotal : Order {
  calculate() { 
    base.calc_from_total(market.order_book.asks)
  }
}

class SellForTotal : Order {
  calculate() { 
    base.calc_from_total(market.order_book.bids)
  }
}

你会如何在 Rust 中做到这一点? (我想避免使用枚举和if/else。)

【问题讨论】:

标签: rust


【解决方案1】:

Rust 有 enum,正如您所期望的那样,但功能更强大,对于类,您可以为其创建 structimpl 函数。还有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 这样的简单代码应该可以)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-11
    • 2017-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多