1use std::collections::HashMap;
28
29use chrono::{DateTime, Utc};
30use nautilus_model::enums::OrderSide;
31use rust_decimal::Decimal;
32use serde::{Deserialize, Serialize};
33
34fn deserialize_empty_string_as_none<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
36where
37 D: serde::Deserializer<'de>,
38{
39 let s: Option<String> = Option::deserialize(deserializer)?;
40 Ok(s.filter(|s| !s.is_empty()))
41}
42use serde_with::{DisplayFromStr, serde_as};
43use ustr::Ustr;
44
45use crate::common::enums::{
46 DydxCandleResolution, DydxConditionType, DydxFillType, DydxLiquidity, DydxMarketStatus,
47 DydxOrderExecution, DydxOrderStatus, DydxOrderType, DydxPositionSide, DydxPositionStatus,
48 DydxTickerType, DydxTimeInForce, DydxTradeType, DydxTransferType,
49};
50
51#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct MarketsResponse {
58 pub markets: HashMap<String, PerpetualMarket>,
60}
61
62#[serde_as]
64#[derive(Debug, Clone, Serialize, Deserialize)]
65#[serde(rename_all = "camelCase")]
66pub struct PerpetualMarket {
67 #[serde_as(as = "DisplayFromStr")]
69 pub clob_pair_id: u32,
70 pub ticker: String,
72 pub status: DydxMarketStatus,
74 #[serde(default)]
76 pub base_asset: Option<String>,
77 #[serde(default)]
79 pub quote_asset: Option<String>,
80 #[serde_as(as = "DisplayFromStr")]
82 pub step_size: Decimal,
83 #[serde_as(as = "DisplayFromStr")]
85 pub tick_size: Decimal,
86 #[serde(default)]
88 #[serde_as(as = "Option<DisplayFromStr>")]
89 pub index_price: Option<Decimal>,
90 #[serde_as(as = "DisplayFromStr")]
92 pub oracle_price: Decimal,
93 #[serde(rename = "priceChange24H")]
95 #[serde_as(as = "DisplayFromStr")]
96 pub price_change_24h: Decimal,
97 #[serde_as(as = "DisplayFromStr")]
99 pub next_funding_rate: Decimal,
100 #[serde(default)]
102 pub next_funding_at: Option<DateTime<Utc>>,
103 #[serde(default)]
105 #[serde_as(as = "Option<DisplayFromStr>")]
106 pub min_order_size: Option<Decimal>,
107 #[serde(rename = "type", default)]
109 pub market_type: Option<DydxTickerType>,
110 #[serde_as(as = "DisplayFromStr")]
112 pub initial_margin_fraction: Decimal,
113 #[serde_as(as = "DisplayFromStr")]
115 pub maintenance_margin_fraction: Decimal,
116 #[serde(default)]
118 #[serde_as(as = "Option<DisplayFromStr>")]
119 pub base_position_notional: Option<Decimal>,
120 #[serde(default)]
122 #[serde_as(as = "Option<DisplayFromStr>")]
123 pub incremental_position_size: Option<Decimal>,
124 #[serde(default)]
126 #[serde_as(as = "Option<DisplayFromStr>")]
127 pub incremental_initial_margin_fraction: Option<Decimal>,
128 #[serde(default)]
130 #[serde_as(as = "Option<DisplayFromStr>")]
131 pub max_position_size: Option<Decimal>,
132 #[serde_as(as = "DisplayFromStr")]
134 pub open_interest: Decimal,
135 pub atomic_resolution: i32,
137 pub quantum_conversion_exponent: i32,
139 pub subticks_per_tick: u32,
141 pub step_base_quantums: u64,
143 #[serde(default)]
145 pub is_reduce_only: bool,
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct OrderbookResponse {
151 pub bids: Vec<OrderbookLevel>,
153 pub asks: Vec<OrderbookLevel>,
155}
156
157#[serde_as]
159#[derive(Debug, Clone, Serialize, Deserialize)]
160pub struct OrderbookLevel {
161 #[serde_as(as = "DisplayFromStr")]
163 pub price: Decimal,
164 #[serde_as(as = "DisplayFromStr")]
166 pub size: Decimal,
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize)]
171pub struct TradesResponse {
172 pub trades: Vec<Trade>,
174}
175
176#[serde_as]
178#[derive(Debug, Clone, Serialize, Deserialize)]
179#[serde(rename_all = "camelCase")]
180pub struct Trade {
181 pub id: String,
183 pub side: OrderSide,
185 #[serde_as(as = "DisplayFromStr")]
187 pub size: Decimal,
188 #[serde_as(as = "DisplayFromStr")]
190 pub price: Decimal,
191 pub created_at: DateTime<Utc>,
193 #[serde_as(as = "DisplayFromStr")]
195 pub created_at_height: u64,
196 #[serde(rename = "type")]
198 pub trade_type: DydxTradeType,
199}
200
201#[derive(Debug, Clone, Serialize, Deserialize)]
203pub struct CandlesResponse {
204 pub candles: Vec<Candle>,
206}
207
208#[serde_as]
210#[derive(Debug, Clone, Serialize, Deserialize)]
211#[serde(rename_all = "camelCase")]
212pub struct Candle {
213 pub started_at: DateTime<Utc>,
215 pub ticker: String,
217 pub resolution: DydxCandleResolution,
219 #[serde_as(as = "DisplayFromStr")]
221 pub open: Decimal,
222 #[serde_as(as = "DisplayFromStr")]
224 pub high: Decimal,
225 #[serde_as(as = "DisplayFromStr")]
227 pub low: Decimal,
228 #[serde_as(as = "DisplayFromStr")]
230 pub close: Decimal,
231 #[serde_as(as = "DisplayFromStr")]
233 pub base_token_volume: Decimal,
234 #[serde_as(as = "DisplayFromStr")]
236 pub usd_volume: Decimal,
237 pub trades: u64,
239 #[serde_as(as = "DisplayFromStr")]
241 pub starting_open_interest: Decimal,
242}
243
244#[derive(Debug, Clone, Serialize, Deserialize)]
250pub struct SubaccountResponse {
251 pub subaccount: Subaccount,
253}
254
255#[serde_as]
257#[derive(Debug, Clone, Serialize, Deserialize)]
258#[serde(rename_all = "camelCase")]
259pub struct Subaccount {
260 pub address: String,
262 pub subaccount_number: u32,
264 #[serde_as(as = "DisplayFromStr")]
266 pub equity: Decimal,
267 #[serde_as(as = "DisplayFromStr")]
269 pub free_collateral: Decimal,
270 #[serde(default)]
272 pub open_perpetual_positions: HashMap<String, PerpetualPosition>,
273 #[serde(default)]
275 pub asset_positions: HashMap<String, AssetPosition>,
276 #[serde(default)]
278 pub margin_enabled: bool,
279 #[serde_as(as = "DisplayFromStr")]
281 pub updated_at_height: u64,
282 #[serde(default)]
284 #[serde_as(as = "Option<DisplayFromStr>")]
285 pub latest_processed_block_height: Option<u64>,
286}
287
288#[serde_as]
290#[derive(Debug, Clone, Serialize, Deserialize)]
291#[serde(rename_all = "camelCase")]
292pub struct PerpetualPosition {
293 pub market: String,
295 pub status: DydxPositionStatus,
297 pub side: OrderSide,
299 #[serde_as(as = "DisplayFromStr")]
301 pub size: Decimal,
302 #[serde_as(as = "DisplayFromStr")]
304 pub max_size: Decimal,
305 #[serde_as(as = "DisplayFromStr")]
307 pub entry_price: Decimal,
308 #[serde_as(as = "Option<DisplayFromStr>")]
310 #[serde(skip_serializing_if = "Option::is_none")]
311 pub exit_price: Option<Decimal>,
312 #[serde_as(as = "DisplayFromStr")]
314 pub realized_pnl: Decimal,
315 #[serde_as(as = "DisplayFromStr")]
317 pub created_at_height: u64,
318 pub created_at: DateTime<Utc>,
320 #[serde_as(as = "DisplayFromStr")]
322 pub sum_open: Decimal,
323 #[serde_as(as = "DisplayFromStr")]
325 pub sum_close: Decimal,
326 #[serde_as(as = "DisplayFromStr")]
328 pub net_funding: Decimal,
329 #[serde_as(as = "DisplayFromStr")]
331 pub unrealized_pnl: Decimal,
332 #[serde(skip_serializing_if = "Option::is_none")]
334 pub closed_at: Option<DateTime<Utc>>,
335}
336
337#[serde_as]
339#[derive(Debug, Clone, Serialize, Deserialize)]
340#[serde(rename_all = "camelCase")]
341pub struct AssetPosition {
342 pub symbol: Ustr,
344 pub side: DydxPositionSide,
346 #[serde_as(as = "DisplayFromStr")]
348 pub size: Decimal,
349 pub asset_id: String,
351 #[serde(default)]
353 pub subaccount_number: u32,
354}
355
356pub type OrdersResponse = Vec<Order>;
358
359#[serde_as]
361#[derive(Debug, Clone, Serialize, Deserialize)]
362#[serde(rename_all = "camelCase")]
363pub struct Order {
364 pub id: String,
366 pub subaccount_id: String,
368 pub client_id: String,
370 #[serde_as(as = "DisplayFromStr")]
372 pub clob_pair_id: u32,
373 pub side: OrderSide,
375 #[serde_as(as = "DisplayFromStr")]
377 pub size: Decimal,
378 #[serde_as(as = "DisplayFromStr")]
380 pub total_filled: Decimal,
381 #[serde_as(as = "DisplayFromStr")]
383 pub price: Decimal,
384 pub status: DydxOrderStatus,
386 #[serde(rename = "type")]
388 pub order_type: DydxOrderType,
389 pub time_in_force: DydxTimeInForce,
391 pub reduce_only: bool,
393 pub post_only: bool,
395 #[serde_as(as = "DisplayFromStr")]
397 pub order_flags: u32,
398 #[serde_as(as = "Option<DisplayFromStr>")]
400 #[serde(skip_serializing_if = "Option::is_none")]
401 pub good_til_block: Option<u64>,
402 #[serde(skip_serializing_if = "Option::is_none")]
404 pub good_til_block_time: Option<DateTime<Utc>>,
405 #[serde_as(as = "Option<DisplayFromStr>")]
407 #[serde(skip_serializing_if = "Option::is_none")]
408 pub created_at_height: Option<u64>,
409 #[serde_as(as = "DisplayFromStr")]
411 pub client_metadata: u32,
412 #[serde_as(as = "Option<DisplayFromStr>")]
414 #[serde(skip_serializing_if = "Option::is_none")]
415 pub trigger_price: Option<Decimal>,
416 #[serde(skip_serializing_if = "Option::is_none")]
418 pub condition_type: Option<DydxConditionType>,
419 #[serde_as(as = "Option<DisplayFromStr>")]
421 #[serde(skip_serializing_if = "Option::is_none")]
422 pub conditional_order_trigger_subticks: Option<u64>,
423 #[serde(skip_serializing_if = "Option::is_none")]
425 pub execution: Option<DydxOrderExecution>,
426 #[serde(skip_serializing_if = "Option::is_none")]
428 pub updated_at: Option<DateTime<Utc>>,
429 #[serde_as(as = "Option<DisplayFromStr>")]
431 #[serde(skip_serializing_if = "Option::is_none")]
432 pub updated_at_height: Option<u64>,
433 #[serde(skip_serializing_if = "Option::is_none")]
435 pub ticker: Option<String>,
436 #[serde(default)]
438 pub subaccount_number: u32,
439 #[serde(default, deserialize_with = "deserialize_empty_string_as_none")]
441 #[serde(skip_serializing_if = "Option::is_none")]
442 pub order_router_address: Option<String>,
443}
444
445#[derive(Debug, Clone, Serialize, Deserialize)]
447#[serde(rename_all = "camelCase")]
448pub struct FillsResponse {
449 pub fills: Vec<Fill>,
451}
452
453#[serde_as]
455#[derive(Debug, Clone, Serialize, Deserialize)]
456#[serde(rename_all = "camelCase")]
457pub struct Fill {
458 pub id: String,
460 pub side: OrderSide,
462 pub liquidity: DydxLiquidity,
464 #[serde(rename = "type")]
466 pub fill_type: DydxFillType,
467 pub market: String,
469 pub market_type: DydxTickerType,
471 #[serde_as(as = "DisplayFromStr")]
473 pub price: Decimal,
474 #[serde_as(as = "DisplayFromStr")]
476 pub size: Decimal,
477 #[serde_as(as = "DisplayFromStr")]
479 pub fee: Decimal,
480 pub created_at: DateTime<Utc>,
482 #[serde_as(as = "DisplayFromStr")]
484 pub created_at_height: u64,
485 pub order_id: String,
487 #[serde_as(as = "DisplayFromStr")]
489 pub client_metadata: u32,
490}
491
492#[derive(Debug, Clone, Serialize, Deserialize)]
494pub struct TransfersResponse {
495 pub transfers: Vec<Transfer>,
497}
498
499#[serde_as]
501#[derive(Debug, Clone, Serialize, Deserialize)]
502#[serde(rename_all = "camelCase")]
503pub struct Transfer {
504 pub id: String,
506 #[serde(rename = "type")]
508 pub transfer_type: DydxTransferType,
509 pub sender: TransferAccount,
511 pub recipient: TransferAccount,
513 pub asset: String,
515 #[serde_as(as = "DisplayFromStr")]
517 pub amount: Decimal,
518 pub created_at: DateTime<Utc>,
520 #[serde_as(as = "DisplayFromStr")]
522 pub created_at_height: u64,
523 pub transaction_hash: String,
525}
526
527#[derive(Debug, Clone, Serialize, Deserialize)]
529#[serde(rename_all = "camelCase")]
530pub struct TransferAccount {
531 pub address: String,
533 pub subaccount_number: u32,
535}
536
537#[derive(Debug, Clone, Serialize, Deserialize)]
543pub struct TimeResponse {
544 pub iso: DateTime<Utc>,
546 #[serde(rename = "epoch")]
548 pub epoch_ms: i64,
549}
550
551#[serde_as]
553#[derive(Debug, Clone, Serialize, Deserialize)]
554pub struct HeightResponse {
555 #[serde_as(as = "DisplayFromStr")]
557 pub height: u64,
558 pub time: DateTime<Utc>,
560}
561
562#[serde_as]
568#[derive(Debug, Clone, Serialize, Deserialize)]
569#[serde(rename_all = "camelCase")]
570pub struct PlaceOrderRequest {
571 pub subaccount: SubaccountId,
573 pub client_id: u32,
575 pub order_flags: String,
577 pub clob_pair_id: u32,
579 pub side: OrderSide,
581 pub quantums: u64,
583 pub subticks: u64,
585 pub time_in_force: DydxTimeInForce,
587 #[serde(skip_serializing_if = "Option::is_none")]
589 pub good_til_block: Option<u32>,
590 #[serde(skip_serializing_if = "Option::is_none")]
592 pub good_til_block_time: Option<u32>,
593 pub reduce_only: bool,
595 #[serde(skip_serializing_if = "Option::is_none")]
597 pub authenticator_ids: Option<Vec<u64>>,
598}
599
600#[derive(Debug, Clone, Serialize, Deserialize)]
602pub struct SubaccountId {
603 pub owner: String,
605 pub number: u32,
607}
608
609#[derive(Debug, Clone, Serialize, Deserialize)]
611#[serde(rename_all = "camelCase")]
612pub struct CancelOrderRequest {
613 pub subaccount_id: SubaccountId,
615 pub client_id: u32,
617 pub clob_pair_id: u32,
619 pub order_flags: String,
621 pub good_til_block: Option<u32>,
623 pub good_til_block_time: Option<u32>,
624}
625
626#[serde_as]
628#[derive(Debug, Clone, Serialize, Deserialize)]
629pub struct TransactionResponse {
630 pub tx_hash: String,
632 #[serde_as(as = "DisplayFromStr")]
634 pub height: u64,
635 pub code: u32,
637 pub raw_log: String,
639}