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}