From 8c85d4421dfa80f06404abbaaf2032eda1807bde Mon Sep 17 00:00:00 2001 From: MathisWellmann Date: Tue, 21 Jul 2026 17:39:29 +0100 Subject: [PATCH] 0.138.1: api: add conventional account and trade accessors --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/account/account_impl.rs | 8 +++-- src/account/active_limit_orders.rs | 50 ++++++++++++++++++++++++++++-- src/market_update/trade_update.rs | 11 +++++-- 5 files changed, 64 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5a5f57c..a52b5bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1136,7 +1136,7 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lfest" -version = "0.138.1" +version = "0.138.2" dependencies = [ "assert2", "const-decimal", diff --git a/Cargo.toml b/Cargo.toml index 7e183bf..6a5bf20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lfest" -version = "0.138.1" +version = "0.138.2" authors = ["MathisWellmann "] edition = "2024" license-file = "LICENSE" diff --git a/src/account/account_impl.rs b/src/account/account_impl.rs index b9f0110..3c6ceae 100644 --- a/src/account/account_impl.rs +++ b/src/account/account_impl.rs @@ -1,7 +1,10 @@ use std::num::NonZeroU16; use const_decimal::Decimal; -use getset::Getters; +use getset::{ + CopyGetters, + Getters, +}; use num::Zero; use super::Balances; @@ -35,7 +38,7 @@ use crate::{ /// - `D`: The constant decimal precision of the currencies. /// - `BaseOrQuote`: Either `BaseCurrency` or `QuoteCurrency` depending on the futures type. /// - `UserOrderIdT`: The type of user order id to use. Set to `()` if you don't need one. -#[derive(Debug, Clone, Getters)] +#[derive(Debug, Clone, CopyGetters, Getters)] pub struct Account where I: Mon, @@ -52,6 +55,7 @@ where balances: Balances, /// The initial margin requirement is set based on the selected leverage of the account. + #[getset(get_copy = "pub")] init_margin_req: Decimal, /// The maker fee rate of the venue, used to reserve fees for resting limit orders. diff --git a/src/account/active_limit_orders.rs b/src/account/active_limit_orders.rs index 85d7f78..1c836ef 100644 --- a/src/account/active_limit_orders.rs +++ b/src/account/active_limit_orders.rs @@ -79,11 +79,17 @@ where /// Get the number of active limit orders. #[inline(always)] - pub fn num_active(&self) -> usize { + pub fn len(&self) -> usize { self.bids.len() + self.asks.len() } - /// `true` is there are no active orders. + /// Get the number of active limit orders. + #[inline(always)] + pub fn num_active(&self) -> usize { + self.len() + } + + /// `true` if there are no active orders. #[inline(always)] pub fn is_empty(&self) -> bool { self.bids.is_empty() && self.asks.is_empty() @@ -116,6 +122,14 @@ where self.bids.orders().iter().chain(self.asks.orders().iter()) } + /// Alias for [`Self::iter`] for callers treating active orders as values. + pub fn values( + &self, + ) -> impl Iterator>> + { + self.iter() + } + /// Iterate over the user order ids of all active limit orders; bids first, then asks. pub fn user_order_ids(&self) -> impl Iterator + '_ { self.iter().map(|order| order.user_order_id()) @@ -287,6 +301,31 @@ where } } +impl<'a, I, const D: u8, BaseOrQuote, UserOrderIdT> IntoIterator + for &'a ActiveLimitOrders +where + I: Mon, + BaseOrQuote: Currency, + BaseOrQuote::PairedCurrency: MarginCurrency, + UserOrderIdT: UserOrderId, +{ + type Item = &'a LimitOrder>; + type IntoIter = std::iter::Chain< + std::slice::Iter< + 'a, + LimitOrder>, + >, + std::slice::Iter< + 'a, + LimitOrder>, + >, + >; + + fn into_iter(self) -> Self::IntoIter { + self.bids.orders().iter().chain(self.asks.orders().iter()) + } +} + #[cfg(test)] mod tests { use std::num::NonZeroU16; @@ -393,7 +432,9 @@ mod tests { let mut book = ActiveLimitOrders::, i32>::with_capacity( NonZeroU16::new(10).unwrap(), ); - assert_eq!(book.iter().count(), 0); + assert_eq!(book.len(), 0); + assert_eq!(book.values().count(), 0); + assert_eq!((&book).into_iter().count(), 0); assert_eq!(book.user_order_ids().count(), 0); let bid = LimitOrder::new_with_user_order_id( @@ -416,7 +457,10 @@ mod tests { .into_pending(ExchangeOrderMeta::new(1.into(), 0.into())); book.try_insert(ask.clone()).unwrap(); + assert_eq!(book.len(), 2); assert_eq!(Vec::from_iter(book.iter()), vec![&bid, &ask]); + assert_eq!(Vec::from_iter(book.values()), vec![&bid, &ask]); + assert_eq!(Vec::from_iter(&book), vec![&bid, &ask]); assert_eq!(Vec::from_iter(book.user_order_ids()), vec![100, 200]); } diff --git a/src/market_update/trade_update.rs b/src/market_update/trade_update.rs index 93cb6ff..1974d5a 100644 --- a/src/market_update/trade_update.rs +++ b/src/market_update/trade_update.rs @@ -1,3 +1,4 @@ +use getset::CopyGetters; use num::Zero; use super::MarketUpdate; @@ -27,23 +28,26 @@ use crate::{ utils::min, }; -// TODO: use `Getters` /// A taker trade that consumes liquidity in the book. -#[derive(Debug, Clone, Copy, Eq, PartialEq)] +#[derive(Debug, Clone, Copy, Eq, PartialEq, CopyGetters)] pub struct Trade where I: Mon, BaseOrQuote: Currency, { /// The nanosecond timestamp at which this trade occurred at the exchange. + #[getset(get_copy = "pub")] pub timestamp_exchange_ns: TimestampNs, /// The price at which the trade executed at. + #[getset(get_copy = "pub")] pub price: QuoteCurrency, /// The executed quantity. /// Generic denotation, e.g either Quote or Base currency denoted. + #[getset(get_copy = "pub")] pub quantity: BaseOrQuote, /// Either a buy or sell order. // TODO: remove field and derive from sign of `quantity` to save size of struct. + #[getset(get_copy = "pub")] pub side: Side, } @@ -161,6 +165,9 @@ mod tests { side, timestamp_exchange_ns: 0.into(), }; + assert_eq!(trade.side(), side); + assert_eq!(trade.price(), trade.price); + assert_eq!(trade.quantity(), trade.quantity); assert_eq!(trade.can_fill_bids(), can_fill_bid); assert_eq!(trade.can_fill_asks(), can_fill_ask); }