df_hyperliquid_core/account/
order.rs1use std::{borrow::Cow, fmt, str::FromStr};
6
7use serde::{Deserialize, Deserializer, de};
8
9use crate::markets::side::Side;
10use crate::utils::dec::Dec;
11use crate::utils::hex::{self, BadHex};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15pub enum Tif {
16 Alo,
18 Ioc,
20 Gtc,
22 FrontendMarket,
25}
26
27impl Tif {
28 #[must_use]
29 pub const fn wire(self) -> &'static str {
30 match self {
31 Tif::Alo => "Alo",
32 Tif::Ioc => "Ioc",
33 Tif::Gtc => "Gtc",
34 Tif::FrontendMarket => "FrontendMarket",
35 }
36 }
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
41pub enum Tpsl {
42 TakeProfit,
44 StopLoss,
46}
47
48impl Tpsl {
49 #[must_use]
50 pub const fn wire(self) -> &'static str {
51 match self {
52 Tpsl::TakeProfit => "tp",
53 Tpsl::StopLoss => "sl",
54 }
55 }
56}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
60pub struct Cloid([u8; 16]);
61
62impl Cloid {
63 #[must_use]
64 pub const fn from_bytes(bytes: [u8; 16]) -> Cloid {
65 Cloid(bytes)
66 }
67
68 #[must_use]
69 pub const fn from_u128(value: u128) -> Cloid {
70 Cloid(value.to_be_bytes())
71 }
72
73 #[must_use]
74 pub const fn bytes(&self) -> &[u8; 16] {
75 &self.0
76 }
77
78 #[must_use]
79 pub fn to_hex(self) -> String {
80 hex::encode_prefixed(&self.0)
81 }
82}
83
84impl FromStr for Cloid {
85 type Err = BadHex;
86
87 fn from_str(text: &str) -> Result<Self, Self::Err> {
88 hex::decode_array(text).map(Cloid)
89 }
90}
91
92impl fmt::Display for Cloid {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 f.write_str(&self.to_hex())
95 }
96}
97
98impl<'de> Deserialize<'de> for Cloid {
100 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
101 <Cow<'de, str>>::deserialize(deserializer)?.parse().map_err(de::Error::custom)
102 }
103}
104
105#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
108#[serde(rename_all = "camelCase")]
109pub struct OpenOrder {
110 pub coin: String,
112 pub side: Side,
114 pub limit_px: Dec,
115 pub sz: Dec,
117 pub oid: u64,
118 pub timestamp: u64,
120 pub orig_sz: Dec,
121 #[serde(default)]
122 pub reduce_only: bool,
123 #[serde(default)]
124 pub cloid: Option<Cloid>,
125}
126
127#[derive(Debug, Clone, PartialEq, Eq)]
129pub enum OrderKind {
130 Limit,
131 Market,
132 StopMarket,
133 StopLimit,
134 TakeProfitMarket,
135 TakeProfitLimit,
136 Other(String),
138}
139
140impl OrderKind {
141 #[must_use]
144 pub fn parse(word: &str) -> OrderKind {
145 match word {
146 "Limit" => OrderKind::Limit,
147 "Market" => OrderKind::Market,
148 "Stop Market" => OrderKind::StopMarket,
149 "Stop Limit" => OrderKind::StopLimit,
150 "Take Profit Market" => OrderKind::TakeProfitMarket,
151 "Take Profit Limit" => OrderKind::TakeProfitLimit,
152 other => OrderKind::Other(other.to_owned()),
153 }
154 }
155
156 #[must_use]
158 pub fn word(&self) -> &str {
159 match self {
160 OrderKind::Limit => "Limit",
161 OrderKind::Market => "Market",
162 OrderKind::StopMarket => "Stop Market",
163 OrderKind::StopLimit => "Stop Limit",
164 OrderKind::TakeProfitMarket => "Take Profit Market",
165 OrderKind::TakeProfitLimit => "Take Profit Limit",
166 OrderKind::Other(word) => word,
167 }
168 }
169
170 #[must_use]
172 pub const fn tpsl(&self) -> Option<Tpsl> {
173 match self {
174 OrderKind::StopMarket | OrderKind::StopLimit => Some(Tpsl::StopLoss),
175 OrderKind::TakeProfitMarket | OrderKind::TakeProfitLimit => Some(Tpsl::TakeProfit),
176 _ => None,
177 }
178 }
179
180 #[must_use]
182 pub const fn fills_at_market(&self) -> bool {
183 matches!(self, OrderKind::Market | OrderKind::StopMarket | OrderKind::TakeProfitMarket)
184 }
185}
186
187impl<'de> Deserialize<'de> for OrderKind {
188 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
189 Ok(OrderKind::parse(&<Cow<'de, str>>::deserialize(deserializer)?))
190 }
191}
192
193#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
197#[serde(rename_all = "camelCase")]
198pub struct FrontendOrder {
199 pub coin: String,
200 pub side: Side,
201 pub limit_px: Dec,
202 pub sz: Dec,
204 pub oid: u64,
205 pub timestamp: u64,
207 pub trigger_condition: String,
209 pub is_trigger: bool,
210 pub trigger_px: Dec,
212 #[serde(default)]
214 pub children: Vec<FrontendOrder>,
215 pub is_position_tpsl: bool,
216 pub reduce_only: bool,
217 pub order_type: OrderKind,
218 pub orig_sz: Dec,
219 #[serde(default)]
222 pub tif: Option<String>,
223 #[serde(default)]
224 pub cloid: Option<Cloid>,
225}
226
227impl FrontendOrder {
228 #[must_use]
230 pub fn trigger(&self) -> Option<Dec> {
231 (self.is_trigger && self.trigger_px.is_positive()).then_some(self.trigger_px)
232 }
233
234 #[must_use]
235 pub const fn tpsl(&self) -> Option<Tpsl> {
236 self.order_type.tpsl()
237 }
238
239 #[must_use]
241 pub fn time_in_force(&self) -> Option<Tif> {
242 match self.tif.as_deref()? {
243 "Gtc" => Some(Tif::Gtc),
244 "Alo" => Some(Tif::Alo),
245 "Ioc" => Some(Tif::Ioc),
246 "FrontendMarket" => Some(Tif::FrontendMarket),
247 _ => None,
248 }
249 }
250}
251
252#[derive(Debug, Clone, PartialEq, Eq)]
256pub enum OrderState {
257 Open,
258 Filled,
259 Triggered,
260 Canceled(String),
261 Rejected(String),
262 Other(String),
264}
265
266impl OrderState {
267 #[must_use]
269 pub fn word(&self) -> &str {
270 match self {
271 OrderState::Open => "open",
272 OrderState::Filled => "filled",
273 OrderState::Triggered => "triggered",
274 OrderState::Canceled(word) | OrderState::Rejected(word) | OrderState::Other(word) => word,
275 }
276 }
277}
278
279impl<'de> Deserialize<'de> for OrderState {
280 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
281 let word = String::deserialize(deserializer)?;
282 Ok(match word.as_str() {
283 "open" => OrderState::Open,
284 "filled" => OrderState::Filled,
285 "triggered" => OrderState::Triggered,
286 "canceled" | "scheduledCancel" => OrderState::Canceled(word),
287 canceled if canceled.ends_with("Canceled") => OrderState::Canceled(word),
288 rejected if rejected == "rejected" || rejected.ends_with("Rejected") => OrderState::Rejected(word),
289 _ => OrderState::Other(word),
290 })
291 }
292}
293
294impl fmt::Display for OrderState {
295 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
296 f.write_str(self.word())
297 }
298}
299
300#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
303#[serde(rename_all = "camelCase")]
304pub struct OrderRecord {
305 pub order: FrontendOrder,
306 pub status: OrderState,
307 pub status_timestamp: u64,
309}