use crate::account::holdings::account::Account;
use crate::account::holdings::positions::Position;
use crate::utils::units::COLLATERAL_DECIMALS;
use crate::utils::{Dec, MarginMode, MarketId, Round};
pub const BPS: u32 = 10_000;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Setting {
pub market: MarketId,
pub leverage: u16,
pub margin_mode: MarginMode,
pub imf_bps: u32,
}
#[must_use]
pub fn imf_bps(percent: Dec) -> Option<u32> {
let bps = percent.units_at(2, Round::Nearest)?;
u32::try_from(bps).ok().filter(|bps| (1..=BPS).contains(bps))
}
#[must_use]
pub fn leverage_of(imf_bps: u32) -> Option<u16> {
BPS.checked_div(imf_bps).and_then(|leverage| u16::try_from(leverage).ok()).filter(|leverage| *leverage > 0)
}
impl Position {
#[must_use]
pub fn imf_bps(&self) -> Option<u32> {
imf_bps(self.initial_margin_fraction)
}
#[must_use]
pub fn leverage(&self) -> Option<u16> {
self.imf_bps().and_then(leverage_of)
}
#[must_use]
pub fn notional(&self) -> Dec {
self.position_value.abs()
}
#[must_use]
pub fn margin(&self) -> Option<Dec> {
match self.margin_mode {
MarginMode::Isolated => Some(self.allocated_margin),
MarginMode::Cross => {
let fraction = Dec::new(i128::from(self.imf_bps()?), 4)?;
Some(self.notional().checked_mul(fraction)?.round(COLLATERAL_DECIMALS, Round::Nearest))
}
}
}
#[must_use]
pub fn entry_price(&self) -> Option<Dec> {
Some(self.avg_entry_price).filter(|price| price.is_positive())
}
#[must_use]
pub fn liquidation(&self) -> Option<Dec> {
Some(self.liquidation_price).filter(|price| price.is_positive())
}
#[must_use]
pub fn setting(&self) -> Option<Setting> {
let imf_bps = self.imf_bps()?;
let leverage = leverage_of(imf_bps)?;
Some(Setting { market: self.market_id, leverage, margin_mode: self.margin_mode, imf_bps })
}
}
impl Account {
#[must_use]
pub fn setting(&self, market: MarketId) -> Option<Setting> {
self.position(market)?.setting()
}
}
#[cfg(test)]
#[path = "tests/margin.rs"]
mod tests;