df_hyperliquid_core/trade/
order.rs1use crate::trade::builder_fee::BuilderFee;
5use crate::utils::sign::{L1Action, Map, Value};
6use crate::utils::{Address, Asset, AssetKind, Dec};
7
8pub use crate::markets::Side;
10
11pub use crate::account::order::{Cloid, Tif, Tpsl};
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
17pub enum Grouping {
18 #[default]
20 Na,
21 NormalTpsl,
23 PositionTpsl,
25}
26
27impl Grouping {
28 #[must_use]
29 pub const fn wire(self) -> &'static str {
30 match self {
31 Grouping::Na => "na",
32 Grouping::NormalTpsl => "normalTpsl",
33 Grouping::PositionTpsl => "positionTpsl",
34 }
35 }
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub enum OrderType {
41 Limit(Tif),
42 Trigger {
45 is_market: bool,
46 trigger: Dec,
47 tpsl: Tpsl,
48 },
49}
50
51impl OrderType {
52 fn wire(self) -> Map {
53 match self {
54 OrderType::Limit(tif) => Map::new().put("limit", Map::new().put("tif", tif.wire())),
55 OrderType::Trigger { is_market, trigger, tpsl } => Map::new().put(
56 "trigger",
57 Map::new().put("isMarket", is_market).put("triggerPx", trigger.to_wire()).put("tpsl", tpsl.wire()),
58 ),
59 }
60 }
61}
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66pub struct Order {
67 asset: Asset,
68 side: Side,
69 price: Dec,
70 size: Dec,
71 reduce_only: bool,
72 order_type: OrderType,
73 cloid: Option<Cloid>,
74}
75
76impl Order {
77 #[must_use]
79 pub const fn limit(asset: Asset, side: Side, price: Dec, size: Dec, tif: Tif) -> Order {
80 Order { asset, side, price, size, reduce_only: false, order_type: OrderType::Limit(tif), cloid: None }
81 }
82
83 #[must_use]
88 pub const fn market(asset: Asset, side: Side, bound: Dec, size: Dec) -> Order {
89 Order::limit(asset, side, bound, size, Tif::FrontendMarket)
90 }
91
92 #[must_use]
96 pub const fn trigger(
97 asset: Asset,
98 side: Side,
99 price: Dec,
100 size: Dec,
101 trigger: Dec,
102 tpsl: Tpsl,
103 is_market: bool,
104 ) -> Order {
105 Order {
106 asset,
107 side,
108 price,
109 size,
110 reduce_only: false,
111 order_type: OrderType::Trigger { is_market, trigger, tpsl },
112 cloid: None,
113 }
114 }
115
116 #[must_use]
118 pub const fn reduce_only(mut self) -> Order {
119 self.reduce_only = true;
120 self
121 }
122
123 #[must_use]
124 pub const fn with_cloid(mut self, cloid: Cloid) -> Order {
125 self.cloid = Some(cloid);
126 self
127 }
128
129 #[must_use]
130 pub const fn asset(&self) -> Asset {
131 self.asset
132 }
133
134 #[must_use]
135 pub const fn side(&self) -> Side {
136 self.side
137 }
138
139 #[must_use]
140 pub const fn price(&self) -> Dec {
141 self.price
142 }
143
144 #[must_use]
145 pub const fn size(&self) -> Dec {
146 self.size
147 }
148
149 #[must_use]
150 pub const fn is_reduce_only(&self) -> bool {
151 self.reduce_only
152 }
153
154 #[must_use]
155 pub const fn order_type(&self) -> OrderType {
156 self.order_type
157 }
158
159 #[must_use]
160 pub const fn cloid(&self) -> Option<Cloid> {
161 self.cloid
162 }
163
164 #[must_use]
166 pub const fn tif(&self) -> Option<Tif> {
167 match self.order_type {
168 OrderType::Limit(tif) => Some(tif),
169 OrderType::Trigger { .. } => None,
170 }
171 }
172
173 #[must_use]
174 pub const fn is_market(&self) -> bool {
175 matches!(self.order_type, OrderType::Limit(Tif::FrontendMarket))
176 }
177
178 #[must_use]
179 pub const fn is_trigger(&self) -> bool {
180 matches!(self.order_type, OrderType::Trigger { .. })
181 }
182
183 #[must_use]
185 pub fn wire(&self) -> Map {
186 Map::new()
187 .put("a", self.asset)
188 .put("b", self.side.is_buy())
189 .put("p", self.price.to_wire())
190 .put("s", self.size.to_wire())
191 .put("r", self.reduce_only)
192 .put("t", self.order_type.wire())
193 .put_some("c", self.cloid.map(Cloid::to_hex))
194 }
195}
196
197#[derive(Debug, Clone, Copy, PartialEq, Eq)]
200pub struct Builder {
201 address: Address,
202 fee: BuilderFee,
203}
204
205impl Builder {
206 #[must_use]
209 pub const fn new(address: Address, fee: BuilderFee) -> Builder {
210 Builder { address, fee }
211 }
212
213 #[must_use]
214 pub const fn address(&self) -> Address {
215 self.address
216 }
217
218 #[must_use]
219 pub const fn fee(&self) -> BuilderFee {
220 self.fee
221 }
222
223 #[must_use]
224 pub fn wire(&self) -> Map {
225 Map::new().put("b", self.address.to_hex()).put("f", self.fee.tenths())
226 }
227}
228
229#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
231#[error("the builder fee of {} is over the {} {} order allows ({})", .fee.tenths(), .cap.tenths(), .kind.one(), .cap)]
232pub struct OverCap {
233 pub fee: BuilderFee,
234 pub cap: BuilderFee,
235 pub kind: AssetKind,
236}
237
238#[derive(Debug, Clone, PartialEq, Eq)]
240pub struct PlaceOrder {
241 orders: Vec<Order>,
242 grouping: Grouping,
243 builder: Option<Builder>,
244}
245
246impl PlaceOrder {
247 #[must_use]
249 pub fn new(orders: Vec<Order>) -> PlaceOrder {
250 PlaceOrder { orders, grouping: Grouping::Na, builder: None }
251 }
252
253 #[must_use]
254 pub fn one(order: Order) -> PlaceOrder {
255 PlaceOrder::new(vec![order])
256 }
257
258 #[must_use]
259 pub fn grouped(mut self, grouping: Grouping) -> PlaceOrder {
260 self.grouping = grouping;
261 self
262 }
263
264 #[must_use]
265 pub fn through(mut self, builder: Builder) -> PlaceOrder {
266 self.builder = Some(builder);
267 self
268 }
269
270 #[must_use]
271 pub fn orders(&self) -> &[Order] {
272 &self.orders
273 }
274
275 #[must_use]
276 pub const fn grouping(&self) -> Grouping {
277 self.grouping
278 }
279
280 #[must_use]
281 pub const fn builder(&self) -> Option<Builder> {
282 self.builder
283 }
284
285 #[must_use]
288 pub fn over_builder_cap(&self) -> Option<OverCap> {
289 let fee = self.builder?.fee;
290 let kind = AssetKind::strictest(self.orders.iter().map(Order::asset))?;
291 let cap = kind.builder_cap();
292 (fee > cap).then_some(OverCap { fee, cap, kind })
293 }
294}
295
296impl L1Action for PlaceOrder {
297 fn action(&self) -> Map {
298 Map::new()
299 .put("type", "order")
300 .put("orders", Value::array(self.orders.iter().map(Order::wire)))
301 .put("grouping", self.grouping.wire())
302 .put_some("builder", self.builder.as_ref().map(Builder::wire))
303 }
304}
305
306#[cfg(test)]
307#[path = "tests/order.rs"]
308mod tests;