Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "lfest"
version = "0.138.1"
version = "0.138.2"
authors = ["MathisWellmann <wellmannmathis@gmail.com>"]
edition = "2024"
license-file = "LICENSE"
Expand Down
8 changes: 6 additions & 2 deletions src/account/account_impl.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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<I, const D: u8, BaseOrQuote, UserOrderIdT>
where
I: Mon<D>,
Expand All @@ -52,6 +55,7 @@ where
balances: Balances<I, D, BaseOrQuote::PairedCurrency>,

/// The initial margin requirement is set based on the selected leverage of the account.
#[getset(get_copy = "pub")]
init_margin_req: Decimal<I, D>,

/// The maker fee rate of the venue, used to reserve fees for resting limit orders.
Expand Down
50 changes: 47 additions & 3 deletions src/account/active_limit_orders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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<Item = &LimitOrder<I, D, BaseOrQuote, UserOrderIdT, Pending<I, D, BaseOrQuote>>>
{
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<Item = UserOrderIdT> + '_ {
self.iter().map(|order| order.user_order_id())
Expand Down Expand Up @@ -287,6 +301,31 @@ where
}
}

impl<'a, I, const D: u8, BaseOrQuote, UserOrderIdT> IntoIterator
for &'a ActiveLimitOrders<I, D, BaseOrQuote, UserOrderIdT>
where
I: Mon<D>,
BaseOrQuote: Currency<I, D>,
BaseOrQuote::PairedCurrency: MarginCurrency<I, D>,
UserOrderIdT: UserOrderId,
{
type Item = &'a LimitOrder<I, D, BaseOrQuote, UserOrderIdT, Pending<I, D, BaseOrQuote>>;
type IntoIter = std::iter::Chain<
std::slice::Iter<
'a,
LimitOrder<I, D, BaseOrQuote, UserOrderIdT, Pending<I, D, BaseOrQuote>>,
>,
std::slice::Iter<
'a,
LimitOrder<I, D, BaseOrQuote, UserOrderIdT, Pending<I, D, BaseOrQuote>>,
>,
>;

fn into_iter(self) -> Self::IntoIter {
self.bids.orders().iter().chain(self.asks.orders().iter())
}
}

#[cfg(test)]
mod tests {
use std::num::NonZeroU16;
Expand Down Expand Up @@ -393,7 +432,9 @@ mod tests {
let mut book = ActiveLimitOrders::<i64, 5, BaseCurrency<i64, 5>, 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(
Expand All @@ -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]);
}

Expand Down
11 changes: 9 additions & 2 deletions src/market_update/trade_update.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use getset::CopyGetters;
use num::Zero;

use super::MarketUpdate;
Expand Down Expand Up @@ -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<I, const D: u8, BaseOrQuote>
where
I: Mon<D>,
BaseOrQuote: Currency<I, D>,
{
/// 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<I, D>,
/// 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,
}

Expand Down Expand Up @@ -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);
}
Expand Down
Loading