Skip to main content

off_grid_primitives/spot/
market.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
4pub struct L1 {
5    /// Last match price
6    pub lmp: Option<u64>,
7    /// Head of the bid list
8    pub bid_head: Option<u64>,
9    /// Head of the ask list
10    pub ask_head: Option<u64>,
11    /// Slippage limit for limit buy orders in 8 decimals
12    pub limit_buy_slippage_limit: Option<u64>,
13    /// Slippage limit for limit sell orders in 8 decimals
14    pub limit_sell_slippage_limit: Option<u64>,
15    /// Slippage limit for market buy orders in 8 decimals
16    pub market_buy_slippage_limit: Option<u64>,
17    /// Slippage limit for market sell orders in 8 decimals
18    pub market_sell_slippage_limit: Option<u64>,
19}
20
21#[derive(Debug, thiserror::Error, PartialEq, Eq)]
22pub enum L1Error {
23    #[error("price is zero")]
24    PriceIsZero,
25}
26
27impl Default for L1 {
28    fn default() -> Self {
29        Self {
30            lmp: None,
31            bid_head: None,
32            ask_head: None,
33            limit_buy_slippage_limit: Some(10000u64),
34            limit_sell_slippage_limit: Some(10000u64),
35            market_buy_slippage_limit: Some(10000u64),
36            market_sell_slippage_limit: Some(10000u64),
37        }
38    }
39}
40
41impl L1 {
42    pub fn new() -> Self {
43        Self::default()
44    }
45
46    pub fn new_with_slippage(
47        lmp: Option<u64>,
48        bid_head: Option<u64>,
49        ask_head: Option<u64>,
50        limit_buy_slippage_limit: Option<u64>,
51        limit_sell_slippage_limit: Option<u64>,
52        market_buy_slippage_limit: Option<u64>,
53        market_sell_slippage_limit: Option<u64>,
54    ) -> Self {
55        Self {
56            lmp,
57            bid_head,
58            ask_head,
59            limit_buy_slippage_limit,
60            limit_sell_slippage_limit,
61            market_buy_slippage_limit,
62            market_sell_slippage_limit,
63        }
64    }
65
66    // ask head price
67    pub fn ask_head(&self) -> Option<u64> {
68        self.ask_head
69    }
70
71    // bid head price
72    pub fn bid_head(&self) -> Option<u64> {
73        self.bid_head
74    }
75
76    pub fn lmp(&self) -> Option<u64> {
77        self.lmp
78    }
79
80    pub fn set_lmp(&mut self, price: u64) {
81        self.lmp = Some(price);
82    }
83
84    pub fn set_ask_head(&mut self, price: u64) {
85        self.ask_head = Some(price);
86    }
87
88    pub fn set_bid_head(&mut self, price: u64) {
89        self.bid_head = Some(price);
90    }
91
92    /// Set the slippage limit for limit buy orders
93    pub fn set_limit_buy_slippage_limit(&mut self, slippage_limit: Option<u64>) {
94        self.limit_buy_slippage_limit = slippage_limit;
95    }
96
97    /// Set the slippage limit for limit sell orders
98    pub fn set_limit_sell_slippage_limit(&mut self, slippage_limit: Option<u64>) {
99        self.limit_sell_slippage_limit = slippage_limit;
100    }
101
102    /// Set the slippage limit for market buy orders
103    pub fn set_market_buy_slippage_limit(&mut self, slippage_limit: Option<u64>) {
104        self.market_buy_slippage_limit = slippage_limit;
105    }
106
107    /// Set the slippage limit for market sell orders
108    pub fn set_market_sell_slippage_limit(&mut self, slippage_limit: Option<u64>) {
109        self.market_sell_slippage_limit = slippage_limit;
110    }
111
112    /// Determine the maker price for a limit sell order
113    /// This function calculates the price at which a limit sell order should be placed
114    /// as a maker order based on current market conditions, limit price, and spread.
115    /// 
116    /// - `lp`: Limit price specified by the user
117    /// - `bid_head`: Current best bid price (0 if no bids)
118    /// - `ask_head`: Current best ask price (0 if no asks)
119    /// - `spread`: Spread in basis points (10000 = 100%)
120    /// - Returns the calculated maker price and lmp tuple
121    #[cfg_attr(test, allow(dead_code))]
122    pub fn det_limit_sell_make_price(
123        &self,
124        lp: u64,
125        bid_head: u64,
126        ask_head: u64,
127        spread: u32,
128    ) -> u64 {
129        const DENOM: u64 = 10000; // Basis points denominator (10000 = 100%)
130        let lmp = self.lmp().unwrap_or(0);
131
132        if ask_head == 0 && bid_head == 0 {
133            // No orders in orderbook
134            if lmp != 0 {
135                // Use lmp with spread (subtract spread for sell)
136                let down = ((lmp as u128 * (DENOM - spread as u64) as u128) / DENOM as u128) as u64;
137                let price = if lp <= down { down } else { lp };
138                return price;
139            }
140            // No lmp, return limit price as-is
141            return lp;
142        } else if ask_head == 0 && bid_head != 0 {
143            // Only bids exist
144            if lmp != 0 {
145                let mut down = ((lmp as u128 * (DENOM - spread as u64) as u128) / DENOM as u128) as u64;
146                down = if lp <= down { down } else { lp };
147                let price = if down <= bid_head { bid_head } else { down };
148                return price;
149            }
150            // No lmp, use bidHead with spread
151            let mut down = ((bid_head as u128 * (DENOM - spread as u64) as u128) / DENOM as u128) as u64;
152            down = if lp <= down { down } else { lp };
153            let price = if down <= bid_head { bid_head } else { down };
154            return price;
155        } else if ask_head != 0 && bid_head == 0 {
156            // Only asks exist
157            if lmp != 0 {
158                let down = ((lmp as u128 * (DENOM - spread as u64) as u128) / DENOM as u128) as u64;
159                let price = if lp <= down { down } else { lp };
160                return price;
161            }
162            // No lmp, use askHead with spread
163            let down = ((ask_head as u128 * (DENOM - spread as u64) as u128) / DENOM as u128) as u64;
164            let price = if lp <= down { down } else { lp };
165            return price;
166        } else {
167            // Both bids and asks exist
168            if lmp != 0 {
169                let down = ((lmp as u128 * (DENOM - spread as u64) as u128) / DENOM as u128) as u64;
170                let price = if lp <= down { down } else { lp };
171                return price;
172            }
173            // No lmp, lower limit price on sell cannot be lower than bid head price
174            // Note: Following Solidity code pattern, using lp directly when no lmp
175            let price = if lp <= bid_head { bid_head } else { lp };
176            return price;
177        }
178    }
179
180    /// Determine the maker price for a limit buy order
181    /// This function calculates the price at which a limit buy order should be placed
182    /// as a maker order based on current market conditions, limit price, and spread.
183    /// 
184    /// - `lp`: Limit price specified by the user
185    /// - `bid_head`: Current best bid price (0 if no bids)
186    /// - `ask_head`: Current best ask price (0 if no asks)
187    /// - `spread`: Spread in basis points (10000 = 100%)
188    /// - Returns the calculated maker price and lmp tuple
189    #[cfg_attr(test, allow(dead_code))]
190    pub fn det_limit_buy_make_price(
191        &self,
192        lp: u64,
193        bid_head: u64,
194        ask_head: u64,
195        spread: u32,
196    ) -> u64 {
197        const DENOM: u64 = 10000; // Basis points denominator (10000 = 100%)
198        let lmp = self.lmp().unwrap_or(0);
199
200        if ask_head == 0 && bid_head == 0 {
201            // No orders in orderbook
202            if lmp != 0 {
203                // Use lmp with spread (add spread for buy)
204                let up = ((lmp as u128 * (DENOM + spread as u64) as u128) / DENOM as u128) as u64;
205                let price = if lp >= up { up } else { lp };
206                return price;
207            }
208            // No lmp, return limit price as-is
209            return lp;
210        } else if ask_head == 0 && bid_head != 0 {
211            // Only bids exist
212            if lmp != 0 {
213                // Use lmp with spread (add spread for buy)
214                let up = ((lmp as u128 * (DENOM + spread as u64) as u128) / DENOM as u128) as u64;
215                let price = if lp >= up { up } else { lp };
216                return price;
217            }
218            // No lmp, use bidHead with spread
219            let up = ((bid_head as u128 * (DENOM + spread as u64) as u128) / DENOM as u128) as u64;
220            let price = if lp >= up { up } else { lp };
221            return price;
222        } else if ask_head != 0 && bid_head == 0 {
223            // Only asks exist
224            if lmp != 0 {
225                let mut up = ((lmp as u128 * (DENOM + spread as u64) as u128) / DENOM as u128) as u64;
226                up = if lp >= up { up } else { lp };
227                let price = if up >= ask_head { ask_head } else { up };
228                return price;
229            }
230            // No lmp, use askHead with spread
231            let mut up = ((ask_head as u128 * (DENOM + spread as u64) as u128) / DENOM as u128) as u64;
232            up = if lp >= up { up } else { lp };
233            let price = if up >= ask_head { ask_head } else { up };
234            return price;
235        } else {
236            // Both bids and asks exist
237            if lmp != 0 {
238                let mut up = ((lmp as u128 * (DENOM + spread as u64) as u128) / DENOM as u128) as u64;
239                up = if lp >= up { up } else { lp };
240                let price = if up >= ask_head { ask_head } else { up };
241                return price;
242            }
243            // No lmp, upper limit on make price must not go above ask price
244            let price = if lp >= ask_head { ask_head } else { lp };
245            return price;
246        }
247    }
248
249    /// Determine the maker price for a market buy order
250    /// This function calculates the price at which a market buy order should be placed
251    /// as a maker order based on current market conditions (bid_head, ask_head, lmp) and spread.
252    /// 
253    /// - `bid_head`: Current best bid price (0 if no bids)
254    /// - `ask_head`: Current best ask price (0 if no asks)
255    /// - `spread`: Spread in basis points (10000 = 100%)
256    /// - Returns the calculated maker price
257    #[cfg_attr(test, allow(dead_code))]
258    pub fn det_market_buy_make_price(
259        &self,
260        bid_head: u64,
261        ask_head: u64,
262        spread: u32,
263    ) -> u64 {
264        const DENOM: u64 = 10000; // Basis points denominator (10000 = 100%)
265        let lmp = self.lmp().unwrap_or(0);
266
267        if ask_head == 0 && bid_head == 0 {
268            // No orders in orderbook
269            // lmp must exist unless there has been no order in orderbook
270            if lmp != 0 {
271                // Use lmp with spread (add spread for buy)
272                let up = ((lmp as u128 * (DENOM + spread as u64) as u128) / DENOM as u128) as u64;
273                return up;
274            }
275            // No lmp, return 0 (should not happen in practice)
276            return 0;
277        } else if ask_head == 0 && bid_head != 0 {
278            // Only bids exist
279            if lmp != 0 {
280                let temp = if bid_head >= lmp { bid_head } else { lmp };
281                let up = ((temp as u128 * (DENOM + spread as u64) as u128) / DENOM as u128) as u64;
282                return up;
283            }
284            // No lmp, use bid_head with spread
285            let up = ((bid_head as u128 * (DENOM + spread as u64) as u128) / DENOM as u128) as u64;
286            return up;
287        } else if ask_head != 0 && bid_head == 0 {
288            // Only asks exist
289            if lmp != 0 {
290                let up = ((lmp as u128 * (DENOM + spread as u64) as u128) / DENOM as u128) as u64;
291                let price = if ask_head >= up { up } else { ask_head };
292                return price;
293            }
294            // No lmp, return ask_head
295            return ask_head;
296        } else {
297            // Both bids and asks exist
298            if lmp != 0 {
299                let temp = if bid_head >= lmp { bid_head } else { lmp };
300                let up = ((temp as u128 * (DENOM + spread as u64) as u128) / DENOM as u128) as u64;
301                let price = if ask_head >= up { up } else { ask_head };
302                return price;
303            }
304            // No lmp, return ask_head
305            return ask_head;
306        }
307    }
308
309    /// Determine the maker price for a market sell order
310    /// This function calculates the price at which a market sell order should be placed
311    /// as a maker order based on current market conditions (bid_head, ask_head, lmp) and spread.
312    /// 
313    /// - `bid_head`: Current best bid price (0 if no bids)
314    /// - `ask_head`: Current best ask price (0 if no asks)
315    /// - `spread`: Spread in basis points (10000 = 100%)
316    /// - Returns the calculated maker price (minimum 1 to prevent zero price)
317    #[cfg_attr(test, allow(dead_code))]
318    pub fn det_market_sell_make_price(
319        &self,
320        bid_head: u64,
321        ask_head: u64,
322        spread: u32,
323    ) -> u64 {
324        const DENOM: u64 = 10000; // Basis points denominator (10000 = 100%)
325        let lmp = self.lmp().unwrap_or(0);
326
327        if ask_head == 0 && bid_head == 0 {
328            // No orders in orderbook
329            // lmp must exist unless there has been no order in orderbook
330            if lmp != 0 {
331                // Use lmp with spread (subtract spread for sell)
332                let down = ((lmp as u128 * (DENOM - spread as u64) as u128) / DENOM as u128) as u64;
333                // Ensure price is never 0
334                let price = if down == 0 { 1 } else { down };
335                return price;
336            }
337            // No lmp, return 0 (should not happen in practice)
338            return 0;
339        } else if ask_head == 0 && bid_head != 0 {
340            // Only bids exist
341            if lmp != 0 {
342                let mut down = ((lmp as u128 * (DENOM - spread as u64) as u128) / DENOM as u128) as u64;
343                down = if down <= bid_head { bid_head } else { down };
344                // Ensure price is never 0
345                let price = if down == 0 { 1 } else { down };
346                return price;
347            }
348            // No lmp, return bid_head
349            return bid_head;
350        } else if ask_head != 0 && bid_head == 0 {
351            // Only asks exist
352            if lmp != 0 {
353                let temp = if lmp <= ask_head { lmp } else { ask_head };
354                let down = ((temp as u128 * (DENOM - spread as u64) as u128) / DENOM as u128) as u64;
355                // Ensure price is never 0
356                let price = if down == 0 { 1 } else { down };
357                return price;
358            }
359            // No lmp, use ask_head with spread
360            let down = ((ask_head as u128 * (DENOM - spread as u64) as u128) / DENOM as u128) as u64;
361            // Ensure price is never 0
362            let price = if down == 0 { 1 } else { down };
363            return price;
364        } else {
365            // Both bids and asks exist
366            if lmp != 0 {
367                let temp = if lmp <= ask_head { lmp } else { ask_head };
368                let mut down = ((temp as u128 * (DENOM - spread as u64) as u128) / DENOM as u128) as u64;
369                down = if down <= bid_head { bid_head } else { down };
370                // Ensure price is never 0
371                let price = if down == 0 { 1 } else { down };
372                return price;
373            }
374            // No lmp, return bid_head
375            return bid_head;
376        }
377    }
378
379    
380}