vertex_sdk/vertex_utils/
subaccount_info.rs

1use std::collections::HashMap;
2
3use eyre::{eyre, Result};
4
5use crate::bindings::querier::{HealthInfo, PerpBalance, PerpProduct, SpotBalance};
6use crate::engine::{AllProductsResponse, SpotProduct, SubaccountInfoResponse};
7
8impl SubaccountInfoResponse {
9    pub fn get_spot_balance(&self, product_id: u32) -> Result<&SpotBalance> {
10        let balance = self
11            .spot_balances
12            .iter()
13            .find(|&balance| balance.product_id == product_id)
14            .ok_or(eyre!(
15                "spot balance not found for product_id: {}",
16                product_id
17            ))?;
18        Ok(balance)
19    }
20
21    pub fn get_perp_balance(&self, product_id: u32) -> Result<&PerpBalance> {
22        let balance = self
23            .perp_balances
24            .iter()
25            .find(|&balance| balance.product_id == product_id)
26            .ok_or(eyre!(
27                "spot balance not found for product_id: {}",
28                product_id
29            ))?;
30        Ok(balance)
31    }
32
33    pub fn get_spot_product(&self, product_id: u32) -> Result<&SpotProduct> {
34        let product = self
35            .spot_products
36            .iter()
37            .find(|&product| product.product_id == product_id)
38            .ok_or(eyre!(
39                "spot product not found for product_id: {}",
40                product_id
41            ))?;
42        Ok(product)
43    }
44
45    pub fn get_perp_product(&self, product_id: u32) -> Result<&PerpProduct> {
46        let product = self
47            .perp_products
48            .iter()
49            .find(|&product| product.product_id == product_id)
50            .ok_or(eyre!(
51                "perp product not found for product_id: {}",
52                product_id
53            ))?;
54        Ok(product)
55    }
56
57    pub fn get_product_balances(&self, product_ids: Vec<u32>) -> Result<Vec<i128>> {
58        let mut ret = vec![];
59        for product_id in &product_ids {
60            if *product_id != 0 && *product_id % 2 == 0 {
61                let balance = self.get_perp_balance(*product_id)?;
62                ret.push(balance.balance.amount);
63            } else {
64                let balance = self.get_spot_balance(*product_id)?;
65                ret.push(balance.balance.amount);
66            }
67        }
68        let mut quote_bias = 0;
69        for product_id in [2, 4, 6] {
70            let balance = self.get_perp_balance(product_id)?;
71            quote_bias += balance.balance.v_quote_balance;
72        }
73        for index in 0..product_ids.len() {
74            if product_ids[index] == 0 {
75                ret[index] += quote_bias;
76            }
77        }
78        Ok(ret)
79    }
80
81    pub fn get_lp_balances(&self, product_ids: Vec<u32>) -> Result<Vec<i128>> {
82        let mut ret = vec![];
83        for product_id in &product_ids {
84            if *product_id != 0 && *product_id % 2 == 0 {
85                let balance = self.get_perp_balance(*product_id)?;
86                ret.push(balance.lp_balance.amount);
87            } else {
88                let balance = self.get_spot_balance(*product_id)?;
89                ret.push(balance.lp_balance.amount);
90            }
91        }
92        Ok(ret)
93    }
94
95    pub fn with_corrected_fees(&mut self, all_products: AllProductsResponse) -> Self {
96        let mut subaccount_info = self.clone();
97
98        let mut spot_products_without_collected_fees = all_products.spot_products.clone();
99        let mut perp_products_without_collected_fees = all_products.perp_products.clone();
100        for spot_product in &mut spot_products_without_collected_fees {
101            spot_product.book_info.collected_fees = 0;
102        }
103        for perp_product in &mut perp_products_without_collected_fees {
104            perp_product.book_info.collected_fees = 0;
105        }
106
107        let mut subaccount_info_clone = subaccount_info.clone();
108
109        for spot_product in subaccount_info_clone.spot_products.iter_mut() {
110            spot_product.book_info.price_increment_x18 = 0;
111        }
112        for perp_product in subaccount_info_clone.perp_products.iter_mut() {
113            perp_product.book_info.price_increment_x18 = 0;
114        }
115
116        assert_eq!(
117            subaccount_info_clone.spot_products,
118            spot_products_without_collected_fees
119        );
120        assert_eq!(
121            subaccount_info_clone.perp_products,
122            perp_products_without_collected_fees
123        );
124        subaccount_info.spot_products = all_products.spot_products;
125        subaccount_info.perp_products = all_products.perp_products;
126        subaccount_info
127    }
128
129    pub fn validate_size_increments(&self) {
130        let mut size_increments: HashMap<u32, i128> = HashMap::new();
131        for perp_product in self.perp_products.iter() {
132            if perp_product.book_info.size_increment == 0
133                && perp_product.risk.long_weight_initial_x18 == 0
134                && perp_product.oracle_price_x18 == 0
135            {
136                // placeholder product
137                continue;
138            }
139            let open_interest = perp_product.state.open_interest;
140            let size_increment = perp_product.book_info.size_increment;
141            size_increments.insert(perp_product.product_id, size_increment);
142            assert_eq!(open_interest % size_increment, 0);
143        }
144        for perp_balance in self.perp_balances.iter() {
145            if !size_increments.contains_key(&perp_balance.product_id) {
146                continue;
147            }
148            let balance = perp_balance.balance.amount;
149            let size_increment = size_increments.get(&perp_balance.product_id).unwrap();
150            assert_eq!(balance % size_increment, 0);
151        }
152    }
153
154    pub fn get_states(&self, product_ids: Vec<u32>) -> Result<Vec<Vec<i128>>> {
155        let mut ret = vec![];
156        for product_id in product_ids {
157            let mut state = vec![];
158            if product_id != 0 && product_id % 2 == 0 {
159                let perp = self.get_perp_product(product_id)?;
160                state.push(perp.state.cumulative_funding_long_x18);
161                state.push(perp.state.cumulative_funding_short_x18);
162                state.push(perp.state.open_interest);
163            } else {
164                let spot = self.get_spot_product(product_id)?;
165                state.push(spot.state.cumulative_deposits_multiplier_x18);
166                state.push(spot.state.cumulative_borrows_multiplier_x18);
167                state.push(spot.state.total_deposits_normalized);
168                state.push(spot.state.total_borrows_normalized);
169            }
170            ret.push(state);
171        }
172        Ok(ret)
173    }
174
175    pub fn get_lp_states(&self, product_ids: Vec<u32>) -> Result<Vec<Vec<i128>>> {
176        let mut ret = vec![];
177        for product_id in product_ids {
178            let mut lp_state = vec![];
179            if product_id != 0 && product_id % 2 == 0 {
180                let perp = self.get_perp_product(product_id)?;
181                lp_state.push(perp.lp_state.supply);
182                lp_state.push(perp.lp_state.base);
183                lp_state.push(perp.lp_state.quote);
184            } else {
185                let spot = self.get_spot_product(product_id)?;
186                lp_state.push(spot.lp_state.supply);
187                lp_state.push(spot.lp_state.base.amount);
188                lp_state.push(spot.lp_state.quote.amount);
189            }
190            ret.push(lp_state)
191        }
192        Ok(ret)
193    }
194
195    pub fn get_health_info(&self) -> (HealthInfo, HealthInfo, HealthInfo) {
196        (
197            self.healths[0].clone(),
198            self.healths[1].clone(),
199            self.healths[2].clone(),
200        )
201    }
202}