use df_hyperliquid_core::utils::Json;
use crate::utils::wire::{BadWire, bad, bool_of, nullable, str_of, tuple, u32_of, u64_of, u128_of};
pub const NO_CAP: u128 = u128::MAX;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Instrument {
pub id: u32,
pub symbol: String,
pub asset_index: u32,
pub price_scale: u64,
pub active: bool,
pub paused: bool,
pub terminal: bool,
pub retired: bool,
pub close_only: bool,
pub frozen_bid: Option<u128>,
pub frozen_ask: Option<u128>,
pub paused_at: Option<u64>,
pub rate_multiplier: u128,
pub position_multiplier: u128,
pub max_leverage: u32,
pub max_position_notional: u128,
pub bust_buffer: u128,
pub base_rate: u128,
pub reference_notional: u128,
pub long_oi: u128,
pub short_oi: u128,
pub long_cap: u128,
pub short_cap: u128,
}
impl Instrument {
#[must_use]
pub const fn openable(&self) -> bool {
self.active && !self.paused
}
#[must_use]
pub fn frozen_mid(&self) -> Option<u128> {
crate::utils::units::mid(self.frozen_bid?, self.frozen_ask?)
}
pub(super) fn read(row: &Json) -> Result<Instrument, BadWire> {
let f = tuple(row, 23, "trading_instrument_tuple")?;
let instrument = Instrument {
id: u32_of(&f[0], "trading_instrument_id")?,
symbol: str_of(&f[1], "trading_symbol")?.to_owned(),
asset_index: u32_of(&f[2], "trading_asset_index")?,
price_scale: u64_of(&f[3], "trading_price_scale")?,
active: bool_of(&f[4], "trading_lifecycle")?,
paused: bool_of(&f[5], "trading_lifecycle")?,
terminal: bool_of(&f[6], "trading_lifecycle")?,
retired: bool_of(&f[7], "trading_lifecycle")?,
close_only: bool_of(&f[8], "trading_lifecycle")?,
frozen_bid: nullable(&f[9], "trading_frozen_bid", u128_of)?,
frozen_ask: nullable(&f[10], "trading_frozen_ask", u128_of)?,
paused_at: nullable(&f[11], "trading_paused_at", u64_of)?,
rate_multiplier: u128_of(&f[12], "trading_rate_multiplier")?,
position_multiplier: u128_of(&f[13], "trading_position_multiplier")?,
max_leverage: u32_of(&f[14], "trading_max_leverage")?,
max_position_notional: u128_of(&f[15], "trading_max_position")?,
bust_buffer: u128_of(&f[16], "trading_bust_buffer")?,
base_rate: u128_of(&f[17], "trading_base_rate")?,
reference_notional: u128_of(&f[18], "trading_reference_notional")?,
long_oi: u128_of(&f[19], "trading_long_oi")?,
short_oi: u128_of(&f[20], "trading_short_oi")?,
long_cap: u128_of(&f[21], "trading_long_cap")?,
short_cap: u128_of(&f[22], "trading_short_cap")?,
};
let frozen = [instrument.frozen_bid.is_some(), instrument.frozen_ask.is_some(), instrument.paused_at.is_some()];
let consistent = frozen.iter().all(|set| *set == instrument.terminal)
&& instrument.terminal == instrument.paused
&& instrument.close_only == instrument.paused
&& instrument.retired == (instrument.paused || !instrument.active)
&& instrument.price_scale > 0
&& crate::utils::units::scale_places(instrument.price_scale).is_some();
if !consistent {
return Err(bad("trading_lifecycle", row));
}
Ok(instrument)
}
}
#[cfg(test)]
#[path = "tests/instrument.rs"]
mod tests;