levana_perpswap_cosmos/
compat.rs1#![allow(missing_docs)]
4
5use crate::prelude::*;
6use crate::storage::{MaxGainsInQuote, PricePoint};
7
8pub struct BackwardsCompatTakeProfit<'a> {
10 pub collateral: NonZero<Collateral>,
11 pub direction: DirectionToBase,
12 pub leverage: LeverageToBase,
13 pub market_type: MarketType,
14 pub price_point: &'a PricePoint,
15 pub max_gains: MaxGainsInQuote,
16 pub take_profit: Option<PriceBaseInQuote>,
17}
18
19impl<'a> BackwardsCompatTakeProfit<'a> {
20 pub fn calc(self) -> Result<TakeProfitTrader> {
21 let BackwardsCompatTakeProfit {
22 collateral,
23 direction,
24 leverage,
25 market_type,
26 price_point,
27 max_gains,
28 take_profit,
29 } = self;
30 match take_profit {
31 Some(take_profit) => Ok(TakeProfitTrader::Finite(take_profit.into_non_zero())),
32 None => match max_gains {
33 MaxGainsInQuote::PosInfinity => Ok(TakeProfitTrader::PosInfinity),
34 MaxGainsInQuote::Finite(_) => {
35 let leverage_to_notional =
36 leverage.into_signed(direction).into_notional(market_type)?;
37
38 let notional_size_in_collateral =
39 leverage_to_notional.checked_mul_collateral(collateral)?;
40
41 let counter_collateral = max_gains.calculate_counter_collateral(
42 market_type,
43 collateral,
44 notional_size_in_collateral,
45 leverage_to_notional,
46 )?;
47
48 TakeProfitFromCounterCollateral {
49 counter_collateral,
50 market_type,
51 collateral,
52 leverage_to_base: self.leverage,
53 price_point,
54 direction,
55 }
56 .calc()
57 }
58 },
59 }
60 }
61}
62
63pub struct TakeProfitFromCounterCollateral<'a> {
64 pub market_type: MarketType,
65 pub collateral: NonZero<Collateral>,
66 pub counter_collateral: NonZero<Collateral>,
67 pub leverage_to_base: LeverageToBase,
68 pub price_point: &'a PricePoint,
69 pub direction: DirectionToBase,
70}
71impl<'a> TakeProfitFromCounterCollateral<'a> {
72 pub fn calc(&self) -> Result<TakeProfitTrader> {
73 let Self {
74 market_type,
75 collateral,
76 counter_collateral,
77 leverage_to_base,
78 price_point,
79 direction,
80 } = self;
81
82 let notional_size = calc_notional_size(
83 *leverage_to_base,
84 *direction,
85 *market_type,
86 price_point,
87 *collateral,
88 )?;
89
90 let take_profit_price_raw = price_point.price_notional.into_number().checked_add(
91 counter_collateral
92 .into_number()
93 .checked_div(notional_size.into_number())?,
94 )?;
95
96 let take_profit_price = if take_profit_price_raw.approx_eq(Number::ZERO)? {
97 None
98 } else {
99 debug_assert!(
100 take_profit_price_raw.is_positive_or_zero(),
101 "There should never be a calculated take profit price which is negative. In production, this is treated as 0 to indicate infinite max gains."
102 );
103 Price::try_from_number(take_profit_price_raw).ok()
104 };
105
106 match take_profit_price {
107 Some(price) => Ok(TakeProfitTrader::Finite(price.into_base_price(*market_type).into_non_zero())),
108 None =>
109 match market_type {
110 MarketType::CollateralIsBase => Ok(TakeProfitTrader::PosInfinity),
112 MarketType::CollateralIsQuote => Err(anyhow!("Calculated a take profit price of {take_profit_price_raw} in a collateral-is-quote market. Spot notional price: {}. Counter collateral: {}. Notional size: {}.", price_point.price_notional, self.counter_collateral,notional_size)),
113 }
114 }
115 }
116}
117
118pub fn calc_notional_size(
119 leverage: LeverageToBase,
120 direction: DirectionToBase,
121 market_type: MarketType,
122 price_point: &PricePoint,
123 collateral: NonZero<Collateral>,
124) -> Result<Signed<Notional>> {
125 let leverage_to_base = leverage.into_signed(direction);
126
127 let leverage_to_notional = leverage_to_base.into_notional(market_type)?;
128
129 let notional_size_in_collateral = leverage_to_notional.checked_mul_collateral(collateral)?;
130
131 Ok(notional_size_in_collateral.map(|x| price_point.collateral_to_notional(x)))
132}