1use std::{fmt, ops::Deref};
2
3use num_traits::{CheckedNeg, One, Signed, Zero};
4
5use crate::{
6 action::{
7 decrease_position::{DecreasePosition, DecreasePositionFlags, DecreasePositionSwapType},
8 increase_position::IncreasePosition,
9 swap::SwapReport,
10 update_funding_state::unpack_to_funding_amount_delta,
11 },
12 fixed::FixedPointOps,
13 market::{
14 utils::MarketUtils, BaseMarketExt, BorrowingFeeMarket, BorrowingFeeMarketExt, PerpMarket,
15 PerpMarketExt, PositionImpactMarket,
16 },
17 num::{MulDiv, Num, Unsigned, UnsignedAbs},
18 params::fee::{FundingFees, PositionFees},
19 pool::delta::{BalanceChange, PriceImpact},
20 price::{Price, Prices},
21 BalanceExt, BaseMarket, Delta, PerpMarketMut, PerpMarketMutExt, PnlFactorKind, Pool, PoolExt,
22};
23
24pub trait PositionState<const DECIMALS: u8> {
26 type Num: MulDiv<Signed = Self::Signed> + FixedPointOps<DECIMALS>;
28
29 type Signed: UnsignedAbs<Unsigned = Self::Num> + TryFrom<Self::Num> + Num;
31
32 fn collateral_amount(&self) -> &Self::Num;
34
35 fn size_in_usd(&self) -> &Self::Num;
37
38 fn size_in_tokens(&self) -> &Self::Num;
40
41 fn borrowing_factor(&self) -> &Self::Num;
43
44 fn funding_fee_amount_per_size(&self) -> &Self::Num;
46
47 fn claimable_funding_fee_amount_per_size(&self, is_long_collateral: bool) -> &Self::Num;
49}
50
51pub trait PositionStateMut<const DECIMALS: u8>: PositionState<DECIMALS> {
53 fn collateral_amount_mut(&mut self) -> &mut Self::Num;
55
56 fn size_in_usd_mut(&mut self) -> &mut Self::Num;
58
59 fn size_in_tokens_mut(&mut self) -> &mut Self::Num;
61
62 fn borrowing_factor_mut(&mut self) -> &mut Self::Num;
64
65 fn funding_fee_amount_per_size_mut(&mut self) -> &mut Self::Num;
67
68 fn claimable_funding_fee_amount_per_size_mut(
70 &mut self,
71 is_long_collateral: bool,
72 ) -> &mut Self::Num;
73}
74
75pub trait Position<const DECIMALS: u8>: PositionState<DECIMALS> {
77 type Market: PerpMarket<DECIMALS, Num = Self::Num, Signed = Self::Signed>;
79
80 fn market(&self) -> &Self::Market;
82
83 fn is_long(&self) -> bool;
85
86 fn is_collateral_token_long(&self) -> bool;
88
89 fn are_pnl_and_collateral_tokens_the_same(&self) -> bool;
91
92 fn on_validate(&self) -> crate::Result<()>;
94}
95
96pub trait PositionMut<const DECIMALS: u8>: Position<DECIMALS> + PositionStateMut<DECIMALS> {
98 fn market_mut(&mut self) -> &mut Self::Market;
100
101 fn on_increased(&mut self) -> crate::Result<()>;
103
104 fn on_decreased(&mut self) -> crate::Result<()>;
106
107 fn on_swapped(
109 &mut self,
110 ty: DecreasePositionSwapType,
111 report: &SwapReport<Self::Num, <Self::Num as Unsigned>::Signed>,
112 ) -> crate::Result<()>;
113
114 fn on_swap_error(
116 &mut self,
117 ty: DecreasePositionSwapType,
118 error: crate::Error,
119 ) -> crate::Result<()>;
120}
121
122impl<const DECIMALS: u8, P: PositionState<DECIMALS>> PositionState<DECIMALS> for &mut P {
123 type Num = P::Num;
124
125 type Signed = P::Signed;
126
127 fn collateral_amount(&self) -> &Self::Num {
128 (**self).collateral_amount()
129 }
130
131 fn size_in_usd(&self) -> &Self::Num {
132 (**self).size_in_usd()
133 }
134
135 fn size_in_tokens(&self) -> &Self::Num {
136 (**self).size_in_tokens()
137 }
138
139 fn borrowing_factor(&self) -> &Self::Num {
140 (**self).borrowing_factor()
141 }
142
143 fn funding_fee_amount_per_size(&self) -> &Self::Num {
144 (**self).funding_fee_amount_per_size()
145 }
146
147 fn claimable_funding_fee_amount_per_size(&self, is_long_collateral: bool) -> &Self::Num {
148 (**self).claimable_funding_fee_amount_per_size(is_long_collateral)
149 }
150}
151
152impl<const DECIMALS: u8, P: Position<DECIMALS>> Position<DECIMALS> for &mut P {
153 type Market = P::Market;
154
155 fn market(&self) -> &Self::Market {
156 (**self).market()
157 }
158
159 fn is_long(&self) -> bool {
160 (**self).is_long()
161 }
162
163 fn is_collateral_token_long(&self) -> bool {
164 (**self).is_collateral_token_long()
165 }
166
167 fn are_pnl_and_collateral_tokens_the_same(&self) -> bool {
168 (**self).are_pnl_and_collateral_tokens_the_same()
169 }
170
171 fn on_validate(&self) -> crate::Result<()> {
172 (**self).on_validate()
173 }
174}
175
176impl<const DECIMALS: u8, P: PositionStateMut<DECIMALS>> PositionStateMut<DECIMALS> for &mut P {
177 fn collateral_amount_mut(&mut self) -> &mut Self::Num {
178 (**self).collateral_amount_mut()
179 }
180
181 fn size_in_usd_mut(&mut self) -> &mut Self::Num {
182 (**self).size_in_usd_mut()
183 }
184
185 fn size_in_tokens_mut(&mut self) -> &mut Self::Num {
186 (**self).size_in_tokens_mut()
187 }
188
189 fn borrowing_factor_mut(&mut self) -> &mut Self::Num {
190 (**self).borrowing_factor_mut()
191 }
192
193 fn funding_fee_amount_per_size_mut(&mut self) -> &mut Self::Num {
194 (**self).funding_fee_amount_per_size_mut()
195 }
196
197 fn claimable_funding_fee_amount_per_size_mut(
198 &mut self,
199 is_long_collateral: bool,
200 ) -> &mut Self::Num {
201 (**self).claimable_funding_fee_amount_per_size_mut(is_long_collateral)
202 }
203}
204
205impl<const DECIMALS: u8, P: PositionMut<DECIMALS>> PositionMut<DECIMALS> for &mut P {
206 fn market_mut(&mut self) -> &mut Self::Market {
207 (**self).market_mut()
208 }
209
210 fn on_increased(&mut self) -> crate::Result<()> {
211 (**self).on_increased()
212 }
213
214 fn on_decreased(&mut self) -> crate::Result<()> {
215 (**self).on_decreased()
216 }
217
218 fn on_swapped(
219 &mut self,
220 ty: DecreasePositionSwapType,
221 report: &SwapReport<Self::Num, <Self::Num as Unsigned>::Signed>,
222 ) -> crate::Result<()> {
223 (**self).on_swapped(ty, report)
224 }
225
226 fn on_swap_error(
227 &mut self,
228 ty: DecreasePositionSwapType,
229 error: crate::Error,
230 ) -> crate::Result<()> {
231 (**self).on_swap_error(ty, error)
232 }
233}
234
235pub trait PositionStateExt<const DECIMALS: u8>: PositionState<DECIMALS> {
237 fn is_empty(&self) -> bool {
239 self.size_in_usd().is_zero()
240 && self.size_in_tokens().is_zero()
241 && self.collateral_amount().is_zero()
242 }
243}
244
245impl<const DECIMALS: u8, P: PositionState<DECIMALS> + ?Sized> PositionStateExt<DECIMALS> for P {}
246
247pub trait PositionExt<const DECIMALS: u8>: Position<DECIMALS> {
249 fn will_collateral_be_sufficient(
254 &self,
255 prices: &Prices<Self::Num>,
256 delta: &CollateralDelta<Self::Num>,
257 ) -> crate::Result<WillCollateralBeSufficient<Self::Signed>> {
258 use num_traits::{CheckedAdd, CheckedMul};
259
260 let collateral_price = self.collateral_price(prices);
261
262 let mut remaining_collateral_value = delta
263 .next_collateral_amount
264 .checked_mul(collateral_price.pick_price(false))
265 .ok_or(crate::Error::Computation(
266 "overflow calculating collateral value",
267 ))?
268 .to_signed()?;
269
270 if delta.realized_pnl_value.is_negative() {
271 remaining_collateral_value = remaining_collateral_value
272 .checked_add(&delta.realized_pnl_value)
273 .ok_or(crate::Error::Computation("adding realized pnl"))?;
274 }
275
276 if remaining_collateral_value.is_negative() {
277 return Ok(WillCollateralBeSufficient::Insufficient(
278 remaining_collateral_value,
279 ));
280 }
281
282 let min_collateral_factor = self
283 .market()
284 .min_collateral_factor_for_open_interest(&delta.open_interest_delta, self.is_long())?
285 .max(
286 self.market()
287 .position_params()?
288 .min_collateral_factor()
289 .clone(),
290 );
291
292 match check_collateral(
293 &delta.next_size_in_usd,
294 &min_collateral_factor,
295 None,
296 true,
297 &remaining_collateral_value,
298 )? {
299 CheckCollateralResult::Sufficient => Ok(WillCollateralBeSufficient::Sufficient(
300 remaining_collateral_value,
301 )),
302 CheckCollateralResult::Negative | CheckCollateralResult::MinCollateralForLeverage => {
303 Ok(WillCollateralBeSufficient::Insufficient(
304 remaining_collateral_value,
305 ))
306 }
307 CheckCollateralResult::MinCollateral | CheckCollateralResult::Zero => unreachable!(),
308 }
309 }
310
311 fn collateral_price<'a>(&self, prices: &'a Prices<Self::Num>) -> &'a Price<Self::Num> {
313 if self.is_collateral_token_long() {
314 &prices.long_token_price
315 } else {
316 &prices.short_token_price
317 }
318 }
319
320 fn collateral_value(&self, prices: &Prices<Self::Num>) -> crate::Result<Self::Num> {
322 use num_traits::CheckedMul;
323
324 let collateral_token_price = self.collateral_price(prices).pick_price(false);
325
326 let collateral_value = self
327 .collateral_amount()
328 .checked_mul(collateral_token_price)
329 .ok_or(crate::Error::Computation(
330 "overflow calculating collateral value",
331 ))?;
332
333 Ok(collateral_value)
334 }
335
336 fn pnl_value(
340 &self,
341 prices: &Prices<Self::Num>,
342 size_delta_usd: &Self::Num,
343 ) -> crate::Result<(Self::Signed, Self::Signed, Self::Num)> {
344 use num_traits::{CheckedMul, CheckedSub};
345
346 let execution_price = &prices
347 .index_token_price
348 .pick_price_for_pnl(self.is_long(), false);
349
350 let position_value: Self::Signed = self
351 .size_in_tokens()
352 .checked_mul(execution_price)
353 .ok_or(crate::Error::Computation(
354 "overflow calculating position value",
355 ))?
356 .try_into()
357 .map_err(|_| crate::Error::Convert)?;
358 let size_in_usd = self
359 .size_in_usd()
360 .clone()
361 .try_into()
362 .map_err(|_| crate::Error::Convert)?;
363 let mut total_pnl = if self.is_long() {
364 position_value.checked_sub(&size_in_usd)
365 } else {
366 size_in_usd.checked_sub(&position_value)
367 }
368 .ok_or(crate::Error::Computation("calculating total pnl"))?;
369 let uncapped_total_pnl = total_pnl.clone();
370
371 if total_pnl.is_positive() {
372 let pool_value =
373 self.market()
374 .pool_value_without_pnl_for_one_side(prices, self.is_long(), false)?;
375 let pool_pnl = self
376 .market()
377 .pnl(&prices.index_token_price, self.is_long(), true)?;
378 let capped_pool_pnl = self.market().cap_pnl(
379 self.is_long(),
380 &pool_pnl,
381 &pool_value,
382 PnlFactorKind::MaxForTrader,
383 )?;
384
385 if capped_pool_pnl != pool_pnl
388 && !capped_pool_pnl.is_negative()
389 && pool_pnl.is_positive()
390 {
391 total_pnl = capped_pool_pnl
392 .unsigned_abs()
393 .checked_mul_div_with_signed_numerator(&total_pnl, &pool_pnl.unsigned_abs())
394 .ok_or(crate::Error::Computation("calculating capped total pnl"))?;
395 }
396 }
397
398 let size_delta_in_tokens = if *self.size_in_usd() == *size_delta_usd {
399 self.size_in_tokens().clone()
400 } else if self.is_long() {
401 self.size_in_tokens()
402 .checked_mul_div_ceil(size_delta_usd, self.size_in_usd())
403 .ok_or(crate::Error::Computation(
404 "calculating size delta in tokens for long",
405 ))?
406 } else {
407 self.size_in_tokens()
408 .checked_mul_div(size_delta_usd, self.size_in_usd())
409 .ok_or(crate::Error::Computation(
410 "calculating size delta in tokens for short",
411 ))?
412 };
413
414 let pnl_usd = size_delta_in_tokens
415 .checked_mul_div_with_signed_numerator(&total_pnl, self.size_in_tokens())
416 .ok_or(crate::Error::Computation("calculating pnl_usd"))?;
417
418 let uncapped_pnl_usd = size_delta_in_tokens
419 .checked_mul_div_with_signed_numerator(&uncapped_total_pnl, self.size_in_tokens())
420 .ok_or(crate::Error::Computation("calculating uncapped_pnl_usd"))?;
421
422 Ok((pnl_usd, uncapped_pnl_usd, size_delta_in_tokens))
423 }
424
425 fn validate(
427 &self,
428 prices: &Prices<Self::Num>,
429 should_validate_min_position_size: bool,
430 should_validate_min_collateral_usd: bool,
431 ) -> crate::Result<()> {
432 if self.size_in_usd().is_zero() || self.size_in_tokens().is_zero() {
433 return Err(crate::Error::InvalidPosition(
434 "size_in_usd or size_in_tokens is zero",
435 ));
436 }
437
438 self.on_validate()?;
439
440 if should_validate_min_position_size
441 && self.size_in_usd() < self.market().position_params()?.min_position_size_usd()
442 {
443 return Err(crate::Error::InvalidPosition("size in usd too small"));
444 }
445
446 if let Some(reason) = self.check_liquidatable(prices, should_validate_min_collateral_usd)? {
447 return Err(crate::Error::Liquidatable(reason));
448 }
449
450 Ok(())
451 }
452
453 fn check_liquidatable(
457 &self,
458 prices: &Prices<Self::Num>,
459 should_validate_min_collateral_usd: bool,
460 ) -> crate::Result<Option<LiquidatableReason>> {
461 use num_traits::{CheckedAdd, CheckedMul, CheckedSub};
462
463 let size_in_usd = self.size_in_usd();
464
465 let (pnl, _, _) = self.pnl_value(prices, size_in_usd)?;
466
467 let collateral_value = self.collateral_value(prices)?;
468 let collateral_price = self.collateral_price(prices);
469
470 let size_delta_usd = size_in_usd.to_opposite_signed()?;
471
472 let PriceImpact {
473 value: mut price_impact_value,
474 balance_change,
475 } = self.position_price_impact(&size_delta_usd, true)?;
476
477 if price_impact_value.is_negative() {
478 self.market().cap_negative_position_price_impact(
479 &size_delta_usd,
480 true,
481 &mut price_impact_value,
482 )?;
483 } else {
484 price_impact_value = Zero::zero();
485 }
486
487 let fees = self.position_fees(
488 collateral_price,
489 size_in_usd,
490 balance_change,
491 false,
493 )?;
494
495 let collateral_cost_value = fees
496 .total_cost_amount()?
497 .checked_mul(collateral_price.pick_price(false))
498 .ok_or(crate::Error::Computation(
499 "overflow calculating collateral cost value",
500 ))?;
501
502 let remaining_collateral_value = collateral_value
503 .to_signed()?
504 .checked_add(&pnl)
505 .and_then(|v| {
506 v.checked_add(&price_impact_value)?
507 .checked_sub(&collateral_cost_value.to_signed().ok()?)
508 })
509 .ok_or(crate::Error::Computation(
510 "calculating remaining collateral value",
511 ))?;
512
513 let params = self.market().position_params()?;
514
515 match check_collateral(
516 size_in_usd,
517 params.min_collateral_factor(),
518 should_validate_min_collateral_usd.then(|| params.min_collateral_value()),
519 false,
520 &remaining_collateral_value,
521 )? {
522 CheckCollateralResult::Sufficient => Ok(None),
523 CheckCollateralResult::Zero | CheckCollateralResult::Negative => {
524 Ok(Some(LiquidatableReason::NotPositive))
525 }
526 CheckCollateralResult::MinCollateralForLeverage => {
527 Ok(Some(LiquidatableReason::MinCollateralForLeverage))
528 }
529 CheckCollateralResult::MinCollateral => Ok(Some(LiquidatableReason::MinCollateral)),
530 }
531 }
532
533 fn position_price_impact(
535 &self,
536 size_delta_usd: &Self::Signed,
537 include_virtual_inventory_impact: bool,
538 ) -> crate::Result<PriceImpact<Self::Signed>> {
539 struct ReassignedValues<T> {
540 delta_long_usd_value: T,
541 delta_short_usd_value: T,
542 }
543
544 impl<T: Zero + Clone> ReassignedValues<T> {
545 fn new(is_long: bool, size_delta_usd: &T) -> Self {
546 if is_long {
547 Self {
548 delta_long_usd_value: size_delta_usd.clone(),
549 delta_short_usd_value: Zero::zero(),
550 }
551 } else {
552 Self {
553 delta_long_usd_value: Zero::zero(),
554 delta_short_usd_value: size_delta_usd.clone(),
555 }
556 }
557 }
558 }
559
560 let usd_price = One::one();
563
564 let ReassignedValues {
565 delta_long_usd_value,
566 delta_short_usd_value,
567 } = ReassignedValues::new(self.is_long(), size_delta_usd);
568
569 let params = self.market().position_impact_params()?;
570
571 let impact = self
572 .market()
573 .open_interest()?
574 .pool_delta_with_values(
575 delta_long_usd_value.clone(),
576 delta_short_usd_value.clone(),
577 &usd_price,
578 &usd_price,
579 )?
580 .price_impact(¶ms)?;
581
582 if !impact.value.is_negative() || !include_virtual_inventory_impact {
592 return Ok(impact);
593 }
594
595 let Some(virtual_inventory) = self.market().virtual_inventory_for_positions_pool()? else {
597 return Ok(impact);
598 };
599
600 let mut leftover = virtual_inventory.checked_cancel_amounts()?;
601
602 if size_delta_usd.is_negative() {
607 let offset = size_delta_usd
608 .checked_neg()
609 .ok_or(crate::Error::Computation(
610 "calculating virtual open interest offset",
611 ))?;
612 leftover =
613 leftover.checked_apply_delta(Delta::new_both_sides(true, &offset, &offset))?;
614 }
615
616 let virtual_impact = leftover
617 .pool_delta_with_values(
618 delta_long_usd_value,
619 delta_short_usd_value,
620 &usd_price,
621 &usd_price,
622 )?
623 .price_impact(¶ms)?;
624
625 if virtual_impact.value < impact.value {
626 Ok(virtual_impact)
627 } else {
628 Ok(impact)
629 }
630 }
631
632 #[inline]
634 fn capped_positive_position_price_impact(
635 &self,
636 index_token_price: &Price<Self::Num>,
637 size_delta_usd: &Self::Signed,
638 include_virtual_inventory_impact: bool,
639 ) -> crate::Result<PriceImpact<Self::Signed>> {
640 let mut impact =
641 self.position_price_impact(size_delta_usd, include_virtual_inventory_impact)?;
642 self.market().cap_positive_position_price_impact(
643 index_token_price,
644 size_delta_usd,
645 &mut impact.value,
646 )?;
647 Ok(impact)
648 }
649
650 #[inline]
655 fn capped_position_price_impact(
656 &self,
657 index_token_price: &Price<Self::Num>,
658 size_delta_usd: &Self::Signed,
659 include_virtual_inventory_impact: bool,
660 ) -> crate::Result<(PriceImpact<Self::Signed>, Self::Num)> {
661 let mut impact = self.capped_positive_position_price_impact(
662 index_token_price,
663 size_delta_usd,
664 include_virtual_inventory_impact,
665 )?;
666 let impact_diff = self.market().cap_negative_position_price_impact(
667 size_delta_usd,
668 false,
669 &mut impact.value,
670 )?;
671 Ok((impact, impact_diff))
672 }
673
674 fn pending_borrowing_fee_value(&self) -> crate::Result<Self::Num> {
676 use crate::utils;
677 use num_traits::CheckedSub;
678
679 let latest_factor = self.market().cumulative_borrowing_factor(self.is_long())?;
680 let diff_factor = latest_factor
681 .checked_sub(self.borrowing_factor())
682 .ok_or(crate::Error::Computation("invalid latest borrowing factor"))?;
683 utils::apply_factor(self.size_in_usd(), &diff_factor)
684 .ok_or(crate::Error::Computation("calculating borrowing fee value"))
685 }
686
687 fn pending_funding_fees(&self) -> crate::Result<FundingFees<Self::Num>> {
689 let adjustment = self.market().funding_amount_per_size_adjustment();
690 let fees = FundingFees::builder()
691 .amount(
692 unpack_to_funding_amount_delta(
693 &adjustment,
694 &self.market().funding_fee_amount_per_size(
695 self.is_long(),
696 self.is_collateral_token_long(),
697 )?,
698 self.funding_fee_amount_per_size(),
699 self.size_in_usd(),
700 true,
701 )
702 .ok_or(crate::Error::Computation("calculating funding fee amount"))?,
703 )
704 .claimable_long_token_amount(
705 unpack_to_funding_amount_delta(
706 &adjustment,
707 &self
708 .market()
709 .claimable_funding_fee_amount_per_size(self.is_long(), true)?,
710 self.claimable_funding_fee_amount_per_size(true),
711 self.size_in_usd(),
712 false,
713 )
714 .ok_or(crate::Error::Computation(
715 "calculating claimable long token funding fee amount",
716 ))?,
717 )
718 .claimable_short_token_amount(
719 unpack_to_funding_amount_delta(
720 &adjustment,
721 &self
722 .market()
723 .claimable_funding_fee_amount_per_size(self.is_long(), false)?,
724 self.claimable_funding_fee_amount_per_size(false),
725 self.size_in_usd(),
726 false,
727 )
728 .ok_or(crate::Error::Computation(
729 "calculating claimable short token funding fee amount",
730 ))?,
731 )
732 .build();
733 Ok(fees)
734 }
735
736 fn position_fees(
738 &self,
739 collateral_token_price: &Price<Self::Num>,
740 size_delta_usd: &Self::Num,
741 balance_change: BalanceChange,
742 is_liquidation: bool,
743 ) -> crate::Result<PositionFees<Self::Num>> {
744 debug_assert!(!collateral_token_price.has_zero(), "must be non-zero");
745
746 let liquidation_fees = is_liquidation
747 .then(|| {
748 self.market()
751 .liquidation_fee_params()?
752 .fee(size_delta_usd, collateral_token_price)
753 })
754 .transpose()?;
755
756 let fees = self
757 .market()
758 .order_fee_params()?
759 .base_position_fees(collateral_token_price, size_delta_usd, balance_change)?
760 .set_borrowing_fees(
761 self.market().borrowing_fee_params()?.receiver_factor(),
762 collateral_token_price,
763 self.pending_borrowing_fee_value()?,
764 )?
765 .set_funding_fees(self.pending_funding_fees()?)
766 .set_liquidation_fees(liquidation_fees);
767 Ok(fees)
768 }
769}
770
771impl<const DECIMALS: u8, P: Position<DECIMALS>> PositionExt<DECIMALS> for P {}
772
773pub trait PositionMutExt<const DECIMALS: u8>: PositionMut<DECIMALS>
775where
776 Self::Market: PerpMarketMut<DECIMALS, Num = Self::Num, Signed = Self::Signed>,
777{
778 fn increase(
780 &mut self,
781 prices: Prices<Self::Num>,
782 collateral_increment_amount: Self::Num,
783 size_delta_usd: Self::Num,
784 acceptable_price: Option<Self::Num>,
785 ) -> crate::Result<IncreasePosition<&mut Self, DECIMALS>>
786 where
787 Self: Sized,
788 {
789 IncreasePosition::try_new(
790 self,
791 prices,
792 collateral_increment_amount,
793 size_delta_usd,
794 acceptable_price,
795 )
796 }
797
798 fn decrease(
800 &mut self,
801 prices: Prices<Self::Num>,
802 size_delta_usd: Self::Num,
803 acceptable_price: Option<Self::Num>,
804 collateral_withdrawal_amount: Self::Num,
805 flags: DecreasePositionFlags,
806 ) -> crate::Result<DecreasePosition<&mut Self, DECIMALS>>
807 where
808 Self: Sized,
809 {
810 DecreasePosition::try_new(
811 self,
812 prices,
813 size_delta_usd,
814 acceptable_price,
815 collateral_withdrawal_amount,
816 flags,
817 )
818 }
819
820 fn update_open_interest(
822 &mut self,
823 size_delta_usd: &Self::Signed,
824 size_delta_in_tokens: &Self::Signed,
825 ) -> crate::Result<()> {
826 if size_delta_usd.is_zero() {
827 return Ok(());
828 }
829
830 let is_long_collateral = self.is_collateral_token_long();
831 let is_long = self.is_long();
832
833 self.market_mut().apply_delta_to_open_interest(
834 is_long,
835 is_long_collateral,
836 size_delta_usd,
837 )?;
838
839 let open_interest_in_tokens = self
840 .market_mut()
841 .open_interest_in_tokens_pool_mut(is_long)?;
842 if is_long_collateral {
843 open_interest_in_tokens.apply_delta_to_long_amount(size_delta_in_tokens)?;
844 } else {
845 open_interest_in_tokens.apply_delta_to_short_amount(size_delta_in_tokens)?;
846 }
847
848 Ok(())
849 }
850
851 fn update_total_borrowing(
853 &mut self,
854 next_size_in_usd: &Self::Num,
855 next_borrowing_factor: &Self::Num,
856 ) -> crate::Result<()> {
857 let is_long = self.is_long();
858 let previous = crate::utils::apply_factor(self.size_in_usd(), self.borrowing_factor())
859 .ok_or(crate::Error::Computation("calculating previous borrowing"))?;
860
861 let total_borrowing = self.market_mut().total_borrowing_pool_mut()?;
862
863 let delta = {
864 let next = crate::utils::apply_factor(next_size_in_usd, next_borrowing_factor)
865 .ok_or(crate::Error::Computation("calculating next borrowing"))?;
866 next.checked_signed_sub(previous)?
867 };
868
869 total_borrowing.apply_delta_amount(is_long, &delta)?;
870
871 Ok(())
872 }
873}
874
875impl<const DECIMALS: u8, P: PositionMut<DECIMALS>> PositionMutExt<DECIMALS> for P where
876 P::Market: PerpMarketMut<DECIMALS, Num = Self::Num, Signed = Self::Signed>
877{
878}
879
880pub struct CollateralDelta<T: Unsigned> {
882 next_size_in_usd: T,
883 next_collateral_amount: T,
884 realized_pnl_value: T::Signed,
885 open_interest_delta: T::Signed,
886}
887
888impl<T: Unsigned> CollateralDelta<T> {
889 pub fn new(
891 next_size_in_usd: T,
892 next_collateral_amount: T,
893 realized_pnl_value: T::Signed,
894 open_interest_delta: T::Signed,
895 ) -> Self {
896 Self {
897 next_size_in_usd,
898 next_collateral_amount,
899 realized_pnl_value,
900 open_interest_delta,
901 }
902 }
903}
904
905#[derive(Clone, Copy)]
907pub enum WillCollateralBeSufficient<T> {
908 Sufficient(T),
910 Insufficient(T),
912}
913
914impl<T> WillCollateralBeSufficient<T> {
915 pub fn is_sufficient(&self) -> bool {
917 matches!(self, Self::Sufficient(_))
918 }
919}
920
921impl<T> Deref for WillCollateralBeSufficient<T> {
922 type Target = T;
923
924 fn deref(&self) -> &Self::Target {
925 match self {
926 Self::Sufficient(v) => v,
927 Self::Insufficient(v) => v,
928 }
929 }
930}
931
932#[derive(Debug, Clone, Copy)]
934pub enum LiquidatableReason {
935 MinCollateral,
937 NotPositive,
939 MinCollateralForLeverage,
941}
942
943impl fmt::Display for LiquidatableReason {
944 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
945 match self {
946 Self::MinCollateral => write!(f, "min collateral"),
947 Self::NotPositive => write!(f, "<= 0"),
948 Self::MinCollateralForLeverage => write!(f, "min collateral for leverage"),
949 }
950 }
951}
952
953enum CheckCollateralResult {
954 Sufficient,
955 Zero,
956 Negative,
957 MinCollateralForLeverage,
958 MinCollateral,
959}
960
961fn check_collateral<T, const DECIMALS: u8>(
962 size_in_usd: &T,
963 min_collateral_factor: &T,
964 min_collateral_value: Option<&T>,
965 allow_zero_collateral: bool,
966 collateral_value: &T::Signed,
967) -> crate::Result<CheckCollateralResult>
968where
969 T: FixedPointOps<DECIMALS>,
970{
971 if collateral_value.is_negative() {
972 if min_collateral_value.is_some() {
973 Ok(CheckCollateralResult::MinCollateral)
975 } else {
976 Ok(CheckCollateralResult::Negative)
977 }
978 } else {
979 let collateral_value = collateral_value.unsigned_abs();
980
981 if let Some(min_collateral_value) = min_collateral_value {
982 if collateral_value < *min_collateral_value {
983 return Ok(CheckCollateralResult::MinCollateral);
984 }
985 }
986
987 if !allow_zero_collateral && collateral_value.is_zero() {
988 return Ok(CheckCollateralResult::Zero);
989 }
990
991 let min_collateral_usd_for_leverage =
992 crate::utils::apply_factor(size_in_usd, min_collateral_factor).ok_or(
993 crate::Error::Computation("calculating min collateral usd for leverage"),
994 )?;
995
996 if collateral_value < min_collateral_usd_for_leverage {
997 return Ok(CheckCollateralResult::MinCollateralForLeverage);
998 }
999
1000 Ok(CheckCollateralResult::Sufficient)
1001 }
1002}
1003
1004#[derive(Debug, Clone, Copy)]
1006#[cfg_attr(
1007 feature = "anchor-lang",
1008 derive(
1009 anchor_lang::AnchorDeserialize,
1010 anchor_lang::AnchorSerialize,
1011 anchor_lang::InitSpace
1012 )
1013)]
1014#[non_exhaustive]
1015pub enum InsolventCloseStep {
1016 Pnl,
1018 Fees,
1020 Funding,
1022 Impact,
1024 Diff,
1026}