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