1use std::ops::DerefMut;
2
3use num_traits::{CheckedAdd, Signed};
4
5use crate::{
6 action::update_funding_state::UpdateFundingState,
7 num::{Unsigned, UnsignedAbs},
8 params::{
9 fee::{FundingFeeParams, LiquidationFeeParams},
10 FeeParams, PositionParams,
11 },
12 price::{Price, Prices},
13 Balance, BalanceExt, BorrowingFeeMarket, Pool, PoolExt, PositionImpactMarket,
14 PositionImpactMarketMut, SwapMarket, SwapMarketMut,
15};
16
17use super::BaseMarketExt;
18
19pub trait PerpMarket<const DECIMALS: u8>:
21 SwapMarket<DECIMALS> + PositionImpactMarket<DECIMALS> + BorrowingFeeMarket<DECIMALS>
22{
23 fn funding_factor_per_second(&self) -> &Self::Signed;
25
26 fn funding_amount_per_size_pool(&self, is_long: bool) -> crate::Result<&Self::Pool>;
28
29 fn claimable_funding_amount_per_size_pool(&self, is_long: bool) -> crate::Result<&Self::Pool>;
31
32 fn funding_amount_per_size_adjustment(&self) -> Self::Num;
34
35 fn funding_fee_params(&self) -> crate::Result<FundingFeeParams<Self::Num>>;
37
38 fn position_params(&self) -> crate::Result<PositionParams<Self::Num>>;
40
41 fn order_fee_params(&self) -> crate::Result<FeeParams<Self::Num>>;
43
44 fn min_collateral_factor_for_open_interest_multiplier(
46 &self,
47 is_long: bool,
48 ) -> crate::Result<Self::Num>;
49
50 fn liquidation_fee_params(&self) -> crate::Result<LiquidationFeeParams<Self::Num>>;
52}
53
54pub trait PerpMarketMut<const DECIMALS: u8>:
56 SwapMarketMut<DECIMALS> + PositionImpactMarketMut<DECIMALS> + PerpMarket<DECIMALS>
57{
58 fn just_passed_in_seconds_for_funding(&mut self) -> crate::Result<u64>;
60
61 fn funding_factor_per_second_mut(&mut self) -> &mut Self::Signed;
63
64 fn open_interest_pool_mut(&mut self, is_long: bool) -> crate::Result<&mut Self::Pool>;
72
73 fn open_interest_in_tokens_pool_mut(&mut self, is_long: bool)
78 -> crate::Result<&mut Self::Pool>;
79
80 fn funding_amount_per_size_pool_mut(&mut self, is_long: bool)
84 -> crate::Result<&mut Self::Pool>;
85
86 fn claimable_funding_amount_per_size_pool_mut(
90 &mut self,
91 is_long: bool,
92 ) -> crate::Result<&mut Self::Pool>;
93
94 fn collateral_sum_pool_mut(&mut self, is_long: bool) -> crate::Result<&mut Self::Pool>;
99
100 fn total_borrowing_pool_mut(&mut self) -> crate::Result<&mut Self::Pool>;
104
105 fn virtual_inventory_for_positions_pool_mut(
110 &mut self,
111 ) -> crate::Result<Option<impl DerefMut<Target = Self::Pool>>>;
112
113 fn on_insufficient_funding_fee_payment(
115 &mut self,
116 _cost_amount: &Self::Num,
117 _paid_in_collateral_amount: &Self::Num,
118 _paid_in_secondary_output_amount: &Self::Num,
119 _is_collateral_token_long: bool,
120 ) -> crate::Result<()> {
121 Ok(())
122 }
123}
124
125impl<M: PerpMarket<DECIMALS>, const DECIMALS: u8> PerpMarket<DECIMALS> for &mut M {
126 fn funding_factor_per_second(&self) -> &Self::Signed {
127 (**self).funding_factor_per_second()
128 }
129
130 fn funding_amount_per_size_pool(&self, is_long: bool) -> crate::Result<&Self::Pool> {
131 (**self).funding_amount_per_size_pool(is_long)
132 }
133
134 fn claimable_funding_amount_per_size_pool(&self, is_long: bool) -> crate::Result<&Self::Pool> {
135 (**self).claimable_funding_amount_per_size_pool(is_long)
136 }
137
138 fn funding_amount_per_size_adjustment(&self) -> Self::Num {
139 (**self).funding_amount_per_size_adjustment()
140 }
141
142 fn funding_fee_params(&self) -> crate::Result<FundingFeeParams<Self::Num>> {
143 (**self).funding_fee_params()
144 }
145
146 fn position_params(&self) -> crate::Result<PositionParams<Self::Num>> {
147 (**self).position_params()
148 }
149
150 fn order_fee_params(&self) -> crate::Result<FeeParams<Self::Num>> {
151 (**self).order_fee_params()
152 }
153
154 fn min_collateral_factor_for_open_interest_multiplier(
155 &self,
156 is_long: bool,
157 ) -> crate::Result<Self::Num> {
158 (**self).min_collateral_factor_for_open_interest_multiplier(is_long)
159 }
160
161 fn liquidation_fee_params(&self) -> crate::Result<LiquidationFeeParams<Self::Num>> {
162 (**self).liquidation_fee_params()
163 }
164}
165
166impl<M: PerpMarketMut<DECIMALS>, const DECIMALS: u8> PerpMarketMut<DECIMALS> for &mut M {
167 fn funding_factor_per_second_mut(&mut self) -> &mut Self::Signed {
168 (**self).funding_factor_per_second_mut()
169 }
170
171 fn open_interest_pool_mut(&mut self, is_long: bool) -> crate::Result<&mut Self::Pool> {
172 (**self).open_interest_pool_mut(is_long)
173 }
174
175 fn open_interest_in_tokens_pool_mut(
176 &mut self,
177 is_long: bool,
178 ) -> crate::Result<&mut Self::Pool> {
179 (**self).open_interest_in_tokens_pool_mut(is_long)
180 }
181
182 fn funding_amount_per_size_pool_mut(
183 &mut self,
184 is_long: bool,
185 ) -> crate::Result<&mut Self::Pool> {
186 (**self).funding_amount_per_size_pool_mut(is_long)
187 }
188
189 fn claimable_funding_amount_per_size_pool_mut(
190 &mut self,
191 is_long: bool,
192 ) -> crate::Result<&mut Self::Pool> {
193 (**self).claimable_funding_amount_per_size_pool_mut(is_long)
194 }
195
196 fn collateral_sum_pool_mut(&mut self, is_long: bool) -> crate::Result<&mut Self::Pool> {
197 (**self).collateral_sum_pool_mut(is_long)
198 }
199
200 fn total_borrowing_pool_mut(&mut self) -> crate::Result<&mut Self::Pool> {
201 (**self).total_borrowing_pool_mut()
202 }
203
204 fn virtual_inventory_for_positions_pool_mut(
205 &mut self,
206 ) -> crate::Result<Option<impl DerefMut<Target = Self::Pool>>> {
207 (**self).virtual_inventory_for_positions_pool_mut()
208 }
209
210 fn just_passed_in_seconds_for_funding(&mut self) -> crate::Result<u64> {
211 (**self).just_passed_in_seconds_for_funding()
212 }
213
214 fn on_insufficient_funding_fee_payment(
215 &mut self,
216 cost_amount: &Self::Num,
217 paid_in_collateral_amount: &Self::Num,
218 paid_in_secondary_output_amount: &Self::Num,
219 is_collateral_token_long: bool,
220 ) -> crate::Result<()> {
221 (**self).on_insufficient_funding_fee_payment(
222 cost_amount,
223 paid_in_collateral_amount,
224 paid_in_secondary_output_amount,
225 is_collateral_token_long,
226 )
227 }
228}
229
230pub trait PerpMarketExt<const DECIMALS: u8>: PerpMarket<DECIMALS> {
232 #[inline]
234 fn funding_fee_amount_per_size(
235 &self,
236 is_long: bool,
237 is_long_collateral: bool,
238 ) -> crate::Result<Self::Num> {
239 self.funding_amount_per_size_pool(is_long)?
240 .amount(is_long_collateral)
241 }
242
243 #[inline]
245 fn claimable_funding_fee_amount_per_size(
246 &self,
247 is_long: bool,
248 is_long_collateral: bool,
249 ) -> crate::Result<Self::Num> {
250 self.claimable_funding_amount_per_size_pool(is_long)?
251 .amount(is_long_collateral)
252 }
253
254 fn validate_open_interest_reserve(
256 &self,
257 prices: &Prices<Self::Num>,
258 is_long: bool,
259 ) -> crate::Result<()> {
260 let pool_value = self.pool_value_without_pnl_for_one_side(prices, is_long, false)?;
261
262 let max_reserved_value =
263 crate::utils::apply_factor(&pool_value, &self.open_interest_reserve_factor()?)
264 .ok_or(crate::Error::Computation("calculating max reserved value"))?;
265
266 let reserved_value = self.reserved_value(&prices.index_token_price, is_long)?;
267
268 if reserved_value > max_reserved_value {
269 Err(crate::Error::InsufficientReserveForOpenInterest(
270 reserved_value.to_string(),
271 max_reserved_value.to_string(),
272 ))
273 } else {
274 Ok(())
275 }
276 }
277
278 fn min_collateral_factor_for_open_interest(
280 &self,
281 delta: &Self::Signed,
282 is_long: bool,
283 ) -> crate::Result<Self::Num> {
284 let next_open_interest = self
285 .open_interest()?
286 .amount(is_long)?
287 .checked_add_with_signed(delta)
288 .ok_or(crate::Error::Computation(
289 "calculating next OI for min collateral factor",
290 ))?;
291 let factor = self.min_collateral_factor_for_open_interest_multiplier(is_long)?;
292 crate::utils::apply_factor(&next_open_interest, &factor).ok_or(crate::Error::Computation(
293 "calculating min collateral factor for OI",
294 ))
295 }
296
297 fn cap_positive_position_price_impact(
300 &self,
301 index_token_price: &Price<Self::Num>,
302 size_delta_usd: &Self::Signed,
303 impact: &mut Self::Signed,
304 ) -> crate::Result<()> {
305 use crate::{market::PositionImpactMarketExt, num::UnsignedAbs, utils};
306 use num_traits::{CheckedMul, Signed};
307
308 if !impact.is_negative() {
309 let impact_pool_amount = self.position_impact_pool_amount()?;
310 let max_impact = impact_pool_amount
312 .checked_mul(index_token_price.pick_price(false))
313 .ok_or(crate::Error::Computation(
314 "overflow calculating max positive position impact based on pool amount",
315 ))?
316 .to_signed()?;
317 if *impact > max_impact {
318 *impact = max_impact;
319 }
320
321 let params = self.position_params()?;
323 let max_impact_factor = params.max_positive_position_impact_factor();
324 let max_impact = utils::apply_factor(&size_delta_usd.unsigned_abs(), max_impact_factor)
325 .ok_or(crate::Error::Computation(
326 "calculating max positive position impact based on max factor",
327 ))?
328 .to_signed()?;
329 if *impact > max_impact {
330 *impact = max_impact;
331 }
332 }
333 Ok(())
334 }
335
336 fn cap_negative_position_price_impact(
343 &self,
344 size_delta_usd: &Self::Signed,
345 for_liquidations: bool,
346 impact: &mut Self::Signed,
347 ) -> crate::Result<Self::Num> {
348 use crate::{num::UnsignedAbs, utils};
349 use num_traits::{CheckedSub, Signed, Zero};
350
351 let mut impact_diff = Zero::zero();
352 if impact.is_negative() {
353 let params = self.position_params()?;
354 let max_impact_factor = if for_liquidations {
355 params.max_position_impact_factor_for_liquidations()
356 } else {
357 params.max_negative_position_impact_factor()
358 };
359 let min_impact = utils::apply_factor(&size_delta_usd.unsigned_abs(), max_impact_factor)
363 .ok_or(crate::Error::Computation(
364 "calculating max negative position impact based on max factor",
365 ))?
366 .to_opposite_signed()?;
367 if *impact < min_impact {
368 impact_diff = min_impact
369 .checked_sub(impact)
370 .ok_or(crate::Error::Computation(
371 "overflow calculating impact diff",
372 ))?
373 .unsigned_abs();
374 *impact = min_impact;
375 }
376 }
377 Ok(impact_diff)
378 }
379}
380
381impl<M: PerpMarket<DECIMALS>, const DECIMALS: u8> PerpMarketExt<DECIMALS> for M {}
382
383pub trait PerpMarketMutExt<const DECIMALS: u8>: PerpMarketMut<DECIMALS> {
385 fn update_funding(
387 &mut self,
388 prices: &Prices<Self::Num>,
389 ) -> crate::Result<UpdateFundingState<&mut Self, DECIMALS>>
390 where
391 Self: Sized,
392 {
393 UpdateFundingState::try_new(self, prices)
394 }
395
396 fn apply_delta_to_funding_amount_per_size(
398 &mut self,
399 is_long: bool,
400 is_long_collateral: bool,
401 delta: &Self::Signed,
402 ) -> crate::Result<()> {
403 self.funding_amount_per_size_pool_mut(is_long)?
404 .apply_delta_amount(is_long_collateral, delta)
405 }
406
407 fn apply_delta_to_claimable_funding_amount_per_size(
409 &mut self,
410 is_long: bool,
411 is_long_collateral: bool,
412 delta: &Self::Signed,
413 ) -> crate::Result<()> {
414 self.claimable_funding_amount_per_size_pool_mut(is_long)?
415 .apply_delta_amount(is_long_collateral, delta)
416 }
417
418 fn apply_delta_to_open_interest(
420 &mut self,
421 is_long: bool,
422 is_long_collateral: bool,
423 delta: &Self::Signed,
424 ) -> crate::Result<()> {
425 let max_open_interest = self.max_open_interest(is_long)?;
427
428 let open_interest = self.open_interest_pool_mut(is_long)?;
429 if is_long_collateral {
430 open_interest.apply_delta_to_long_amount(delta)?;
431 } else {
432 open_interest.apply_delta_to_short_amount(delta)?;
433 }
434
435 if delta.is_positive() {
436 let is_exceeded = open_interest
437 .long_amount()?
438 .checked_add(&open_interest.short_amount()?)
439 .map(|total| total > max_open_interest)
440 .unwrap_or(true);
441
442 if is_exceeded {
443 return Err(crate::Error::MaxOpenInterestExceeded);
444 }
445 }
446
447 if let Some(mut pool) = self.virtual_inventory_for_positions_pool_mut()? {
449 let is_increased = !delta.is_negative();
450 let abs_delta = delta.unsigned_abs().to_signed()?;
451
452 match (is_long, is_increased) {
454 (true, true) | (false, false) => {
455 pool.apply_delta_to_long_amount(&abs_delta)?;
456 }
457 (true, false) | (false, true) => {
458 pool.apply_delta_to_short_amount(&abs_delta)?;
459 }
460 }
461 *pool = pool.checked_cancel_amounts()?;
462 }
463
464 Ok(())
465 }
466}
467
468impl<M: PerpMarketMut<DECIMALS>, const DECIMALS: u8> PerpMarketMutExt<DECIMALS> for M {}