#![allow(missing_docs)]
use crate::prelude::*;
use crate::storage::{MaxGainsInQuote, PricePoint};
pub struct BackwardsCompatTakeProfit<'a> {
pub collateral: NonZero<Collateral>,
pub direction: DirectionToBase,
pub leverage: LeverageToBase,
pub market_type: MarketType,
pub price_point: &'a PricePoint,
pub max_gains: MaxGainsInQuote,
pub take_profit: Option<PriceBaseInQuote>,
}
impl<'a> BackwardsCompatTakeProfit<'a> {
pub fn calc(self) -> Result<TakeProfitTrader> {
let BackwardsCompatTakeProfit {
collateral,
direction,
leverage,
market_type,
price_point,
max_gains,
take_profit,
} = self;
match take_profit {
Some(take_profit) => Ok(TakeProfitTrader::Finite(take_profit.into_non_zero())),
None => match max_gains {
MaxGainsInQuote::PosInfinity => Ok(TakeProfitTrader::PosInfinity),
MaxGainsInQuote::Finite(_) => {
let leverage_to_notional =
leverage.into_signed(direction).into_notional(market_type)?;
let notional_size_in_collateral =
leverage_to_notional.checked_mul_collateral(collateral)?;
let counter_collateral = max_gains.calculate_counter_collateral(
market_type,
collateral,
notional_size_in_collateral,
leverage_to_notional,
)?;
TakeProfitFromCounterCollateral {
counter_collateral,
market_type,
collateral,
leverage_to_base: self.leverage,
price_point,
direction,
}
.calc()
}
},
}
}
}
pub struct TakeProfitFromCounterCollateral<'a> {
pub market_type: MarketType,
pub collateral: NonZero<Collateral>,
pub counter_collateral: NonZero<Collateral>,
pub leverage_to_base: LeverageToBase,
pub price_point: &'a PricePoint,
pub direction: DirectionToBase,
}
impl<'a> TakeProfitFromCounterCollateral<'a> {
pub fn calc(&self) -> Result<TakeProfitTrader> {
let Self {
market_type,
collateral,
counter_collateral,
leverage_to_base,
price_point,
direction,
} = self;
let notional_size = calc_notional_size(
*leverage_to_base,
*direction,
*market_type,
price_point,
*collateral,
)?;
let take_profit_price_raw = price_point.price_notional.into_number().checked_add(
counter_collateral
.into_number()
.checked_div(notional_size.into_number())?,
)?;
let take_profit_price = if take_profit_price_raw.approx_eq(Number::ZERO)? {
None
} else {
debug_assert!(
take_profit_price_raw.is_positive_or_zero(),
"There should never be a calculated take profit price which is negative. In production, this is treated as 0 to indicate infinite max gains."
);
Price::try_from_number(take_profit_price_raw).ok()
};
match take_profit_price {
Some(price) => Ok(TakeProfitTrader::Finite(price.into_base_price(*market_type).into_non_zero())),
None =>
match market_type {
MarketType::CollateralIsBase => Ok(TakeProfitTrader::PosInfinity),
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)),
}
}
}
}
pub fn calc_notional_size(
leverage: LeverageToBase,
direction: DirectionToBase,
market_type: MarketType,
price_point: &PricePoint,
collateral: NonZero<Collateral>,
) -> Result<Signed<Notional>> {
let leverage_to_base = leverage.into_signed(direction);
let leverage_to_notional = leverage_to_base.into_notional(market_type)?;
let notional_size_in_collateral = leverage_to_notional.checked_mul_collateral(collateral)?;
Ok(notional_size_in_collateral.map(|x| price_point.collateral_to_notional(x)))
}