1use std::collections::HashMap;
28
29use chrono::{DateTime, Utc};
30use nautilus_core::serialization::deserialize_empty_string_as_none;
31use nautilus_model::enums::OrderSide;
32use rust_decimal::Decimal;
33use serde::{Deserialize, Serialize};
34use serde_with::{DisplayFromStr, serde_as};
35use ustr::Ustr;
36
37use crate::common::enums::{
38 DydxCandleResolution, DydxConditionType, DydxFillType, DydxLiquidity, DydxMarketStatus,
39 DydxOrderExecution, DydxOrderStatus, DydxOrderType, DydxPositionSide, DydxPositionStatus,
40 DydxTickerType, DydxTimeInForce, DydxTradeType, DydxTransferType,
41};
42
43#[derive(Debug, Clone, Serialize, Deserialize)]
45pub struct MarketsResponse {
46 pub markets: HashMap<String, PerpetualMarket>,
48}
49
50#[serde_as]
52#[derive(Debug, Clone, Serialize, Deserialize)]
53#[serde(rename_all = "camelCase")]
54pub struct PerpetualMarket {
55 #[serde_as(as = "DisplayFromStr")]
57 pub clob_pair_id: u32,
58 pub ticker: Ustr,
60 pub status: DydxMarketStatus,
62 #[serde(default)]
64 pub base_asset: Option<Ustr>,
65 #[serde(default)]
67 pub quote_asset: Option<Ustr>,
68 #[serde_as(as = "DisplayFromStr")]
70 pub step_size: Decimal,
71 #[serde_as(as = "DisplayFromStr")]
73 pub tick_size: Decimal,
74 #[serde(default)]
76 #[serde_as(as = "Option<DisplayFromStr>")]
77 pub index_price: Option<Decimal>,
78 #[serde(default)]
80 #[serde_as(as = "Option<DisplayFromStr>")]
81 pub oracle_price: Option<Decimal>,
82 #[serde(rename = "priceChange24H")]
84 #[serde_as(as = "DisplayFromStr")]
85 pub price_change_24h: Decimal,
86 #[serde_as(as = "DisplayFromStr")]
88 pub next_funding_rate: Decimal,
89 #[serde(default)]
91 pub next_funding_at: Option<DateTime<Utc>>,
92 #[serde(default)]
94 #[serde_as(as = "Option<DisplayFromStr>")]
95 pub min_order_size: Option<Decimal>,
96 #[serde(rename = "type", default)]
98 pub market_type: Option<DydxTickerType>,
99 #[serde_as(as = "DisplayFromStr")]
101 pub initial_margin_fraction: Decimal,
102 #[serde_as(as = "DisplayFromStr")]
104 pub maintenance_margin_fraction: Decimal,
105 #[serde(default)]
107 #[serde_as(as = "Option<DisplayFromStr>")]
108 pub base_position_notional: Option<Decimal>,
109 #[serde(default)]
111 #[serde_as(as = "Option<DisplayFromStr>")]
112 pub incremental_position_size: Option<Decimal>,
113 #[serde(default)]
115 #[serde_as(as = "Option<DisplayFromStr>")]
116 pub incremental_initial_margin_fraction: Option<Decimal>,
117 #[serde(default)]
119 #[serde_as(as = "Option<DisplayFromStr>")]
120 pub max_position_size: Option<Decimal>,
121 #[serde_as(as = "DisplayFromStr")]
123 pub open_interest: Decimal,
124 pub atomic_resolution: i32,
126 pub quantum_conversion_exponent: i32,
128 pub subticks_per_tick: u32,
130 pub step_base_quantums: u64,
132 #[serde(default)]
134 pub is_reduce_only: bool,
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize)]
139pub struct OrderbookResponse {
140 pub bids: Vec<OrderbookLevel>,
142 pub asks: Vec<OrderbookLevel>,
144}
145
146#[serde_as]
148#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct OrderbookLevel {
150 #[serde_as(as = "DisplayFromStr")]
152 pub price: Decimal,
153 #[serde_as(as = "DisplayFromStr")]
155 pub size: Decimal,
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize)]
160pub struct TradesResponse {
161 pub trades: Vec<Trade>,
163}
164
165#[serde_as]
167#[derive(Debug, Clone, Serialize, Deserialize)]
168#[serde(rename_all = "camelCase")]
169pub struct Trade {
170 pub id: String,
172 pub side: OrderSide,
174 #[serde_as(as = "DisplayFromStr")]
176 pub size: Decimal,
177 #[serde_as(as = "DisplayFromStr")]
179 pub price: Decimal,
180 pub created_at: DateTime<Utc>,
182 #[serde_as(as = "DisplayFromStr")]
184 pub created_at_height: u64,
185 #[serde(rename = "type")]
187 pub trade_type: DydxTradeType,
188}
189
190#[derive(Debug, Clone, Serialize, Deserialize)]
192pub struct CandlesResponse {
193 pub candles: Vec<Candle>,
195}
196
197#[serde_as]
199#[derive(Debug, Clone, Serialize, Deserialize)]
200#[serde(rename_all = "camelCase")]
201pub struct Candle {
202 pub started_at: DateTime<Utc>,
204 pub ticker: Ustr,
206 pub resolution: DydxCandleResolution,
208 #[serde_as(as = "DisplayFromStr")]
210 pub open: Decimal,
211 #[serde_as(as = "DisplayFromStr")]
213 pub high: Decimal,
214 #[serde_as(as = "DisplayFromStr")]
216 pub low: Decimal,
217 #[serde_as(as = "DisplayFromStr")]
219 pub close: Decimal,
220 #[serde_as(as = "DisplayFromStr")]
222 pub base_token_volume: Decimal,
223 #[serde_as(as = "DisplayFromStr")]
225 pub usd_volume: Decimal,
226 pub trades: u64,
228 #[serde_as(as = "DisplayFromStr")]
230 pub starting_open_interest: Decimal,
231}
232
233#[derive(Debug, Clone, Serialize, Deserialize)]
235pub struct SubaccountResponse {
236 pub subaccount: Subaccount,
238}
239
240#[serde_as]
242#[derive(Debug, Clone, Serialize, Deserialize)]
243#[serde(rename_all = "camelCase")]
244pub struct Subaccount {
245 pub address: String,
247 pub subaccount_number: u32,
249 #[serde_as(as = "DisplayFromStr")]
251 pub equity: Decimal,
252 #[serde_as(as = "DisplayFromStr")]
254 pub free_collateral: Decimal,
255 #[serde(default)]
257 pub open_perpetual_positions: HashMap<String, PerpetualPosition>,
258 #[serde(default)]
260 pub asset_positions: HashMap<String, AssetPosition>,
261 #[serde(default)]
263 pub margin_enabled: bool,
264 #[serde_as(as = "DisplayFromStr")]
266 pub updated_at_height: u64,
267 #[serde(default)]
269 #[serde_as(as = "Option<DisplayFromStr>")]
270 pub latest_processed_block_height: Option<u64>,
271}
272
273#[serde_as]
275#[derive(Debug, Clone, Serialize, Deserialize)]
276#[serde(rename_all = "camelCase")]
277pub struct PerpetualPosition {
278 pub market: Ustr,
280 pub status: DydxPositionStatus,
282 pub side: DydxPositionSide,
284 #[serde_as(as = "DisplayFromStr")]
286 pub size: Decimal,
287 #[serde_as(as = "DisplayFromStr")]
289 pub max_size: Decimal,
290 #[serde_as(as = "DisplayFromStr")]
292 pub entry_price: Decimal,
293 #[serde_as(as = "Option<DisplayFromStr>")]
295 #[serde(skip_serializing_if = "Option::is_none")]
296 pub exit_price: Option<Decimal>,
297 #[serde_as(as = "DisplayFromStr")]
299 pub realized_pnl: Decimal,
300 #[serde_as(as = "DisplayFromStr")]
302 pub created_at_height: u64,
303 pub created_at: DateTime<Utc>,
305 #[serde_as(as = "DisplayFromStr")]
307 pub sum_open: Decimal,
308 #[serde_as(as = "DisplayFromStr")]
310 pub sum_close: Decimal,
311 #[serde_as(as = "DisplayFromStr")]
313 pub net_funding: Decimal,
314 #[serde_as(as = "DisplayFromStr")]
316 pub unrealized_pnl: Decimal,
317 #[serde(skip_serializing_if = "Option::is_none")]
319 pub closed_at: Option<DateTime<Utc>>,
320}
321
322#[serde_as]
324#[derive(Debug, Clone, Serialize, Deserialize)]
325#[serde(rename_all = "camelCase")]
326pub struct AssetPosition {
327 pub symbol: Ustr,
329 pub side: DydxPositionSide,
331 #[serde_as(as = "DisplayFromStr")]
333 pub size: Decimal,
334 pub asset_id: String,
336 #[serde(default)]
338 pub subaccount_number: u32,
339}
340
341pub type OrdersResponse = Vec<Order>;
343
344#[serde_as]
346#[derive(Debug, Clone, Serialize, Deserialize)]
347#[serde(rename_all = "camelCase")]
348pub struct Order {
349 pub id: String,
351 pub subaccount_id: String,
353 pub client_id: String,
355 #[serde_as(as = "DisplayFromStr")]
357 pub clob_pair_id: u32,
358 pub side: OrderSide,
360 #[serde_as(as = "DisplayFromStr")]
362 pub size: Decimal,
363 #[serde_as(as = "DisplayFromStr")]
365 pub total_filled: Decimal,
366 #[serde_as(as = "DisplayFromStr")]
368 pub price: Decimal,
369 pub status: DydxOrderStatus,
371 #[serde(rename = "type")]
373 pub order_type: DydxOrderType,
374 pub time_in_force: DydxTimeInForce,
376 pub reduce_only: bool,
378 pub post_only: bool,
380 #[serde_as(as = "DisplayFromStr")]
382 pub order_flags: u32,
383 #[serde_as(as = "Option<DisplayFromStr>")]
385 #[serde(skip_serializing_if = "Option::is_none")]
386 pub good_til_block: Option<u64>,
387 #[serde(skip_serializing_if = "Option::is_none")]
389 pub good_til_block_time: Option<DateTime<Utc>>,
390 #[serde_as(as = "Option<DisplayFromStr>")]
392 #[serde(skip_serializing_if = "Option::is_none")]
393 pub created_at_height: Option<u64>,
394 #[serde_as(as = "DisplayFromStr")]
396 pub client_metadata: u32,
397 #[serde_as(as = "Option<DisplayFromStr>")]
399 #[serde(skip_serializing_if = "Option::is_none")]
400 pub trigger_price: Option<Decimal>,
401 #[serde(skip_serializing_if = "Option::is_none")]
403 pub condition_type: Option<DydxConditionType>,
404 #[serde_as(as = "Option<DisplayFromStr>")]
406 #[serde(skip_serializing_if = "Option::is_none")]
407 pub conditional_order_trigger_subticks: Option<u64>,
408 #[serde(skip_serializing_if = "Option::is_none")]
410 pub execution: Option<DydxOrderExecution>,
411 #[serde(skip_serializing_if = "Option::is_none")]
413 pub updated_at: Option<DateTime<Utc>>,
414 #[serde_as(as = "Option<DisplayFromStr>")]
416 #[serde(skip_serializing_if = "Option::is_none")]
417 pub updated_at_height: Option<u64>,
418 #[serde(skip_serializing_if = "Option::is_none")]
420 pub ticker: Option<Ustr>,
421 #[serde(default)]
423 pub subaccount_number: u32,
424 #[serde(default, deserialize_with = "deserialize_empty_string_as_none")]
426 #[serde(skip_serializing_if = "Option::is_none")]
427 pub order_router_address: Option<String>,
428}
429
430#[derive(Debug, Clone, Serialize, Deserialize)]
432#[serde(rename_all = "camelCase")]
433pub struct FillsResponse {
434 pub fills: Vec<Fill>,
436}
437
438#[serde_as]
440#[derive(Debug, Clone, Serialize, Deserialize)]
441#[serde(rename_all = "camelCase")]
442pub struct Fill {
443 pub id: String,
445 pub side: OrderSide,
447 pub liquidity: DydxLiquidity,
449 #[serde(rename = "type")]
451 pub fill_type: DydxFillType,
452 pub market: Ustr,
454 pub market_type: DydxTickerType,
456 #[serde_as(as = "DisplayFromStr")]
458 pub price: Decimal,
459 #[serde_as(as = "DisplayFromStr")]
461 pub size: Decimal,
462 #[serde_as(as = "DisplayFromStr")]
464 pub fee: Decimal,
465 pub created_at: DateTime<Utc>,
467 #[serde_as(as = "DisplayFromStr")]
469 pub created_at_height: u64,
470 pub order_id: String,
472 #[serde_as(as = "DisplayFromStr")]
474 pub client_metadata: u32,
475}
476
477#[derive(Debug, Clone, Serialize, Deserialize)]
479pub struct TransfersResponse {
480 pub transfers: Vec<Transfer>,
482}
483
484#[serde_as]
486#[derive(Debug, Clone, Serialize, Deserialize)]
487#[serde(rename_all = "camelCase")]
488pub struct Transfer {
489 pub id: String,
491 #[serde(rename = "type")]
493 pub transfer_type: DydxTransferType,
494 pub sender: TransferAccount,
496 pub recipient: TransferAccount,
498 pub asset: String,
500 #[serde_as(as = "DisplayFromStr")]
502 pub amount: Decimal,
503 pub created_at: DateTime<Utc>,
505 #[serde_as(as = "DisplayFromStr")]
507 pub created_at_height: u64,
508 pub transaction_hash: String,
510}
511
512#[derive(Debug, Clone, Serialize, Deserialize)]
514#[serde(rename_all = "camelCase")]
515pub struct TransferAccount {
516 pub address: String,
518 pub subaccount_number: u32,
520}
521
522#[derive(Debug, Clone, Serialize, Deserialize)]
524#[serde(rename_all = "camelCase")]
525pub struct HistoricalFundingResponse {
526 pub historical_funding: Vec<HistoricalFunding>,
528}
529
530#[serde_as]
532#[derive(Debug, Clone, Serialize, Deserialize)]
533#[serde(rename_all = "camelCase")]
534pub struct HistoricalFunding {
535 pub ticker: Ustr,
537 #[serde_as(as = "DisplayFromStr")]
539 pub rate: Decimal,
540 #[serde_as(as = "DisplayFromStr")]
542 pub price: Decimal,
543 #[serde_as(as = "DisplayFromStr")]
545 pub effective_at_height: u64,
546 pub effective_at: DateTime<Utc>,
548}
549
550#[derive(Debug, Clone, Serialize, Deserialize)]
552pub struct TimeResponse {
553 pub iso: DateTime<Utc>,
555 #[serde(rename = "epoch")]
557 pub epoch_ms: i64,
558}
559
560#[serde_as]
562#[derive(Debug, Clone, Serialize, Deserialize)]
563pub struct HeightResponse {
564 #[serde_as(as = "DisplayFromStr")]
566 pub height: u64,
567 pub time: DateTime<Utc>,
569}
570
571#[serde_as]
573#[derive(Debug, Clone, Serialize, Deserialize)]
574#[serde(rename_all = "camelCase")]
575pub struct PlaceOrderRequest {
576 pub subaccount: SubaccountId,
578 pub client_id: u32,
580 pub order_flags: String,
582 pub clob_pair_id: u32,
584 pub side: OrderSide,
586 pub quantums: u64,
588 pub subticks: u64,
590 pub time_in_force: DydxTimeInForce,
592 #[serde(skip_serializing_if = "Option::is_none")]
594 pub good_til_block: Option<u32>,
595 #[serde(skip_serializing_if = "Option::is_none")]
597 pub good_til_block_time: Option<u32>,
598 pub reduce_only: bool,
600 #[serde(skip_serializing_if = "Option::is_none")]
602 pub authenticator_ids: Option<Vec<u64>>,
603}
604
605#[derive(Debug, Clone, Serialize, Deserialize)]
607pub struct SubaccountId {
608 pub owner: String,
610 pub number: u32,
612}
613
614#[derive(Debug, Clone, Serialize, Deserialize)]
616#[serde(rename_all = "camelCase")]
617pub struct CancelOrderRequest {
618 pub subaccount_id: SubaccountId,
620 pub client_id: u32,
622 pub clob_pair_id: u32,
624 pub order_flags: String,
626 pub good_til_block: Option<u32>,
628 pub good_til_block_time: Option<u32>,
629}
630
631#[serde_as]
633#[derive(Debug, Clone, Serialize, Deserialize)]
634pub struct TransactionResponse {
635 pub tx_hash: String,
637 #[serde_as(as = "DisplayFromStr")]
639 pub height: u64,
640 pub code: u32,
642 pub raw_log: String,
644}