Skip to main content

df_hyperliquid_core/account/
orders.rs

1//! An account's orders as the venue reads them back, on every market kind:
2//! `openOrders`, `frontendOpenOrders`, `orderStatus` and `historicalOrders`.
3//! Figures are exact decimals; sides are `B` (a buy) and `A` (a sell).
4
5use crate::client::request::{body, dex, number, text, user};
6use crate::client::{ApiError, Transport, info};
7use crate::trade::modify::OrderRef;
8use crate::utils::Address;
9use serde::{Deserialize, Deserializer, de};
10
11/// The order rows `/info` answers and the socket sends.
12pub use crate::account::order::{FrontendOrder, OpenOrder, OrderKind, OrderRecord, OrderState};
13
14/// `orderStatus`: the order, or `unknownOid` for one the venue never took.
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub enum OrderLookup {
17    Found(Box<OrderRecord>),
18    Unknown,
19}
20
21impl<'de> Deserialize<'de> for OrderLookup {
22    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
23        #[derive(Deserialize)]
24        struct Raw {
25            status: String,
26            #[serde(default)]
27            order: Option<OrderRecord>,
28        }
29        let raw = Raw::deserialize(deserializer)?;
30        match (raw.status.as_str(), raw.order) {
31            ("order", Some(order)) => Ok(OrderLookup::Found(Box::new(order))),
32            ("unknownOid", _) => Ok(OrderLookup::Unknown),
33            (other, _) => Err(de::Error::custom(format!("orderStatus answered `{other}`"))),
34        }
35    }
36}
37
38/// `{"type":"openOrders","user":..,"dex"?}`. Spot orders come with the main
39/// dex only.
40#[must_use]
41pub fn open_orders_request(account: Address, on: Option<&str>) -> String {
42    body("openOrders", [user(account)].into_iter().chain(dex(on)))
43}
44
45/// `{"type":"frontendOpenOrders","user":..,"dex"?}`.
46#[must_use]
47pub fn frontend_open_orders_request(account: Address, on: Option<&str>) -> String {
48    body("frontendOpenOrders", [user(account)].into_iter().chain(dex(on)))
49}
50
51/// `{"type":"orderStatus","user":..,"oid":..}`: the venue's id as a number,
52/// or the client's as its hex.
53#[must_use]
54pub fn order_status_request(account: Address, target: OrderRef) -> String {
55    let oid = match target {
56        OrderRef::Oid(oid) => number(oid),
57        OrderRef::Cloid(cloid) => text(&cloid.to_hex()),
58    };
59    body("orderStatus", [user(account), ("oid", oid)])
60}
61
62/// `{"type":"historicalOrders","user":..}`: at most the 2000 most recent.
63#[must_use]
64pub fn historical_orders_request(account: Address) -> String {
65    body("historicalOrders", [user(account)])
66}
67
68/// The account's resting orders on one dex: the main dex when `on` is `None`.
69pub async fn open_orders(
70    transport: &impl Transport,
71    account: Address,
72    on: Option<&str>,
73) -> Result<Vec<OpenOrder>, ApiError> {
74    info(transport, open_orders_request(account, on)).await
75}
76
77/// The account's resting orders, with what kind each is.
78pub async fn frontend_open_orders(
79    transport: &impl Transport,
80    account: Address,
81    on: Option<&str>,
82) -> Result<Vec<FrontendOrder>, ApiError> {
83    info(transport, frontend_open_orders_request(account, on)).await
84}
85
86/// One order of the account, by either id.
87pub async fn order_status(
88    transport: &impl Transport,
89    account: Address,
90    target: OrderRef,
91) -> Result<OrderLookup, ApiError> {
92    info(transport, order_status_request(account, target)).await
93}
94
95/// The account's order history, newest first.
96pub async fn historical_orders(transport: &impl Transport, account: Address) -> Result<Vec<OrderRecord>, ApiError> {
97    info(transport, historical_orders_request(account)).await
98}
99
100#[cfg(test)]
101#[path = "tests/orders.rs"]
102mod tests;