use std::collections::BTreeMap;
use serde::Deserialize;
use crate::account::holdings::positions::{Asset, Position};
use crate::client::{ApiError, Call, Transport, read};
use crate::utils::lenient::{blank_dec, list, maybe_market, unsigned, zero_dec};
use crate::utils::{AccountIndex, Dec, Figure, MarketId};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum LiquidationKind {
Partial,
Deleverage,
#[serde(other)]
Other,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct LiquidationTrade {
pub price: Dec,
pub size: Dec,
#[serde(default, deserialize_with = "blank_dec")]
pub taker_fee: Option<Dec>,
#[serde(default, deserialize_with = "blank_dec")]
pub maker_fee: Option<Dec>,
#[serde(default, deserialize_with = "unsigned")]
pub transaction_time: u64,
pub ask_account_id: AccountIndex,
pub bid_account_id: AccountIndex,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct RiskParameters {
#[serde(default, deserialize_with = "maybe_market")]
pub market_id: Option<MarketId>,
#[serde(default, deserialize_with = "zero_dec")]
pub collateral: Dec,
#[serde(default, deserialize_with = "zero_dec")]
pub total_account_value: Dec,
#[serde(default, deserialize_with = "zero_dec")]
pub initial_margin_req: Dec,
#[serde(default, deserialize_with = "zero_dec")]
pub maintenance_margin_req: Dec,
#[serde(default, deserialize_with = "zero_dec")]
pub close_out_margin_req: Dec,
#[serde(default, deserialize_with = "zero_dec")]
pub total_account_liquidation_threshold: Dec,
#[serde(default, deserialize_with = "zero_dec")]
pub usdc_collateral_with_funding: Dec,
#[serde(default, deserialize_with = "zero_dec")]
pub usdc_portfolio_value: Dec,
#[serde(default, deserialize_with = "zero_dec")]
pub total_portfolio_value: Dec,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct RiskInfo {
pub cross_risk_parameters: Option<RiskParameters>,
#[serde(default, deserialize_with = "list")]
pub isolated_risk_parameters: Vec<RiskParameters>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct LiquidationState {
#[serde(default, deserialize_with = "list")]
pub positions: Vec<Position>,
#[serde(default, deserialize_with = "list")]
pub assets: Vec<Asset>,
#[serde(default)]
pub risk_info_before: Option<RiskInfo>,
#[serde(default)]
pub risk_info_after: Option<RiskInfo>,
#[serde(default)]
pub mark_prices: BTreeMap<String, Figure>,
#[serde(default)]
pub asset_index_prices: BTreeMap<String, String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct Liquidation {
#[serde(deserialize_with = "unsigned")]
pub id: u64,
pub market_id: MarketId,
#[serde(rename = "type")]
pub kind: LiquidationKind,
pub trade: LiquidationTrade,
pub info: Option<LiquidationState>,
#[serde(default, deserialize_with = "unsigned")]
pub executed_at: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct LiquidationPage {
#[serde(default, deserialize_with = "list")]
pub liquidations: Vec<Liquidation>,
#[serde(default)]
pub next_cursor: Option<String>,
}
#[must_use]
pub fn liquidations_call(account: AccountIndex, market: Option<MarketId>, cursor: Option<&str>, limit: u8) -> Call {
Call::get("liquidations")
.field("account_index", account)
.maybe("market_id", market)
.maybe("cursor", cursor)
.field("limit", limit.clamp(1, 100))
.authed()
}
pub async fn liquidations(
transport: &impl Transport,
account: AccountIndex,
market: Option<MarketId>,
cursor: Option<&str>,
limit: u8,
) -> Result<LiquidationPage, ApiError> {
read(transport, liquidations_call(account, market, cursor, limit)).await
}
#[cfg(test)]
#[path = "tests/liquidations.rs"]
mod tests;