df_hyperliquid_core/trade/margin/
isolated.rs1use crate::trade::error::TradeError;
4use crate::trade::margin::MarginMode;
5use crate::trade::orders::Side;
6use crate::trade::ticket::{Market, Refusal};
7use crate::utils::sign::{L1Action, Map};
8use crate::utils::{Asset, BadUnits, Dec, Round};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub struct UpdateIsolatedMargin {
15 asset: Asset,
16 position: Side,
17 ntli: i64,
18}
19
20impl UpdateIsolatedMargin {
21 pub fn new(asset: Asset, position: Side, usdc: Dec) -> Result<UpdateIsolatedMargin, TradeError> {
24 let bad = |error: BadUnits| TradeError::Margin(error);
25 if usdc.places() > 6 {
26 return Err(bad(BadUnits::TooFine { value: usdc, places: 6 }));
27 }
28 let ntli = usdc.units_at(6, Round::Down).and_then(|units| i64::try_from(units).ok());
29 match ntli {
30 Some(0) => Err(bad(BadUnits::NotPositive(usdc))),
31 Some(ntli) => Ok(UpdateIsolatedMargin { asset, position, ntli }),
32 None => Err(bad(BadUnits::TooLarge(usdc))),
33 }
34 }
35
36 #[must_use]
37 pub const fn ntli(&self) -> i64 {
38 self.ntli
39 }
40}
41
42impl L1Action for UpdateIsolatedMargin {
43 fn action(&self) -> Map {
44 Map::new()
45 .put("type", "updateIsolatedMargin")
46 .put("asset", self.asset)
47 .put("isBuy", self.position.is_buy())
48 .put("ntli", self.ntli)
49 }
50}
51
52pub fn add_margin(asset: Asset, position: Side, usdc: Dec) -> Result<UpdateIsolatedMargin, TradeError> {
54 match usdc.is_positive() {
55 true => UpdateIsolatedMargin::new(asset, position, usdc),
56 false => Err(TradeError::Margin(BadUnits::NotPositive(usdc))),
57 }
58}
59
60pub fn remove_margin(asset: Asset, position: Side, usdc: Dec) -> Result<UpdateIsolatedMargin, TradeError> {
62 match usdc.is_positive() {
63 true => UpdateIsolatedMargin::new(asset, position, usdc.negate()),
64 false => Err(TradeError::Margin(BadUnits::NotPositive(usdc))),
65 }
66}
67
68pub fn margin(market: &Market, position: Dec, mode: MarginMode, usdc: Dec) -> Result<UpdateIsolatedMargin, Refusal> {
71 if market.is_spot() {
72 return Err(Refusal::Spot("margin"));
73 }
74 if position.is_zero() {
75 return Err(Refusal::NoPosition);
76 }
77 if mode == MarginMode::Cross {
78 return Err(Refusal::CrossPosition);
79 }
80 let side = match position.is_positive() {
81 true => Side::Buy,
82 false => Side::Sell,
83 };
84 Ok(UpdateIsolatedMargin::new(market.asset(), side, usdc)?)
85}
86
87#[cfg(test)]
88#[path = "tests/isolated.rs"]
89mod tests;