Skip to main content

rustrade_execution/
borrow_capacity.rs

1//! How much more this account can borrow now: what a venue says about the account's own capacity
2//! to borrow, as opposed to [`Shortability`](crate::shortability::Shortability), which says what
3//! the venue has to lend.
4//!
5//! The two are kept apart because they are different kinds of fact. A [`Shortability`] describes
6//! the instrument and can be recorded and replayed into a simulated venue, which subtracts the
7//! account's own short from it. A [`BorrowCapacity`] is already net of this account's collateral and
8//! existing borrows, so it changes with every fill, price move and borrow, and replaying it as the
9//! lender's total would count the account's own short twice.
10//!
11//! [`Shortability`]: crate::shortability::Shortability
12
13use rust_decimal::Decimal;
14use rustrade_instrument::asset::name::AssetNameExchange;
15use serde::{Deserialize, Serialize};
16
17/// How much of an asset this account can borrow now, as the venue reported it.
18///
19/// Returned by [`BorrowCapacityClient::fetch_borrow_capacity`]. Both amounts are in units of
20/// [`asset`](Self::asset), and `None` means the venue did not say, never zero.
21///
22/// # Advisory
23/// The values were true when the venue answered and are not reserved: a fill, a price move or
24/// another borrow can change them before an order arrives, so an order sized to them must still
25/// handle [`ApiError::BorrowRejected`](crate::error::ApiError::BorrowRejected).
26///
27/// [`BorrowCapacityClient::fetch_borrow_capacity`]: crate::client::BorrowCapacityClient::fetch_borrow_capacity
28#[non_exhaustive]
29#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
30pub struct BorrowCapacity {
31    /// The asset that would be borrowed: an instrument's base asset for a sell, its quote asset for
32    /// a buy.
33    pub asset: AssetNameExchange,
34    /// How much more of [`asset`](Self::asset) this account can borrow now: what its collateral
35    /// and its limit allow, less what it has already borrowed, and no more than the venue has to
36    /// lend.
37    pub borrowable_now: Option<Decimal>,
38    /// The most of [`asset`](Self::asset) the venue lets this account borrow in total, whatever its
39    /// collateral. When [`borrowable_now`](Self::borrowable_now) is small, this says whether the
40    /// account's limit is why.
41    pub account_limit: Option<Decimal>,
42}
43
44impl BorrowCapacity {
45    /// The capacity to borrow `asset`, with nothing known.
46    pub fn new(asset: AssetNameExchange) -> Self {
47        Self {
48            asset,
49            borrowable_now: None,
50            account_limit: None,
51        }
52    }
53
54    /// With how much more of the asset this account can borrow now.
55    #[must_use]
56    pub fn with_borrowable_now(mut self, borrowable_now: Decimal) -> Self {
57        self.borrowable_now = Some(borrowable_now);
58        self
59    }
60
61    /// With the most of the asset the venue lets this account borrow in total.
62    #[must_use]
63    pub fn with_account_limit(mut self, account_limit: Decimal) -> Self {
64        self.account_limit = Some(account_limit);
65        self
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72    use rust_decimal_macros::dec;
73
74    #[test]
75    fn a_new_capacity_knows_only_its_asset() {
76        let capacity = BorrowCapacity::new(AssetNameExchange::new("BTC"));
77
78        assert_eq!(capacity.asset, AssetNameExchange::new("BTC"));
79        assert_eq!(capacity.borrowable_now, None);
80        assert_eq!(capacity.account_limit, None);
81    }
82
83    #[test]
84    fn the_setters_fill_their_own_field() {
85        let capacity = BorrowCapacity::new(AssetNameExchange::new("BTC"))
86            .with_borrowable_now(dec!(1.5))
87            .with_account_limit(dec!(10));
88
89        assert_eq!(capacity.borrowable_now, Some(dec!(1.5)));
90        assert_eq!(capacity.account_limit, Some(dec!(10)));
91    }
92}