1use alloy::primitives::U256;
2use fastnum::{D64, D256, UD64, UD128, udec64};
3
4use super::num;
5use crate::{abi::dex::Exchange::PositionInfoV2, types};
6
7#[derive(Clone, Copy, Debug, Eq, PartialEq)]
8pub enum PositionType {
9 Long = 0,
10 Short = 1,
11}
12
13#[derive(Clone, derive_more::Debug)]
15pub struct Position {
16 instant: types::StateInstant,
17 funding_instant: types::StateInstant,
18 perpetual_id: types::PerpetualId,
19 account_id: types::AccountId,
20 r#type: PositionType,
21 #[debug("{entry_price}")]
22 entry_price: UD64, #[debug("{size}")]
24 size: UD64, #[debug("{deposit}")]
26 deposit: UD128, #[debug("{delta_pnl}")]
28 delta_pnl: D256, #[debug("{premium_pnl}")]
30 premium_pnl: D256, #[debug("{maintenance_margin_requirement}")]
32 maintenance_margin_requirement: UD128,
33}
34
35impl Position {
36 pub(crate) fn new(
37 instant: types::StateInstant,
38 perpetual_id: types::PerpetualId,
39 info: &PositionInfoV2,
40 collateral_converter: num::Converter,
41 price_converter: num::Converter,
42 size_converter: num::Converter,
43 maintenance_margin: UD64,
44 ) -> Self {
45 let r#type = info.positionType.into();
46 let entry_price = Self::effective_entry_price(
47 r#type,
48 info.pricePNS,
49 info.priceResiduePNSQ16.to(),
50 price_converter,
51 );
52 let size = size_converter.from_unsigned(info.lotLNS);
53 Self {
54 instant,
55 funding_instant: instant,
56 perpetual_id,
57 account_id: info.accountId.to(),
58 r#type,
59 entry_price,
60 size,
61 deposit: collateral_converter.from_unsigned(info.depositCNS),
62 delta_pnl: collateral_converter.from_signed(info.deltaPnlCNS),
63 premium_pnl: collateral_converter.from_signed(info.premiumPnlCNS),
64 maintenance_margin_requirement: entry_price.resize() * size.resize()
65 / maintenance_margin.resize(),
66 }
67 }
68
69 #[allow(clippy::too_many_arguments)]
70 pub(crate) fn opened(
71 instant: types::StateInstant,
72 perpetual_id: types::PerpetualId,
73 account_id: types::AccountId,
74 r#type: PositionType,
75 price_pns: U256,
76 price_residue_pnsq16: u32,
77 price_converter: num::Converter,
78 size: UD64,
79 deposit: UD128,
80 maintenance_margin: UD64,
81 ) -> Self {
82 let entry_price =
83 Self::effective_entry_price(r#type, price_pns, price_residue_pnsq16, price_converter);
84 Self {
85 instant,
86 funding_instant: instant,
87 perpetual_id,
88 account_id,
89 r#type,
90 entry_price,
91 size,
92 deposit,
93 delta_pnl: D256::ZERO,
94 premium_pnl: D256::ZERO,
95 maintenance_margin_requirement: entry_price.resize() * size.resize()
96 / maintenance_margin.resize(),
97 }
98 }
99
100 pub fn instant(&self) -> types::StateInstant { self.instant }
102
103 pub fn perpetual_id(&self) -> types::PerpetualId { self.perpetual_id }
105
106 pub fn account_id(&self) -> types::AccountId { self.account_id }
108
109 pub fn r#type(&self) -> PositionType { self.r#type }
111
112 pub fn entry_price(&self) -> UD64 { self.entry_price }
114
115 pub fn size(&self) -> UD64 { self.size }
117
118 pub fn deposit(&self) -> UD128 { self.deposit }
120
121 pub fn delta_pnl(&self) -> D256 { self.delta_pnl }
123
124 pub fn premium_pnl(&self) -> D256 { self.premium_pnl }
126
127 pub fn pnl(&self) -> D256 { self.delta_pnl + self.premium_pnl }
129
130 pub fn maintenance_margin_requirement(&self) -> UD128 { self.maintenance_margin_requirement }
132
133 pub fn liquidation_price(&self) -> UD64 {
135 let side = if self.r#type.is_long() { D256::ONE } else { D256::ONE.neg() };
136 let liquidation_price = self.entry_price.to_signed()
137 + (side
138 * (self.maintenance_margin_requirement.to_signed().resize()
139 - self.deposit.to_signed().resize()
140 - self.premium_pnl)
141 / self.size.to_signed().resize())
142 .resize();
143 liquidation_price.max(D64::ZERO).unsigned_abs()
144 }
145
146 pub fn bankruptcy_price(&self) -> UD64 {
148 let side = if self.r#type.is_long() { D256::ONE } else { D256::ONE.neg() };
149 let bankruptcy_price = self.entry_price.to_signed()
150 - (side * (self.deposit.to_signed().resize() + self.premium_pnl)
151 / self.size.to_signed().resize())
152 .resize();
153 bankruptcy_price.max(D64::ZERO).unsigned_abs()
154 }
155
156 pub(crate) fn update_type(&mut self, instant: types::StateInstant, r#type: PositionType) {
157 self.r#type = r#type;
158 self.instant = instant;
159 }
160
161 pub(crate) fn update_entry_price(
162 &mut self,
163 instant: types::StateInstant,
164 price_pns: U256,
165 price_residue_pnsq16: u32,
166 price_converter: num::Converter,
167 ) {
168 self.entry_price = Self::effective_entry_price(
169 self.r#type,
170 price_pns,
171 price_residue_pnsq16,
172 price_converter,
173 );
174 self.instant = instant;
175 }
176
177 pub(crate) fn update_size(&mut self, instant: types::StateInstant, size: UD64) {
178 self.size = size;
179 self.instant = instant;
180 }
181
182 pub(crate) fn update_deposit(&mut self, instant: types::StateInstant, deposit: UD128) {
183 self.deposit = deposit;
184 self.instant = instant;
185 }
186 pub(crate) fn update_premium_pnl(&mut self, instant: types::StateInstant, premium_pnl: D256) {
187 self.premium_pnl = premium_pnl;
188 self.instant = instant;
189 self.funding_instant = instant;
190 }
191
192 pub(crate) fn apply_mark_price(&mut self, instant: types::StateInstant, mark_price: UD64) {
193 let sign = if self.r#type.is_long() { D256::ONE } else { D256::ONE.neg() };
194 self.delta_pnl = sign
195 * (mark_price.resize().to_signed() - self.entry_price.resize().to_signed())
196 * self.size.resize().to_signed();
197 self.instant = instant;
198 }
199
200 pub(crate) fn apply_funding_payment(
201 &mut self,
202 instant: types::StateInstant,
203 payment_per_unit: D256,
204 ) -> bool {
205 if self.funding_instant >= instant {
207 return false;
208 }
209
210 let sign = if self.r#type.is_long() { D256::ONE.neg() } else { D256::ONE };
212 self.premium_pnl += sign * payment_per_unit * self.size.resize().to_signed();
213 self.instant = instant;
214 self.funding_instant = instant;
215 true
216 }
217
218 pub(crate) fn apply_maintenance_margin(
219 &mut self,
220 instant: types::StateInstant,
221 maintenance_margin: UD64,
222 ) {
223 self.maintenance_margin_requirement =
224 self.entry_price.resize() * self.size.resize() / maintenance_margin.resize();
225 self.instant = instant;
226 }
227
228 pub(crate) fn effective_entry_price(
243 position_type: PositionType,
244 price_pns: U256,
245 price_residue_pnsq16: u32,
246 price_converter: num::Converter,
247 ) -> UD64 {
248 const Q: UD64 = udec64!(65536).with_rounding_mode(fastnum::decimal::RoundingMode::Floor);
249 if price_residue_pnsq16 == 0 {
250 price_converter.from_unsigned(price_pns)
251 } else {
252 let mut entry_price = UD64::from_u64(price_pns.to())
253 .with_rounding_mode(fastnum::decimal::RoundingMode::Floor); if position_type.is_long() && entry_price >= UD64::ONE {
255 entry_price -= UD64::ONE;
256 }
257 (entry_price
258 + UD64::from_u32(price_residue_pnsq16)
259 .with_rounding_mode(fastnum::decimal::RoundingMode::Floor)
260 / Q)
261 / price_converter.scale()
262 }
263 }
264}
265
266impl PositionType {
267 pub fn is_long(&self) -> bool { matches!(self, PositionType::Long) }
268
269 pub fn is_short(&self) -> bool { matches!(self, PositionType::Short) }
270}
271
272impl From<u8> for PositionType {
273 fn from(value: u8) -> Self {
274 match value {
275 0 => PositionType::Long,
276 1 => PositionType::Short,
277 _ => unreachable!(),
278 }
279 }
280}
281
282impl std::fmt::Display for PositionType {
283 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
284 match self {
285 PositionType::Long => write!(f, "Long"),
286 PositionType::Short => write!(f, "Short"),
287 }
288 }
289}
290
291#[cfg(feature = "display")]
292impl tabled::Tabled for Position {
293 const LENGTH: usize = 9;
294
295 fn fields(&self) -> Vec<std::borrow::Cow<'_, str>> {
296 use colored::Colorize;
297 vec![
298 self.perpetual_id().to_string().into(),
299 if self.r#type.is_long() {
300 self.r#type().to_string().green().to_string().into()
301 } else {
302 self.r#type().to_string().red().to_string().into()
303 },
304 self.entry_price().to_string().into(),
305 self.size().to_string().into(),
306 self.deposit().to_string().into(),
307 if self.delta_pnl.is_negative() {
308 self.delta_pnl.to_string().red().to_string().into()
309 } else {
310 self.delta_pnl.to_string().green().to_string().into()
311 },
312 if self.premium_pnl.is_negative() {
313 self.premium_pnl.to_string().red().to_string().into()
314 } else {
315 self.premium_pnl.to_string().green().to_string().into()
316 },
317 if self.pnl().is_negative() {
318 self.pnl().to_string().red().to_string().into()
319 } else {
320 self.pnl().to_string().green().to_string().into()
321 },
322 format!("{:.6}", self.liquidation_price()).into(),
323 format!("{:.6}", self.bankruptcy_price()).into(),
324 ]
325 }
326
327 fn headers() -> Vec<std::borrow::Cow<'static, str>> {
328 vec![
329 "Perp ID".into(),
330 "Type".into(),
331 "Entry Price".into(),
332 "Size".into(),
333 "Deposit".into(),
334 "Delta PnL".into(),
335 "Premium PnL".into(),
336 "Total PnL".into(),
337 "Liq Price".into(),
338 "Bnkrp Price".into(),
339 ]
340 }
341}
342
343#[cfg(test)]
344mod tests {
345 use fastnum::{dec256, udec64, udec128};
346
347 use super::*;
348 use crate::types::StateInstant;
349
350 #[test]
351 fn test_effective_entry_price() {
352 let pc = num::Converter::new(4);
353 assert_eq!(
354 Position::effective_entry_price(PositionType::Long, U256::from(1), 0, pc),
355 udec64!(0.0001)
356 );
357 assert_eq!(
358 Position::effective_entry_price(PositionType::Long, U256::from(1), 1, pc),
359 udec64!(0.00000000152587890625) );
361 assert_eq!(
362 Position::effective_entry_price(PositionType::Long, U256::from(10001), 8, pc),
363 udec64!(1.00000001220703125) );
365 assert_eq!(
366 Position::effective_entry_price(PositionType::Long, U256::from(10000), 65535, pc),
367 udec64!(0.9999999984741210937) );
369
370 assert_eq!(
371 Position::effective_entry_price(PositionType::Short, U256::from(1), 0, pc),
372 udec64!(0.0001)
373 );
374 assert_eq!(
375 Position::effective_entry_price(PositionType::Short, U256::from(0), 1, pc),
376 udec64!(0.00000000152587890625) );
378 assert_eq!(
379 Position::effective_entry_price(PositionType::Short, U256::from(10000), 8, pc),
380 udec64!(1.00000001220703125) );
382 assert_eq!(
383 Position::effective_entry_price(PositionType::Short, U256::from(9999), 65535, pc),
384 udec64!(0.9999999984741210937) );
386 }
387
388 #[test]
389 fn test_apply_mark_price() {
390 let pc = num::Converter::new(4);
391 let mut pos = Position::opened(
392 StateInstant::default(),
393 1,
394 1,
395 PositionType::Long,
396 U256::from(100),
397 0,
398 pc,
399 udec64!(10),
400 UD128::ZERO,
401 UD64::ONE,
402 );
403
404 pos.apply_mark_price(StateInstant::default(), udec64!(0.015));
405 assert_eq!(pos.delta_pnl(), dec256!(0.05));
406
407 pos.apply_mark_price(StateInstant::default(), udec64!(0.005));
408 assert_eq!(pos.delta_pnl(), dec256!(-0.05));
409
410 let mut pos = Position::opened(
411 StateInstant::default(),
412 1,
413 1,
414 PositionType::Short,
415 U256::from(100),
416 0,
417 pc,
418 udec64!(10),
419 UD128::ZERO,
420 UD64::ONE,
421 );
422 pos.apply_mark_price(StateInstant::default(), udec64!(0.015));
423 assert_eq!(pos.delta_pnl(), dec256!(-0.05));
424
425 pos.apply_mark_price(StateInstant::default(), udec64!(0.005));
426 assert_eq!(pos.delta_pnl(), dec256!(0.05));
427 }
428
429 #[test]
430 fn test_apply_funding_payment() {
431 let pc = num::Converter::new(4);
432 let (i0, i1, i2) =
433 (StateInstant::default(), StateInstant::new(1, 1), StateInstant::new(2, 2));
434 let mut pos = Position::opened(
435 i0,
436 1,
437 1,
438 PositionType::Long,
439 U256::from(100),
440 0,
441 pc,
442 udec64!(10),
443 UD128::ZERO,
444 UD64::ONE,
445 );
446
447 assert!(pos.apply_funding_payment(i1, dec256!(5)));
448 assert_eq!(pos.premium_pnl(), dec256!(-50));
449
450 assert!(pos.apply_funding_payment(i2, dec256!(-10)));
451 assert_eq!(pos.premium_pnl(), dec256!(50));
452
453 assert!(!pos.apply_funding_payment(i2, dec256!(-10)));
454
455 let mut pos = Position::opened(
456 i0,
457 1,
458 1,
459 PositionType::Short,
460 U256::from(100),
461 0,
462 pc,
463 udec64!(10),
464 UD128::ZERO,
465 UD64::ONE,
466 );
467
468 pos.apply_funding_payment(i1, dec256!(5));
469 assert_eq!(pos.premium_pnl(), dec256!(50));
470
471 pos.apply_funding_payment(i2, dec256!(-10));
472 assert_eq!(pos.premium_pnl(), dec256!(-50));
473 }
474
475 fn short_at(instant: StateInstant) -> Position {
483 Position::opened(
484 instant,
485 1,
486 1,
487 PositionType::Short,
488 U256::from(100),
489 0,
490 num::Converter::new(4),
491 udec64!(10),
492 UD128::ZERO,
493 UD64::ONE,
494 )
495 }
496
497 #[test]
526 fn test_funding_applied_before_same_block_decrease() {
527 let (i1, i2) = (StateInstant::new(1, 1), StateInstant::new(2, 2));
531 let mut pos = short_at(StateInstant::default());
532 assert!(pos.apply_funding_payment(i1, dec256!(5))); assert!(
535 pos.apply_funding_payment(i2, dec256!(5)),
536 "funding must be applied before the same-block decrease"
537 );
538 assert_eq!(pos.premium_pnl(), dec256!(100));
539 pos.update_premium_pnl(i2, pos.premium_pnl());
542 assert_eq!(pos.premium_pnl(), dec256!(100));
543 }
544
545 #[test]
546 fn test_funding_block_single_decrease_matches_sc() {
547 let (i1, i2) = (StateInstant::new(1, 1), StateInstant::new(2, 2));
548 let mut pos = short_at(StateInstant::default());
550 assert!(pos.apply_funding_payment(i1, dec256!(5)));
551 assert!(pos.apply_funding_payment(i2, dec256!(1)));
555 assert_eq!(pos.premium_pnl(), dec256!(60));
556 pos.update_size(i2, udec64!(6));
558 pos.update_premium_pnl(i2, pos.premium_pnl() - dec256!(24));
559 assert_eq!(pos.premium_pnl(), dec256!(36));
562 }
563
564 #[test]
565 fn test_funding_block_two_decreases_matches_sc() {
566 let (i1, i2) = (StateInstant::new(1, 1), StateInstant::new(2, 2));
570 let mut pos = short_at(StateInstant::default());
572 assert!(pos.apply_funding_payment(i1, dec256!(5)));
573 assert!(pos.apply_funding_payment(i2, dec256!(1)));
575 assert_eq!(pos.premium_pnl(), dec256!(60));
576 pos.update_size(i2, udec64!(6));
578 pos.update_premium_pnl(i2, pos.premium_pnl() - dec256!(24)); pos.update_size(i2, udec64!(3));
581 pos.update_premium_pnl(i2, pos.premium_pnl() - dec256!(18)); assert_eq!(pos.premium_pnl(), dec256!(18));
585 }
586
587 #[test]
588 fn test_maintenance_margin_requirement() {
589 let pc = num::Converter::new(4);
590 let i0 = StateInstant::default();
591 let (mm1, mm2) = (udec64!(20), udec64!(10));
592
593 let mut pos = Position::opened(
594 i0,
595 1,
596 1,
597 PositionType::Long,
598 U256::from(1000000),
599 0,
600 pc,
601 udec64!(10),
602 udec128!(100),
603 mm1,
604 );
605 assert_eq!(pos.maintenance_margin_requirement(), udec128!(50));
606
607 pos.update_entry_price(i0, U256::from(800000), 0, pc);
608 pos.apply_maintenance_margin(i0, mm1);
609 assert_eq!(pos.maintenance_margin_requirement(), udec128!(40));
610
611 pos.update_size(i0, udec64!(20));
612 pos.apply_maintenance_margin(i0, mm1);
613 assert_eq!(pos.maintenance_margin_requirement(), udec128!(80));
614
615 pos.apply_maintenance_margin(i0, mm2);
616 assert_eq!(pos.maintenance_margin_requirement(), udec128!(160));
617
618 let mut pos = Position::opened(
619 i0,
620 1,
621 1,
622 PositionType::Short,
623 U256::from(1000000),
624 0,
625 pc,
626 udec64!(10),
627 udec128!(100),
628 mm1,
629 );
630 assert_eq!(pos.maintenance_margin_requirement(), udec128!(50));
631
632 pos.update_entry_price(i0, U256::from(800000), 0, pc);
633 pos.apply_maintenance_margin(i0, mm1);
634 assert_eq!(pos.maintenance_margin_requirement(), udec128!(40));
635
636 pos.update_size(i0, udec64!(20));
637 pos.apply_maintenance_margin(i0, mm1);
638 assert_eq!(pos.maintenance_margin_requirement(), udec128!(80));
639
640 pos.apply_maintenance_margin(i0, mm2);
641 assert_eq!(pos.maintenance_margin_requirement(), udec128!(160));
642 }
643
644 #[test]
645 fn test_liquidation_price() {
646 let pc = num::Converter::new(4);
647 let (i0, i1) = (StateInstant::default(), StateInstant::new(1, 1));
648 let mm1 = udec64!(20);
649
650 let mut pos = Position::opened(
651 i0,
652 1,
653 1,
654 PositionType::Long,
655 U256::from(1000000),
656 0,
657 pc,
658 udec64!(10),
659 udec128!(100),
660 mm1,
661 );
662 assert_eq!(pos.liquidation_price(), udec64!(95));
663
664 assert!(pos.apply_funding_payment(i1, dec256!(5)));
665 assert_eq!(pos.liquidation_price(), udec64!(100));
666
667 let mut pos = Position::opened(
668 i0,
669 1,
670 1,
671 PositionType::Short,
672 U256::from(1000000),
673 0,
674 pc,
675 udec64!(10),
676 udec128!(100),
677 mm1,
678 );
679 assert_eq!(pos.liquidation_price(), udec64!(105));
680
681 assert!(pos.apply_funding_payment(i1, dec256!(-5)));
682 assert_eq!(pos.liquidation_price(), udec64!(100));
683 }
684
685 #[test]
686 fn test_bankruptcy_price() {
687 let pc = num::Converter::new(4);
688 let (i0, i1) = (StateInstant::default(), StateInstant::new(1, 1));
689 let mm1 = udec64!(20);
690
691 let mut pos = Position::opened(
692 i0,
693 1,
694 1,
695 PositionType::Long,
696 U256::from(1000000),
697 0,
698 pc,
699 udec64!(10),
700 udec128!(100),
701 mm1,
702 );
703 assert_eq!(pos.bankruptcy_price(), udec64!(90));
704
705 assert!(pos.apply_funding_payment(i1, dec256!(5)));
706 assert_eq!(pos.bankruptcy_price(), udec64!(95));
707
708 let mut pos = Position::opened(
709 i0,
710 1,
711 1,
712 PositionType::Short,
713 U256::from(1000000),
714 0,
715 pc,
716 udec64!(10),
717 udec128!(100),
718 mm1,
719 );
720 assert_eq!(pos.bankruptcy_price(), udec64!(110));
721
722 assert!(pos.apply_funding_payment(i1, dec256!(-5)));
723 assert_eq!(pos.bankruptcy_price(), udec64!(105));
724 }
725
726 #[test]
727 fn test_liquidation_price_after_funding_received() {
728 let pc = num::Converter::new(4);
734 let (i0, i1) = (StateInstant::default(), StateInstant::new(1, 1));
735 let mm1 = udec64!(20);
736
737 let mut pos = Position::opened(
739 i0,
740 1,
741 1,
742 PositionType::Long,
743 U256::from(1000000),
744 0,
745 pc,
746 udec64!(10),
747 udec128!(100),
748 mm1,
749 );
750 assert_eq!(pos.liquidation_price(), udec64!(95)); assert!(pos.apply_funding_payment(i1, dec256!(-5)), "long receives funding");
752 assert_eq!(pos.premium_pnl(), dec256!(50)); assert_eq!(pos.liquidation_price(), udec64!(90)); let mut pos = Position::opened(
757 i0,
758 1,
759 1,
760 PositionType::Short,
761 U256::from(1000000),
762 0,
763 pc,
764 udec64!(10),
765 udec128!(100),
766 mm1,
767 );
768 assert_eq!(pos.liquidation_price(), udec64!(105)); assert!(pos.apply_funding_payment(i1, dec256!(5)), "short receives funding");
770 assert_eq!(pos.premium_pnl(), dec256!(50)); assert_eq!(pos.liquidation_price(), udec64!(110)); }
773
774 #[test]
775 fn test_bankruptcy_price_after_funding_received() {
776 let pc = num::Converter::new(4);
780 let (i0, i1) = (StateInstant::default(), StateInstant::new(1, 1));
781 let mm1 = udec64!(20);
782
783 let mut pos = Position::opened(
784 i0,
785 1,
786 1,
787 PositionType::Long,
788 U256::from(1000000),
789 0,
790 pc,
791 udec64!(10),
792 udec128!(100),
793 mm1,
794 );
795 assert_eq!(pos.bankruptcy_price(), udec64!(90)); assert!(pos.apply_funding_payment(i1, dec256!(-5)), "long receives funding"); assert_eq!(pos.bankruptcy_price(), udec64!(85)); let mut pos = Position::opened(
800 i0,
801 1,
802 1,
803 PositionType::Short,
804 U256::from(1000000),
805 0,
806 pc,
807 udec64!(10),
808 udec128!(100),
809 mm1,
810 );
811 assert_eq!(pos.bankruptcy_price(), udec64!(110)); assert!(pos.apply_funding_payment(i1, dec256!(5)), "short receives funding"); assert_eq!(pos.bankruptcy_price(), udec64!(115)); }
815
816 #[test]
817 fn test_apply_mark_price_with_residue() {
818 let i0 = StateInstant::default();
819 let half_lsb = dec256!(0.0000005);
820 let pc = num::Converter::new(6);
821
822 let mut pos = Position::opened(
823 i0,
824 1,
825 1,
826 PositionType::Long,
827 U256::from(100000000),
828 32768, pc,
830 udec64!(10),
831 UD128::ZERO,
832 UD64::ONE,
833 );
834 pos.apply_mark_price(i0, udec64!(100));
835 assert_eq!(pos.delta_pnl(), half_lsb * dec256!(10));
837
838 let mut pos = Position::opened(
839 i0,
840 1,
841 1,
842 PositionType::Short,
843 U256::from(100000000),
844 32768,
845 pc,
846 udec64!(10),
847 UD128::ZERO,
848 UD64::ONE,
849 );
850 pos.apply_mark_price(i0, udec64!(100));
851 assert_eq!(pos.delta_pnl(), half_lsb * dec256!(10));
854
855 let mut pos = Position::opened(
857 i0,
858 1,
859 1,
860 PositionType::Long,
861 U256::from(100000000),
862 0,
863 pc,
864 udec64!(10),
865 UD128::ZERO,
866 UD64::ONE,
867 );
868 pos.apply_mark_price(i0, udec64!(150));
869 assert_eq!(pos.delta_pnl(), dec256!(500));
870 }
871}