use crate::math::leverage_tiers::LeverageTiers;
use crate::math::quantities::{
BasisPoints, QuoteLotsPerBaseLotPerTick, SignedQuoteLotsPerBaseLot, Ticks, WrapperNum,
};
use crate::math::risk::{ProgramError, RiskAction, RiskTier};
#[derive(Debug, Clone)]
pub struct PerpAssetMetadata {
pub symbol: String,
pub asset_id: u64,
pub base_lot_decimals: i8,
pub mark_price: Ticks,
pub tick_size: QuoteLotsPerBaseLotPerTick,
pub leverage_tiers: LeverageTiers,
pub risk_factors: [u16; 3],
pub cancel_order_risk_factor: u16,
pub upnl_risk_factor: u16,
pub upnl_risk_factor_for_withdrawals: u16,
pub cumulative_funding_rate: SignedQuoteLotsPerBaseLot,
}
impl PerpAssetMetadata {
pub fn new(
symbol: String,
asset_id: u64,
base_lot_decimals: i8,
mark_price: Ticks,
tick_size: QuoteLotsPerBaseLotPerTick,
leverage_tiers: LeverageTiers,
risk_factors: [u16; 3],
cancel_order_risk_factor: u16,
upnl_risk_factor: u16,
upnl_risk_factor_for_withdrawals: u16,
) -> Self {
Self {
symbol,
asset_id,
base_lot_decimals,
mark_price,
tick_size,
leverage_tiers,
risk_factors,
cancel_order_risk_factor,
upnl_risk_factor,
upnl_risk_factor_for_withdrawals,
cumulative_funding_rate: SignedQuoteLotsPerBaseLot::ZERO,
}
}
#[inline(always)]
pub fn try_get_mark_price(&self, _risk_action: RiskAction) -> Result<Ticks, ProgramError> {
Ok(self.mark_price)
}
#[inline(always)]
pub fn base_lot_decimals(&self) -> i8 {
self.base_lot_decimals
}
#[inline(always)]
pub fn asset_id(&self) -> u64 {
self.asset_id
}
#[inline(always)]
pub fn cumulative_funding_rate(&self) -> SignedQuoteLotsPerBaseLot {
self.cumulative_funding_rate
}
#[inline]
pub fn set_mark_price(&mut self, new_price: Ticks) {
self.mark_price = new_price;
}
#[inline]
pub fn tick_size(&self) -> QuoteLotsPerBaseLotPerTick {
self.tick_size
}
#[inline]
pub fn leverage_tiers(&self) -> &LeverageTiers {
&self.leverage_tiers
}
#[inline]
pub fn get_risk_factor(&self, risk_tier: RiskTier) -> BasisPoints {
let index = match risk_tier {
RiskTier::Liquidatable => 0, RiskTier::BackstopLiquidatable => 1, RiskTier::HighRisk => 2, _ => return BasisPoints::new(10_000), };
BasisPoints::from_u16(self.risk_factors[index]).unwrap_or_else(|| BasisPoints::new(10_000))
}
#[inline]
pub fn cancel_order_risk_factor(&self) -> BasisPoints {
BasisPoints::from_u16(self.cancel_order_risk_factor)
.unwrap_or_else(|| BasisPoints::new(10_000))
}
#[inline]
pub fn upnl_risk_factor(&self, risk_action: RiskAction) -> BasisPoints {
let factor = match risk_action {
RiskAction::Withdrawal { .. } => self.upnl_risk_factor_for_withdrawals,
_ => self.upnl_risk_factor,
};
BasisPoints::from_u16(factor).unwrap_or_else(|| BasisPoints::new(10_000))
}
}
impl Default for PerpAssetMetadata {
fn default() -> Self {
Self {
symbol: String::from("UNKNOWN"),
asset_id: 0,
base_lot_decimals: 0,
mark_price: Ticks::new(1_000_000), tick_size: QuoteLotsPerBaseLotPerTick::new(1),
leverage_tiers: LeverageTiers::default(),
risk_factors: [5_000, 4_000, 3_000], cancel_order_risk_factor: 5_500, upnl_risk_factor: 5_000, upnl_risk_factor_for_withdrawals: 7_500, cumulative_funding_rate: SignedQuoteLotsPerBaseLot::ZERO,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_try_get_mark_price() {
let metadata = PerpAssetMetadata::default();
let price = metadata
.try_get_mark_price(RiskAction::View)
.expect("Should return mark price");
assert_eq!(price, Ticks::new(1_000_000));
}
#[test]
fn test_set_mark_price() {
let mut metadata = PerpAssetMetadata::default();
metadata.set_mark_price(Ticks::new(2_000_000));
assert_eq!(metadata.mark_price, Ticks::new(2_000_000));
}
#[test]
fn test_get_risk_factor() {
let metadata = PerpAssetMetadata::default();
assert_eq!(
metadata.get_risk_factor(RiskTier::Liquidatable),
BasisPoints::new(5_000)
);
assert_eq!(
metadata.get_risk_factor(RiskTier::BackstopLiquidatable),
BasisPoints::new(4_000)
);
assert_eq!(
metadata.get_risk_factor(RiskTier::HighRisk),
BasisPoints::new(3_000)
);
assert_eq!(
metadata.get_risk_factor(RiskTier::Safe),
BasisPoints::new(10_000)
);
}
#[test]
fn test_cancel_order_risk_factor() {
let metadata = PerpAssetMetadata::default();
assert_eq!(metadata.cancel_order_risk_factor(), BasisPoints::new(5_500));
}
#[test]
fn test_upnl_risk_factor() {
let metadata = PerpAssetMetadata::default();
assert_eq!(
metadata.upnl_risk_factor(RiskAction::View),
BasisPoints::new(5_000)
);
assert_eq!(
metadata.upnl_risk_factor(RiskAction::Withdrawal {
current_slot: crate::math::quantities::Slot::new(1000)
}),
BasisPoints::new(7_500)
);
}
}