1use crate::node::OrderMarketParams;
2use anyhow::{anyhow as err, Error};
3use bigdecimal::BigDecimal;
4use chrono::{DateTime, Utc};
5use cosmrs::{AccountId, Denom as CosmosDenom};
6use derive_more::{Add, Deref, DerefMut, Display, Div, From, Mul, Sub};
7use dydx_proto::dydxprotocol::subaccounts::SubaccountId as ProtoSubaccountId;
8use rand::{rng, Rng};
9use serde::{Deserialize, Deserializer, Serialize};
10use serde_with::{serde_as, DisplayFromStr};
11use std::collections::HashMap;
12use std::convert::TryFrom;
13use std::{fmt, str::FromStr};
14
15#[derive(Deserialize, Debug, Clone, Eq, Hash, PartialOrd, Ord, PartialEq)]
19#[serde(rename_all = "camelCase")]
20#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
21pub struct AccountWithParentSubaccountNumber {
22 pub address: Address,
24 pub parent_subaccount_number: Option<ParentSubaccountNumber>,
26}
27
28#[derive(Deserialize, Debug, Clone, Eq, Hash, PartialOrd, Ord, PartialEq)]
30#[serde(rename_all = "camelCase")]
31#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
32pub struct Account {
33 pub address: Address,
35 pub subaccount_number: Option<SubaccountNumber>,
37}
38
39#[derive(
41 Default,
42 Serialize,
43 Deserialize,
44 Debug,
45 Clone,
46 From,
47 Display,
48 PartialEq,
49 Eq,
50 PartialOrd,
51 Ord,
52 Hash,
53)]
54pub struct Address(String);
55
56impl FromStr for Address {
57 type Err = Error;
58 fn from_str(value: &str) -> Result<Self, Error> {
59 Ok(Self(
60 value.parse::<AccountId>().map_err(Error::msg)?.to_string(),
61 ))
62 }
63}
64
65impl AsRef<str> for Address {
66 fn as_ref(&self) -> &str {
67 &self.0
68 }
69}
70
71impl From<Address> for String {
72 fn from(address: Address) -> Self {
73 address.0
74 }
75}
76
77#[derive(Deserialize, Debug, Clone)]
79#[serde(rename_all = "camelCase", untagged)]
80pub enum ApiOrderStatus {
81 OrderStatus(OrderStatus),
83 BestEffort(BestEffortOpenedStatus),
85}
86
87#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
89#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
90pub enum ApiTimeInForce {
91 Gtt,
95 Fok,
99 Ioc,
103}
104
105#[derive(
107 Serialize, Deserialize, Debug, Clone, From, Display, PartialEq, Eq, PartialOrd, Ord, Hash,
108)]
109pub struct AssetId(pub String);
110
111#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
113#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
114pub enum BestEffortOpenedStatus {
115 BestEffortOpened,
117}
118
119#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
121pub enum CandleResolution {
122 #[serde(rename = "1MIN")]
124 M1,
125 #[serde(rename = "5MINS")]
127 M5,
128 #[serde(rename = "15MINS")]
130 M15,
131 #[serde(rename = "30MINS")]
133 M30,
134 #[serde(rename = "1HOUR")]
136 H1,
137 #[serde(rename = "4HOURS")]
139 H4,
140 #[serde(rename = "1DAY")]
142 D1,
143}
144
145#[derive(Clone, Debug)]
147pub struct AnyId;
148
149#[serde_as]
155#[derive(Deserialize, Debug, Clone, Display, PartialEq, Eq, PartialOrd, Ord, Hash)]
156pub struct ClientId(#[serde_as(as = "DisplayFromStr")] pub u32);
157
158impl ClientId {
159 pub fn new(id: u32) -> Self {
161 ClientId(id)
162 }
163
164 pub fn random() -> Self {
166 ClientId(rng().random())
167 }
168
169 pub fn random_with_rng<R: Rng>(rng: &mut R) -> Self {
171 ClientId(rng.random())
172 }
173}
174
175impl From<u32> for ClientId {
176 fn from(value: u32) -> Self {
177 Self(value)
178 }
179}
180
181impl From<AnyId> for ClientId {
182 fn from(_: AnyId) -> Self {
183 Self::random()
184 }
185}
186
187#[serde_as]
189#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
190pub struct ClobPairId(#[serde_as(as = "DisplayFromStr")] pub u32);
191
192impl From<u32> for ClobPairId {
193 fn from(value: u32) -> Self {
194 Self(value)
195 }
196}
197
198impl From<&u32> for ClobPairId {
199 fn from(value: &u32) -> Self {
200 ClobPairId::from(*value)
201 }
202}
203
204#[serde_as]
206#[derive(Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
207pub struct ClientMetadata(#[serde_as(as = "DisplayFromStr")] pub u32);
208
209impl From<u32> for ClientMetadata {
210 fn from(value: u32) -> Self {
211 Self(value)
212 }
213}
214
215#[derive(Deserialize, Debug, Clone, From, Display, PartialEq, Eq, PartialOrd, Ord, Hash)]
217pub struct FillId(pub String);
218
219#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
221#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
222pub enum FillType {
223 Limit,
225 Liquidated,
229 Liquidation,
231 Deleveraged,
235 Offsetting,
239}
240
241#[serde_as]
243#[derive(
244 Serialize, Deserialize, Debug, Clone, From, Display, PartialEq, Eq, PartialOrd, Ord, Hash,
245)]
246pub struct Height(#[serde_as(as = "DisplayFromStr")] pub u32);
247
248impl Height {
249 pub fn ahead(&self, n: u32) -> Height {
251 Height(self.0 + n)
252 }
253}
254
255#[derive(Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
259#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
260pub enum Liquidity {
261 Taker,
263 Maker,
265}
266
267#[derive(Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
269#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
270pub enum PerpetualMarketStatus {
271 Active,
273 Paused,
275 CancelOnly,
277 PostOnly,
279 Initializing,
281 FinalSettlement,
283}
284
285#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
287#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
288pub enum PerpetualPositionStatus {
289 Open,
291 Closed,
293 Liquidated,
295}
296
297#[derive(Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
301#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
302pub enum PositionSide {
303 Long,
305 Short,
307}
308
309#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
311#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
312pub enum MarketType {
313 Perpetual,
315 Spot,
317}
318
319#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
321#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
322pub enum PerpetualMarketType {
323 Cross,
325 Isolated,
327}
328
329#[derive(Deserialize, Debug, Clone, From, Display, PartialEq, Eq, PartialOrd, Ord, Hash)]
331pub struct OrderId(pub String);
332
333#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
335#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
336pub enum OrderStatus {
337 Open,
339 Filled,
341 Canceled,
343 BestEffortCanceled,
345 Untriggered,
347}
348
349#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
353#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
354pub enum OrderExecution {
355 Default,
359 Ioc,
363 Fok,
367 PostOnly,
370}
371
372#[derive(Clone, Debug, Deserialize)]
374pub enum OrderFlags {
375 #[serde(rename = "0")]
377 ShortTerm = 0,
378 #[serde(rename = "32")]
380 Conditional = 32,
381 #[serde(rename = "64")]
383 LongTerm = 64,
384}
385
386#[derive(
389 Serialize, Deserialize, Debug, Clone, From, Display, PartialEq, Eq, PartialOrd, Ord, Hash,
390)]
391pub struct TradeId(pub String);
392
393#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
395#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
396pub enum OrderSide {
397 Buy,
399 Sell,
401}
402
403#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
407#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
408pub enum OrderType {
409 Limit,
411 Market,
413 StopLimit,
415 StopMarket,
417 TrailingStop,
419 TakeProfit,
421 TakeProfitMarket,
423 HardTrade,
425 FailedHardTrade,
427 TransferPlaceholder,
429}
430
431#[derive(Deserialize, Debug, Clone, Eq, Hash, PartialOrd, Ord, PartialEq)]
433#[serde(rename_all = "camelCase")]
434#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
435pub struct Subaccount {
436 pub address: Address,
438 pub number: SubaccountNumber,
440}
441
442impl Subaccount {
443 pub fn new(address: Address, number: SubaccountNumber) -> Self {
445 Self { address, number }
446 }
447
448 pub fn parent(&self) -> ParentSubaccount {
450 let number = ParentSubaccountNumber(self.number.0 % 128);
451 ParentSubaccount::new(self.address.clone(), number)
452 }
453
454 pub fn is_parent(&self) -> bool {
456 self.number.0 < 128
457 }
458}
459
460impl From<Subaccount> for ProtoSubaccountId {
461 fn from(subacc: Subaccount) -> Self {
462 ProtoSubaccountId {
463 owner: subacc.address.0,
464 number: subacc.number.0,
465 }
466 }
467}
468
469#[derive(Serialize, Debug, Clone, Display, PartialEq, Eq, PartialOrd, Ord, Hash)]
471pub struct SubaccountNumber(pub(crate) u32);
472
473impl<'de> Deserialize<'de> for SubaccountNumber {
474 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
475 where
476 D: Deserializer<'de>,
477 {
478 struct SubaccountVisitor;
479
480 impl<'de> serde::de::Visitor<'de> for SubaccountVisitor {
481 type Value = SubaccountNumber;
482
483 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
484 formatter.write_str("a u32 or a string containing a u32")
485 }
486
487 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
488 where
489 E: serde::de::Error,
490 {
491 Ok(SubaccountNumber(value as u32))
492 }
493
494 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
495 where
496 E: serde::de::Error,
497 {
498 value
499 .parse::<u32>()
500 .map(SubaccountNumber)
501 .map_err(|_| E::custom(format!("invalid u32 in string: {value}")))
502 }
503 }
504
505 deserializer.deserialize_any(SubaccountVisitor)
506 }
507}
508
509impl SubaccountNumber {
510 pub fn value(&self) -> u32 {
512 self.0
513 }
514}
515
516impl TryFrom<u32> for SubaccountNumber {
517 type Error = Error;
518 fn try_from(number: u32) -> Result<Self, Error> {
519 match number {
520 0..=128_000 => Ok(SubaccountNumber(number)),
521 _ => Err(err!("Subaccount number must be [0, 128_000]")),
522 }
523 }
524}
525
526impl TryFrom<&u32> for SubaccountNumber {
527 type Error = Error;
528 fn try_from(number: &u32) -> Result<Self, Error> {
529 Self::try_from(*number)
530 }
531}
532
533impl TryFrom<String> for SubaccountNumber {
534 type Error = Error;
535 fn try_from(number: String) -> Result<Self, Error> {
536 Self::try_from(number.parse::<u32>()?)
537 }
538}
539
540impl TryFrom<&str> for SubaccountNumber {
541 type Error = Error;
542 fn try_from(number: &str) -> Result<Self, Error> {
543 Self::try_from(number.parse::<u32>()?)
544 }
545}
546
547impl From<ParentSubaccountNumber> for SubaccountNumber {
548 fn from(parent: ParentSubaccountNumber) -> Self {
549 Self(parent.value())
550 }
551}
552
553#[derive(Deserialize, Debug, Clone, Eq, Hash, PartialOrd, Ord, PartialEq)]
558#[serde(rename_all = "camelCase")]
559#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
560pub struct ParentSubaccount {
561 pub address: Address,
563 pub number: ParentSubaccountNumber,
565}
566
567impl ParentSubaccount {
568 pub fn new(address: Address, number: ParentSubaccountNumber) -> Self {
570 Self { address, number }
571 }
572}
573
574impl std::cmp::PartialEq<Subaccount> for ParentSubaccount {
575 fn eq(&self, other: &Subaccount) -> bool {
576 self.address == other.address && self.number == other.number
577 }
578}
579
580#[derive(Serialize, Deserialize, Debug, Clone, Display, PartialEq, Eq, PartialOrd, Ord, Hash)]
582pub struct ParentSubaccountNumber(u32);
583
584impl ParentSubaccountNumber {
585 pub fn value(&self) -> u32 {
587 self.0
588 }
589}
590
591impl TryFrom<u32> for ParentSubaccountNumber {
592 type Error = Error;
593 fn try_from(number: u32) -> Result<Self, Error> {
594 match number {
595 0..=127 => Ok(ParentSubaccountNumber(number)),
596 _ => Err(err!("Parent subaccount number must be [0, 127]")),
597 }
598 }
599}
600
601impl std::cmp::PartialEq<SubaccountNumber> for ParentSubaccountNumber {
602 fn eq(&self, other: &SubaccountNumber) -> bool {
603 self.0 == other.value()
604 }
605}
606
607#[derive(Deserialize, Debug, Clone, From, Display, PartialEq, Eq, PartialOrd, Ord, Hash)]
609pub struct SubaccountId(pub String);
610
611#[derive(
613 Serialize, Deserialize, Debug, Clone, From, Display, PartialEq, Eq, PartialOrd, Ord, Hash,
614)]
615pub struct Symbol(pub String);
616
617#[derive(Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
619#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
620pub enum TradeType {
621 Limit,
623 Liquidated,
625 Deleveraged,
627}
628
629#[derive(Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
631#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
632pub enum TransferType {
633 TransferIn,
635 TransferOut,
637 Deposit,
639 Withdrawal,
641}
642
643#[derive(
645 Serialize, Deserialize, Debug, Clone, From, Display, PartialEq, Eq, PartialOrd, Ord, Hash,
646)]
647pub struct Ticker(pub String);
648
649impl<'a> From<&'a str> for Ticker {
650 fn from(value: &'a str) -> Self {
651 Self(value.into())
652 }
653}
654
655const USDC_DENOM: &str = "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5";
656const DYDX_DENOM: &str = "adydx";
657const DYDX_TNT_DENOM: &str = "adv4tnt";
658#[cfg(feature = "noble")]
659const NOBLE_USDC_DENOM: &str = "uusdc";
660
661#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
665pub enum Denom {
666 #[serde(rename = "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5")]
668 Usdc,
669 #[serde(rename = "adydx")]
671 Dydx,
672 #[serde(rename = "adv4tnt")]
674 DydxTnt,
675 #[cfg(feature = "noble")]
677 #[serde(rename = "uusdc")]
678 NobleUsdc,
679 #[serde(untagged)]
681 Custom(CosmosDenom),
682}
683
684impl Denom {
685 pub fn gas_price(&self) -> Option<BigDecimal> {
688 match self {
689 Self::Usdc => Some(BigDecimal::new(25.into(), 3)),
692 Self::Dydx | Self::DydxTnt => Some(BigDecimal::new(25_000_000_000u64.into(), 0)),
695 #[cfg(feature = "noble")]
696 Self::NobleUsdc => Some(BigDecimal::new(1.into(), 1)),
697 _ => None,
698 }
699 }
700}
701
702impl FromStr for Denom {
703 type Err = Error;
704 fn from_str(value: &str) -> Result<Self, Error> {
705 match value {
706 USDC_DENOM => Ok(Self::Usdc),
707 DYDX_DENOM => Ok(Self::Dydx),
708 DYDX_TNT_DENOM => Ok(Self::DydxTnt),
709 _ => Ok(Self::Custom(
710 value.parse::<CosmosDenom>().map_err(Error::msg)?,
711 )),
712 }
713 }
714}
715
716impl AsRef<str> for Denom {
717 fn as_ref(&self) -> &str {
718 match self {
719 Self::Usdc => USDC_DENOM,
720 Self::Dydx => DYDX_DENOM,
721 Self::DydxTnt => DYDX_TNT_DENOM,
722 #[cfg(feature = "noble")]
723 Self::NobleUsdc => NOBLE_USDC_DENOM,
724 Self::Custom(denom) => denom.as_ref(),
725 }
726 }
727}
728
729impl fmt::Display for Denom {
730 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
731 f.write_str(self.as_ref())
732 }
733}
734
735impl TryFrom<Denom> for CosmosDenom {
736 type Error = Error;
737 fn try_from(value: Denom) -> Result<Self, Self::Error> {
738 value.as_ref().parse().map_err(Self::Error::msg)
739 }
740}
741
742#[derive(Deserialize, Debug, Clone)]
744#[serde(rename_all = "camelCase")]
745#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
746pub struct ParentSubaccountResponseObject {
747 pub address: Address,
749 pub parent_subaccount_number: SubaccountNumber,
751 pub equity: BigDecimal,
753 pub free_collateral: BigDecimal,
755 pub child_subaccounts: Vec<SubaccountResponseObject>,
757}
758
759#[derive(Deserialize, Debug, Clone)]
761#[serde(rename_all = "camelCase")]
762#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
763pub struct SubaccountResponseObject {
764 pub address: Address,
766 pub subaccount_number: SubaccountNumber,
768 pub equity: BigDecimal,
770 pub free_collateral: BigDecimal,
772 pub open_perpetual_positions: PerpetualPositionsMap,
774 pub asset_positions: AssetPositionsMap,
776 pub margin_enabled: bool,
778 pub updated_at_height: Height,
780 pub latest_processed_block_height: Height,
782}
783
784#[derive(Deserialize, Debug, Clone)]
786#[serde(rename_all = "camelCase")]
787#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
788pub struct AssetPositionResponseObject {
789 pub symbol: Symbol,
791 pub side: PositionSide,
793 pub size: Quantity,
795 pub subaccount_number: SubaccountNumber,
797 pub asset_id: AssetId,
799}
800
801#[derive(Deserialize, Debug, Clone)]
803#[serde(rename_all = "camelCase")]
804#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
805pub struct PerpetualPositionResponseObject {
806 pub market: Ticker,
808 pub status: PerpetualPositionStatus,
810 pub side: PositionSide,
812 pub size: Quantity,
814 pub max_size: Quantity,
816 pub entry_price: Price,
818 pub realized_pnl: BigDecimal,
820 pub created_at: DateTime<Utc>,
822 pub created_at_height: Height,
824 pub sum_open: BigDecimal,
826 pub sum_close: BigDecimal,
828 pub net_funding: BigDecimal,
830 pub unrealized_pnl: BigDecimal,
832 pub closed_at: Option<DateTime<Utc>>,
834 pub exit_price: Option<Price>,
836 pub subaccount_number: SubaccountNumber,
838}
839
840pub type AssetPositionsMap = HashMap<Ticker, AssetPositionResponseObject>;
842
843pub type PerpetualPositionsMap = HashMap<Ticker, PerpetualPositionResponseObject>;
845
846#[derive(
848 Add,
849 Deserialize,
850 Debug,
851 Clone,
852 Div,
853 Display,
854 Deref,
855 DerefMut,
856 PartialEq,
857 Eq,
858 Mul,
859 PartialOrd,
860 Ord,
861 Hash,
862 Sub,
863)]
864#[serde(transparent)]
865pub struct Price(pub BigDecimal);
866
867impl<T> From<T> for Price
868where
869 T: Into<BigDecimal>,
870{
871 fn from(value: T) -> Self {
872 Self(value.into())
873 }
874}
875
876impl FromStr for Price {
877 type Err = bigdecimal::ParseBigDecimalError;
878 fn from_str(s: &str) -> Result<Self, Self::Err> {
879 s.parse().map(Self)
880 }
881}
882
883#[derive(
885 Add,
886 Deserialize,
887 Debug,
888 Clone,
889 Div,
890 Display,
891 Deref,
892 DerefMut,
893 PartialEq,
894 Eq,
895 Mul,
896 PartialOrd,
897 Ord,
898 Hash,
899 Sub,
900)]
901#[serde(transparent)]
902pub struct Quantity(pub BigDecimal);
903
904impl<T> From<T> for Quantity
905where
906 T: Into<BigDecimal>,
907{
908 fn from(value: T) -> Self {
909 Self(value.into())
910 }
911}
912
913impl FromStr for Quantity {
914 type Err = bigdecimal::ParseBigDecimalError;
915 fn from_str(s: &str) -> Result<Self, Self::Err> {
916 s.parse().map(Self)
917 }
918}
919
920#[derive(Deserialize, Debug, Clone)]
922#[serde(rename_all = "camelCase")]
923#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
924pub struct OrderbookResponsePriceLevel {
925 pub price: Price,
927 pub size: Quantity,
929}
930
931#[derive(Deserialize, Debug, Clone)]
933#[serde(rename_all = "camelCase")]
934#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
935pub struct OrderBookResponseObject {
936 pub bids: Vec<OrderbookResponsePriceLevel>,
938 pub asks: Vec<OrderbookResponsePriceLevel>,
940}
941
942#[derive(Deserialize, Debug, Clone)]
944#[serde(rename_all = "camelCase")]
945#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
946pub struct OrderResponseObject {
947 pub client_id: ClientId,
949 pub client_metadata: ClientMetadata,
951 pub clob_pair_id: ClobPairId,
953 pub created_at_height: Option<Height>,
955 pub good_til_block: Option<Height>,
957 pub good_til_block_time: Option<DateTime<Utc>>,
959 pub id: OrderId,
961 pub order_flags: OrderFlags,
963 pub post_only: bool,
965 pub price: Price,
967 pub reduce_only: bool,
969 pub side: OrderSide,
971 pub size: Quantity,
973 pub status: ApiOrderStatus,
975 pub subaccount_id: SubaccountId,
977 pub subaccount_number: SubaccountNumber,
979 pub ticker: Ticker,
981 pub time_in_force: ApiTimeInForce,
983 pub total_filled: BigDecimal,
985 #[serde(rename = "type")]
987 pub order_type: OrderType,
988 pub updated_at: Option<DateTime<Utc>>,
990 pub updated_at_height: Option<Height>,
992 pub trigger_price: Option<Price>,
994 pub fee_ppm: Option<BigDecimal>,
996 pub builder_address: Option<Address>,
998 pub order_router_address: Option<Address>,
1000 pub duration: Option<BigDecimal>,
1002 pub interval: Option<BigDecimal>,
1004 pub price_tolerance: Option<BigDecimal>,
1006}
1007
1008#[derive(Deserialize, Debug, Clone)]
1010#[serde(rename_all = "camelCase")]
1011#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
1012pub struct TradeResponse {
1013 pub trades: Vec<TradeResponseObject>,
1015}
1016
1017#[derive(Deserialize, Debug, Clone)]
1019#[serde(rename_all = "camelCase")]
1020#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
1021pub struct TradeResponseObject {
1022 pub id: TradeId,
1024 pub created_at_height: Height,
1026 pub created_at: DateTime<Utc>,
1028 pub side: OrderSide,
1030 pub price: Price,
1032 pub size: Quantity,
1034 #[serde(rename = "type")]
1036 pub trade_type: TradeType,
1037}
1038
1039#[derive(Deserialize, Debug, Clone)]
1041#[serde(rename_all = "camelCase")]
1042#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
1043pub struct PerpetualMarketResponse {
1044 pub markets: HashMap<Ticker, PerpetualMarket>,
1046}
1047
1048#[derive(Deserialize, Debug, Clone)]
1050#[serde(rename_all = "camelCase")]
1051#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
1052pub struct PerpetualMarket {
1053 pub clob_pair_id: ClobPairId,
1055 pub ticker: Ticker,
1057 pub status: PerpetualMarketStatus,
1059 pub oracle_price: Option<Price>,
1061 #[serde(rename = "priceChange24H")]
1063 pub price_change_24h: BigDecimal,
1064 #[serde(rename = "volume24H")]
1066 pub volume_24h: Quantity,
1067 #[serde(rename = "trades24H")]
1069 pub trades_24h: u64,
1070 pub next_funding_rate: BigDecimal,
1072 pub initial_margin_fraction: BigDecimal,
1074 pub maintenance_margin_fraction: BigDecimal,
1076 pub open_interest: BigDecimal,
1078 pub atomic_resolution: i32,
1080 pub quantum_conversion_exponent: i32,
1082 pub tick_size: BigDecimal,
1084 pub step_size: BigDecimal,
1086 pub step_base_quantums: u64,
1088 pub subticks_per_tick: u32,
1090 pub market_type: PerpetualMarketType,
1092 pub open_interest_lower_cap: Option<BigDecimal>,
1094 pub open_interest_upper_cap: Option<BigDecimal>,
1096 pub base_open_interest: BigDecimal,
1098 #[serde(rename = "defaultFundingRate1H")]
1100 pub default_funding_rate_1h: Option<BigDecimal>,
1101}
1102
1103impl PerpetualMarket {
1104 pub fn order_params(&self) -> OrderMarketParams {
1108 OrderMarketParams {
1109 atomic_resolution: self.atomic_resolution,
1110 clob_pair_id: self.clob_pair_id.clone(),
1111 oracle_price: self.oracle_price.clone(),
1112 quantum_conversion_exponent: self.quantum_conversion_exponent,
1113 step_base_quantums: self.step_base_quantums,
1114 subticks_per_tick: self.subticks_per_tick,
1115 }
1116 }
1117}
1118
1119#[derive(Deserialize, Debug, Clone)]
1121#[serde(rename_all = "camelCase")]
1122#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
1123pub struct CandleResponse {
1124 pub candles: Vec<CandleResponseObject>,
1126}
1127
1128#[derive(Deserialize, Debug, Clone)]
1130#[serde(rename_all = "camelCase")]
1131#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
1132pub struct CandleResponseObject {
1133 pub started_at: DateTime<Utc>,
1135 pub ticker: Ticker,
1137 pub resolution: CandleResolution,
1139 pub low: Price,
1141 pub high: Price,
1143 pub open: Price,
1145 pub close: Price,
1147 pub base_token_volume: Quantity,
1149 pub usd_volume: Quantity,
1151 pub trades: u64,
1153 pub starting_open_interest: BigDecimal,
1155 pub orderbook_mid_price_open: Option<Price>,
1157 pub orderbook_mid_price_close: Option<Price>,
1159}
1160
1161#[derive(Deserialize, Debug, Clone)]
1163#[serde(rename_all = "camelCase")]
1164#[cfg_attr(any(test, feature = "strict-serde"), serde(deny_unknown_fields))]
1165pub struct HeightResponse {
1166 pub height: Height,
1168 pub time: DateTime<Utc>,
1170}
1171
1172#[cfg(test)]
1173mod tests {
1174 use super::*;
1175
1176 #[test]
1177 fn denom_parse() {
1178 let _usdc = Denom::Usdc.to_string().parse::<Denom>().unwrap();
1180 let _dydx = Denom::Dydx.to_string().parse::<Denom>().unwrap();
1181 let _dydx_tnt = Denom::DydxTnt.to_string().parse::<Denom>().unwrap();
1182 let _custom: Denom = "uusdc".parse().unwrap();
1183 }
1184}