use crate::trade::error::TradeError;
use crate::utils::json::Json;
use crate::utils::sign::goldilocks::Fp;
use crate::utils::sign::tx::{Attributes, BadTx, Tx, TxType, element, number};
use crate::utils::{MarginMode, MarketId};
pub const MARGIN_FRACTION_TICK: u16 = 10_000;
#[must_use]
pub const fn leverage_fraction(leverage: u16) -> Option<u16> {
match leverage {
0 => None,
leverage => match MARGIN_FRACTION_TICK / leverage {
0 => None,
fraction => Some(fraction),
},
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct MarginLimits {
pub min_initial_margin_fraction: u16,
pub isolated_only: bool,
}
impl MarginLimits {
#[must_use]
pub const fn max_leverage(&self) -> u16 {
match self.min_initial_margin_fraction {
0 => MARGIN_FRACTION_TICK,
least => match MARGIN_FRACTION_TICK / least {
0 => 1,
most => most,
},
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct UpdateLeverage {
pub market: MarketId,
pub initial_margin_fraction: u16,
pub mode: MarginMode,
pub attributes: Attributes,
}
impl UpdateLeverage {
pub fn new(market: MarketId, leverage: u16, mode: MarginMode) -> Result<UpdateLeverage, TradeError> {
let fraction = leverage_fraction(leverage).ok_or(TradeError::NoLeverage)?;
UpdateLeverage::fraction(market, fraction, mode)
}
pub fn fraction(market: MarketId, fraction: u16, mode: MarginMode) -> Result<UpdateLeverage, TradeError> {
let update = UpdateLeverage { market, initial_margin_fraction: fraction, mode, attributes: Attributes::NONE };
update.check()?;
Ok(update)
}
#[must_use]
pub const fn leverage(&self) -> u16 {
match self.initial_margin_fraction {
0 => 0,
fraction => MARGIN_FRACTION_TICK / fraction,
}
}
pub fn within(self, limits: MarginLimits) -> Result<UpdateLeverage, TradeError> {
if self.initial_margin_fraction < limits.min_initial_margin_fraction {
return Err(TradeError::Leverage { asked: self.leverage(), most: limits.max_leverage() });
}
if limits.isolated_only && self.mode == MarginMode::Cross {
return Err(TradeError::IsolatedOnly);
}
Ok(self)
}
#[must_use]
pub const fn with(mut self, attributes: Attributes) -> UpdateLeverage {
self.attributes = attributes;
self
}
}
impl Tx for UpdateLeverage {
fn tx_type(&self) -> TxType {
TxType::UpdateLeverage
}
fn hash_fields(&self) -> Vec<Fp> {
vec![element(self.market.get()), element(self.initial_margin_fraction), element(self.mode.code())]
}
fn info_fields(&self) -> Vec<(&'static str, Json)> {
vec![
("MarketIndex", number(self.market.get())),
("InitialMarginFraction", number(self.initial_margin_fraction)),
("MarginMode", number(self.mode.code())),
]
}
fn attributes(&self) -> Attributes {
self.attributes
}
fn check(&self) -> Result<(), BadTx> {
self.attributes.check()?;
if self.initial_margin_fraction == 0 {
return Err(BadTx::new("InitialMarginFraction", "a margin fraction is at least 1 basis point"));
}
if self.initial_margin_fraction > MARGIN_FRACTION_TICK {
return Err(BadTx::new("InitialMarginFraction", "a margin fraction is at most 10,000, the whole value"));
}
Ok(())
}
}
#[cfg(test)]
#[path = "tests/leverage.rs"]
mod tests;