1#[cfg(feature = "auth")]
7use alloy_primitives::U256;
8use chrono::{DateTime, Utc};
9use rust_decimal::Decimal;
10use serde::{de, Deserialize, Deserializer, Serialize};
11use serde_json::Value;
12use std::str::FromStr;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
20#[serde(rename_all = "UPPERCASE")]
21pub enum Side {
22 Buy,
23 Sell,
24}
25
26impl Side {
27 #[must_use]
29 pub fn as_str(&self) -> &'static str {
30 match self {
31 Self::Buy => "BUY",
32 Self::Sell => "SELL",
33 }
34 }
35
36 #[must_use]
38 pub fn opposite(&self) -> Self {
39 match self {
40 Self::Buy => Self::Sell,
41 Self::Sell => Self::Buy,
42 }
43 }
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct BookLevel {
49 #[serde(with = "rust_decimal::serde::str")]
50 pub price: Decimal,
51 #[serde(with = "rust_decimal::serde::str")]
52 pub size: Decimal,
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize, Default)]
61pub struct ApiCredentials {
62 #[serde(rename = "apiKey")]
64 pub api_key: String,
65 pub secret: String,
67 pub passphrase: String,
69}
70
71impl ApiCredentials {
72 #[must_use]
74 pub fn new(
75 api_key: impl Into<String>,
76 secret: impl Into<String>,
77 passphrase: impl Into<String>,
78 ) -> Self {
79 Self {
80 api_key: api_key.into(),
81 secret: secret.into(),
82 passphrase: passphrase.into(),
83 }
84 }
85
86 #[must_use]
88 pub fn is_configured(&self) -> bool {
89 !self.api_key.is_empty() && !self.secret.is_empty() && !self.passphrase.is_empty()
90 }
91}
92
93#[derive(Debug, Clone, Default)]
99pub struct OrderOptions {
100 pub tick_size: Option<Decimal>,
102 pub neg_risk: Option<bool>,
104 pub fee_rate_bps: Option<u32>,
106}
107
108impl OrderOptions {
109 #[must_use]
111 pub fn new() -> Self {
112 Self::default()
113 }
114
115 #[must_use]
117 pub fn with_tick_size(mut self, tick_size: Decimal) -> Self {
118 self.tick_size = Some(tick_size);
119 self
120 }
121
122 #[must_use]
124 pub fn with_neg_risk(mut self, neg_risk: bool) -> Self {
125 self.neg_risk = Some(neg_risk);
126 self
127 }
128
129 #[must_use]
131 pub fn with_fee_rate_bps(mut self, fee_rate_bps: u32) -> Self {
132 self.fee_rate_bps = Some(fee_rate_bps);
133 self
134 }
135}
136
137#[cfg(feature = "auth")]
139#[derive(Debug, Clone)]
140pub struct ExtraOrderArgs {
141 pub fee_rate_bps: u32,
143 pub nonce: U256,
145 pub taker: String,
147}
148
149#[cfg(feature = "auth")]
150impl Default for ExtraOrderArgs {
151 fn default() -> Self {
152 Self {
153 fee_rate_bps: 0,
154 nonce: U256::ZERO,
155 taker: "0x0000000000000000000000000000000000000000".to_string(),
156 }
157 }
158}
159
160#[cfg(feature = "auth")]
161impl ExtraOrderArgs {
162 #[must_use]
164 pub fn new() -> Self {
165 Self::default()
166 }
167
168 #[must_use]
170 pub fn with_fee_rate_bps(mut self, fee_rate_bps: u32) -> Self {
171 self.fee_rate_bps = fee_rate_bps;
172 self
173 }
174
175 #[must_use]
177 pub fn with_nonce(mut self, nonce: U256) -> Self {
178 self.nonce = nonce;
179 self
180 }
181
182 #[must_use]
184 pub fn with_taker(mut self, taker: impl Into<String>) -> Self {
185 self.taker = taker.into();
186 self
187 }
188}
189
190#[derive(Debug, Clone)]
192pub struct MarketOrderArgs {
193 pub token_id: String,
195 pub amount: Decimal,
197}
198
199impl MarketOrderArgs {
200 #[must_use]
202 pub fn new(token_id: impl Into<String>, amount: Decimal) -> Self {
203 Self {
204 token_id: token_id.into(),
205 amount,
206 }
207 }
208}
209
210#[derive(Debug, Clone, Serialize, Deserialize)]
212#[serde(rename_all = "camelCase")]
213pub struct SignedOrderRequest {
214 pub salt: u64,
216 pub maker: String,
218 pub signer: String,
220 pub taker: String,
222 pub token_id: String,
224 pub maker_amount: String,
226 pub taker_amount: String,
228 pub expiration: String,
230 pub nonce: String,
232 pub fee_rate_bps: String,
234 pub side: String,
236 pub signature_type: u8,
238 pub signature: String,
240}
241
242#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
244#[serde(rename_all = "UPPERCASE")]
245pub enum OrderType {
246 GTC,
248 FOK,
250 GTD,
252 FAK,
254}
255
256impl Default for OrderType {
257 fn default() -> Self {
258 OrderType::GTC
259 }
260}
261
262#[derive(Debug, Clone, Serialize, Deserialize)]
266#[serde(rename_all = "camelCase")]
267pub struct NewOrder {
268 #[serde(default)]
270 pub defer_exec: bool,
271 pub order: NewOrderData,
273 pub owner: String,
275 pub order_type: OrderType,
277}
278
279#[derive(Debug, Clone, Serialize, Deserialize)]
283#[serde(rename_all = "camelCase")]
284pub struct NewOrderData {
285 pub salt: i64,
287 pub maker: String,
289 pub signer: String,
291 pub taker: String,
293 pub token_id: String,
295 pub maker_amount: String,
297 pub taker_amount: String,
299 pub side: String,
301 pub expiration: String,
303 pub nonce: String,
305 pub fee_rate_bps: String,
307 pub signature_type: u8,
309 pub signature: String,
311}
312
313impl NewOrder {
314 pub fn from_signed_order(
317 order: &SignedOrderRequest,
318 api_key: &str,
319 order_type: OrderType,
320 defer_exec: bool,
321 ) -> Self {
322 NewOrder {
323 defer_exec,
324 order: NewOrderData {
325 salt: order.salt as i64,
327 maker: order.maker.clone(),
328 signer: order.signer.clone(),
329 taker: order.taker.clone(),
330 token_id: order.token_id.clone(),
331 maker_amount: order.maker_amount.clone(),
332 taker_amount: order.taker_amount.clone(),
333 side: order.side.clone(),
334 expiration: order.expiration.clone(),
335 nonce: order.nonce.clone(),
336 fee_rate_bps: order.fee_rate_bps.clone(),
337 signature_type: order.signature_type,
338 signature: order.signature.clone(),
339 },
340 owner: api_key.to_string(),
341 order_type,
342 }
343 }
344}
345
346#[derive(Debug, Clone, Serialize, Deserialize)]
352pub struct Token {
353 pub token_id: String,
355 pub outcome: String,
357 #[serde(skip_serializing_if = "Option::is_none")]
359 #[serde(default, deserialize_with = "deserialize_decimal_opt")]
360 pub price: Option<Decimal>,
361}
362
363#[derive(Debug, Clone, Serialize, Deserialize)]
365#[serde(rename_all = "camelCase")]
366pub struct Market {
367 pub condition_id: String,
369 pub slug: String,
371 #[serde(default)]
373 pub question: Option<String>,
374 #[serde(default)]
376 pub description: Option<String>,
377 #[serde(default)]
379 pub category: Option<String>,
380 pub active: bool,
382 pub closed: bool,
384 #[serde(default)]
386 pub end_date: Option<String>,
387 #[serde(default)]
389 pub icon: Option<String>,
390 #[serde(default)]
392 pub clob_token_ids: Option<String>,
393 #[serde(default)]
395 pub outcomes: Option<String>,
396 #[serde(default)]
398 pub outcome_prices: Option<String>,
399 #[serde(default, deserialize_with = "deserialize_decimal_opt")]
401 pub liquidity_num: Option<Decimal>,
402 #[serde(
404 default,
405 rename = "volume24hr",
406 deserialize_with = "deserialize_decimal_opt"
407 )]
408 pub volume_24hr: Option<Decimal>,
409 #[serde(default, deserialize_with = "deserialize_decimal_opt")]
411 pub volume_num: Option<Decimal>,
412 #[serde(default, deserialize_with = "deserialize_decimal_opt")]
414 pub order_min_size: Option<Decimal>,
415 #[serde(
417 default,
418 rename = "orderPriceMinTickSize",
419 deserialize_with = "deserialize_decimal_opt"
420 )]
421 pub order_tick_size: Option<Decimal>,
422}
423
424impl Market {
425 #[must_use]
427 pub fn parse_token_ids(&self) -> Vec<String> {
428 self.clob_token_ids
429 .as_ref()
430 .and_then(|raw| serde_json::from_str(raw).ok())
431 .unwrap_or_default()
432 }
433
434 #[must_use]
436 pub fn parse_outcomes(&self) -> Vec<String> {
437 self.outcomes
438 .as_ref()
439 .and_then(|raw| serde_json::from_str(raw).ok())
440 .unwrap_or_else(|| vec!["Yes".to_string(), "No".to_string()])
441 }
442
443 #[must_use]
446 pub fn parse_outcome_prices(&self) -> (Option<f64>, Option<f64>) {
447 let prices: Vec<String> = self
448 .outcome_prices
449 .as_ref()
450 .and_then(|raw| serde_json::from_str(raw).ok())
451 .unwrap_or_default();
452
453 let yes_price = prices.first().and_then(|s| s.parse::<f64>().ok());
454 let no_price = prices.get(1).and_then(|s| s.parse::<f64>().ok());
455
456 (yes_price, no_price)
457 }
458}
459
460fn deserialize_decimal_opt<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
462where
463 D: Deserializer<'de>,
464{
465 match Value::deserialize(deserializer)? {
466 Value::Null => Ok(None),
467 Value::String(s) => {
468 if s.is_empty() {
469 Ok(None)
470 } else {
471 Decimal::from_str(&s).map(Some).map_err(de::Error::custom)
472 }
473 }
474 Value::Number(n) => Decimal::from_str(&n.to_string())
475 .map(Some)
476 .map_err(de::Error::custom),
477 other => Err(de::Error::custom(format!("expected decimal, got {other}"))),
478 }
479}
480
481#[derive(Debug, Clone, Serialize, Deserialize)]
483pub struct Event {
484 pub id: String,
486 pub slug: String,
488 #[serde(default)]
490 pub name: Option<String>,
491 #[serde(default)]
493 pub description: Option<String>,
494 #[serde(default)]
496 pub active: Option<bool>,
497 #[serde(default)]
499 pub closed: Option<bool>,
500 #[serde(default)]
502 pub start_date_iso: Option<String>,
503 #[serde(default)]
505 pub end_date_iso: Option<String>,
506 #[serde(default)]
508 pub sport: Option<String>,
509 #[serde(default)]
511 pub markets: Vec<EventMarket>,
512}
513
514#[derive(Debug, Clone, Serialize, Deserialize)]
516#[serde(rename_all = "camelCase")]
517pub struct EventMarket {
518 pub condition_id: String,
520 #[serde(default)]
522 pub market_id: Option<String>,
523 #[serde(default)]
525 pub clob_token_ids: Option<String>,
526 #[serde(default)]
528 pub slug: Option<String>,
529}
530
531#[derive(Debug, Clone, Serialize, Deserialize)]
533pub struct Tag {
534 #[serde(default)]
536 pub id: Option<String>,
537 #[serde(default)]
539 pub slug: Option<String>,
540 #[serde(default)]
542 pub name: Option<String>,
543 #[serde(default)]
545 pub description: Option<String>,
546}
547
548#[derive(Debug, Clone, Serialize, Deserialize)]
554pub struct TraderProfile {
555 pub address: String,
557 #[serde(default)]
559 pub name: Option<String>,
560 #[serde(default)]
562 pub username: Option<String>,
563 #[serde(default, rename = "profileImage")]
565 pub profile_image: Option<String>,
566 #[serde(default)]
568 pub bio: Option<String>,
569 #[serde(default)]
571 pub volume: Option<Decimal>,
572 #[serde(default, rename = "marketsTraded")]
574 pub markets_traded: Option<i32>,
575 #[serde(default)]
577 pub pnl: Option<Decimal>,
578}
579
580#[derive(Debug, Clone, Serialize, Deserialize)]
582pub struct LeaderboardEntry {
583 pub rank: i32,
585 pub address: String,
587 #[serde(default)]
589 pub name: Option<String>,
590 #[serde(default)]
592 pub username: Option<String>,
593 #[serde(default, rename = "profileImage")]
595 pub profile_image: Option<String>,
596 pub volume: Decimal,
598 #[serde(default)]
600 pub profit: Option<Decimal>,
601}
602
603#[derive(Debug, Clone, Default)]
609pub struct PaginationParams {
610 pub limit: Option<u32>,
612 pub offset: Option<u32>,
614 pub cursor: Option<String>,
616}
617
618impl PaginationParams {
619 #[must_use]
621 pub fn new() -> Self {
622 Self::default()
623 }
624
625 #[must_use]
627 pub fn with_limit(mut self, limit: u32) -> Self {
628 self.limit = Some(limit);
629 self
630 }
631
632 #[must_use]
634 pub fn with_offset(mut self, offset: u32) -> Self {
635 self.offset = Some(offset);
636 self
637 }
638
639 #[must_use]
641 pub fn with_cursor(mut self, cursor: impl Into<String>) -> Self {
642 self.cursor = Some(cursor.into());
643 self
644 }
645}
646
647#[derive(Debug, Clone, Default)]
649pub struct ListParams {
650 pub pagination: PaginationParams,
652 pub closed: Option<bool>,
654 pub active: Option<bool>,
656 pub order: Option<String>,
658 pub ascending: Option<bool>,
660}
661
662impl ListParams {
663 #[must_use]
665 pub fn new() -> Self {
666 Self::default()
667 }
668
669 #[must_use]
671 pub fn with_limit(mut self, limit: u32) -> Self {
672 self.pagination.limit = Some(limit);
673 self
674 }
675
676 #[must_use]
678 pub fn with_offset(mut self, offset: u32) -> Self {
679 self.pagination.offset = Some(offset);
680 self
681 }
682
683 #[must_use]
685 pub fn with_closed(mut self, closed: bool) -> Self {
686 self.closed = Some(closed);
687 self
688 }
689
690 #[must_use]
692 pub fn with_active(mut self, active: bool) -> Self {
693 self.active = Some(active);
694 self
695 }
696
697 #[must_use]
699 pub fn with_order(mut self, field: impl Into<String>, ascending: bool) -> Self {
700 self.order = Some(field.into());
701 self.ascending = Some(ascending);
702 self
703 }
704}
705
706#[derive(Debug, Clone, Default)]
712pub struct ConnectionStats {
713 pub messages_received: u64,
715 pub reconnect_attempts: u32,
717 pub last_message_at: Option<DateTime<Utc>>,
719 pub connected_at: Option<DateTime<Utc>>,
721}
722
723impl ConnectionStats {
724 #[must_use]
726 pub fn new() -> Self {
727 Self::default()
728 }
729
730 pub fn record_message(&mut self) {
732 self.messages_received += 1;
733 self.last_message_at = Some(Utc::now());
734 }
735
736 pub fn record_reconnect(&mut self) {
738 self.reconnect_attempts += 1;
739 }
740
741 pub fn record_connected(&mut self) {
743 self.connected_at = Some(Utc::now());
744 self.reconnect_attempts = 0;
745 }
746}
747
748#[derive(Debug, Clone, Serialize, Deserialize)]
754pub struct DataApiTrader {
755 pub address: String,
757 #[serde(rename = "displayName", default)]
759 pub display_name: Option<String>,
760 #[serde(rename = "profileImage", default)]
762 pub profile_image: Option<String>,
763 #[serde(rename = "totalPnl", default)]
765 pub total_pnl: Option<String>,
766 #[serde(rename = "totalVolume", default)]
768 pub total_volume: Option<String>,
769 #[serde(rename = "marketsTraded", default)]
771 pub markets_traded: Option<i32>,
772 #[serde(rename = "winRate", default)]
774 pub win_rate: Option<f64>,
775}
776
777#[derive(Debug, Clone, Serialize, Deserialize)]
779pub struct DataApiPosition {
780 pub id: String,
782 #[serde(rename = "conditionId")]
784 pub condition_id: String,
785 pub outcome: String,
787 pub size: String,
789 #[serde(rename = "avgPrice")]
791 pub avg_price: String,
792 #[serde(rename = "currentValue", default)]
794 pub current_value: Option<String>,
795 #[serde(rename = "realizedPnl", default)]
797 pub realized_pnl: Option<String>,
798 pub status: String,
800 #[serde(rename = "createdAt", default)]
802 pub created_at: Option<String>,
803 #[serde(rename = "closedAt", default)]
805 pub closed_at: Option<String>,
806}
807
808#[derive(Debug, Clone, Serialize, Deserialize)]
810pub struct DataApiTrade {
811 pub id: String,
813 #[serde(rename = "conditionId")]
815 pub condition_id: String,
816 pub maker: String,
818 pub taker: String,
820 pub side: String,
822 pub outcome: String,
824 pub size: String,
826 pub price: String,
828 pub timestamp: String,
830 #[serde(rename = "transactionHash", default)]
832 pub transaction_hash: Option<String>,
833}
834
835#[derive(Debug, Clone, Serialize, Deserialize)]
837pub struct DataApiActivity {
838 #[serde(rename = "transactionHash")]
840 pub transaction_hash: String,
841 pub timestamp: i64,
843 #[serde(rename = "proxyWallet")]
845 pub proxy_wallet: String,
846 #[serde(default)]
848 pub name: Option<String>,
849 #[serde(default)]
851 pub pseudonym: Option<String>,
852 #[serde(default)]
854 pub bio: Option<String>,
855 #[serde(rename = "profileImage", default)]
857 pub profile_image: Option<String>,
858 #[serde(rename = "profileImageOptimized", default)]
860 pub profile_image_optimized: Option<String>,
861 pub side: String,
863 pub outcome: String,
865 #[serde(rename = "outcomeIndex")]
867 pub outcome_index: i32,
868 pub price: f64,
870 pub size: f64,
872 #[serde(rename = "usdcSize", default)]
874 pub usdc_size: Option<f64>,
875 pub asset: String,
877 #[serde(rename = "conditionId")]
879 pub condition_id: String,
880 pub title: String,
882 pub slug: String,
884 #[serde(rename = "eventSlug")]
886 pub event_slug: String,
887 #[serde(default)]
889 pub icon: Option<String>,
890}
891
892#[derive(Debug, Clone, Serialize, Deserialize)]
894pub struct BiggestWinner {
895 #[serde(rename = "winRank")]
897 pub win_rank: String,
898 #[serde(rename = "proxyWallet")]
900 pub proxy_wallet: String,
901 #[serde(rename = "userName", default)]
903 pub user_name: String,
904 #[serde(rename = "eventSlug")]
906 pub event_slug: String,
907 #[serde(rename = "eventTitle")]
909 pub event_title: String,
910 #[serde(rename = "initialValue")]
912 pub initial_value: f64,
913 #[serde(rename = "finalValue")]
915 pub final_value: f64,
916 pub pnl: f64,
918 #[serde(rename = "profileImage", default)]
920 pub profile_image: String,
921}
922
923#[derive(Debug, Clone)]
925pub struct BiggestWinnersQuery {
926 pub time_period: String,
928 pub limit: usize,
930 pub offset: usize,
932 pub category: String,
934}
935
936impl Default for BiggestWinnersQuery {
937 fn default() -> Self {
938 Self {
939 time_period: "all_time".to_string(),
940 limit: 100,
941 offset: 0,
942 category: "all".to_string(),
943 }
944 }
945}
946
947impl BiggestWinnersQuery {
948 #[must_use]
950 pub fn new() -> Self {
951 Self::default()
952 }
953
954 #[must_use]
956 pub fn with_time_period(mut self, period: impl Into<String>) -> Self {
957 self.time_period = period.into();
958 self
959 }
960
961 #[must_use]
963 pub fn with_limit(mut self, limit: usize) -> Self {
964 self.limit = limit;
965 self
966 }
967
968 #[must_use]
970 pub fn with_offset(mut self, offset: usize) -> Self {
971 self.offset = offset;
972 self
973 }
974
975 #[must_use]
977 pub fn with_category(mut self, category: impl Into<String>) -> Self {
978 self.category = category.into();
979 self
980 }
981}
982
983#[derive(Debug, Clone, Serialize, Deserialize)]
989pub struct SearchRequest {
990 pub q: String,
992 #[serde(skip_serializing_if = "Option::is_none")]
994 pub limit_per_type: Option<u32>,
995 #[serde(skip_serializing_if = "Option::is_none")]
997 pub search_profiles: Option<bool>,
998 #[serde(skip_serializing_if = "Option::is_none")]
1000 pub search_tags: Option<bool>,
1001}
1002
1003impl SearchRequest {
1004 #[must_use]
1006 pub fn new(query: impl Into<String>) -> Self {
1007 Self {
1008 q: query.into(),
1009 limit_per_type: None,
1010 search_profiles: None,
1011 search_tags: None,
1012 }
1013 }
1014
1015 #[must_use]
1017 pub fn with_limit(mut self, limit: u32) -> Self {
1018 self.limit_per_type = Some(limit);
1019 self
1020 }
1021
1022 #[must_use]
1024 pub fn with_profiles(mut self, search_profiles: bool) -> Self {
1025 self.search_profiles = Some(search_profiles);
1026 self
1027 }
1028
1029 #[must_use]
1031 pub fn with_tags(mut self, search_tags: bool) -> Self {
1032 self.search_tags = Some(search_tags);
1033 self
1034 }
1035}
1036
1037#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1039pub struct SearchResponse {
1040 #[serde(default)]
1042 pub events: Vec<SearchEvent>,
1043 #[serde(default)]
1045 pub profiles: Vec<SearchProfile>,
1046 #[serde(default)]
1048 pub tags: Vec<SearchTag>,
1049}
1050
1051#[derive(Debug, Clone, Serialize, Deserialize)]
1053pub struct SearchEvent {
1054 #[serde(default)]
1056 pub id: String,
1057 #[serde(default)]
1059 pub slug: String,
1060 #[serde(default)]
1062 pub question: Option<String>,
1063 #[serde(default)]
1065 pub image: Option<String>,
1066 #[serde(default)]
1068 pub active: bool,
1069 #[serde(default)]
1071 pub closed: bool,
1072 #[serde(default)]
1074 pub volume: f64,
1075 #[serde(rename = "volume24hr", default)]
1077 pub volume_24hr: Option<f64>,
1078 #[serde(rename = "endDate", default)]
1080 pub end_date: Option<String>,
1081}
1082
1083#[derive(Debug, Clone, Serialize, Deserialize)]
1085pub struct SearchProfile {
1086 #[serde(default)]
1088 pub id: Option<String>,
1089 #[serde(default)]
1091 pub name: Option<String>,
1092 #[serde(rename = "imageURI", default)]
1094 pub image_uri: Option<String>,
1095 #[serde(rename = "profileImage", default)]
1097 pub profile_image: Option<String>,
1098 #[serde(default)]
1100 pub bio: Option<String>,
1101 #[serde(default)]
1103 pub pseudonym: Option<String>,
1104 #[serde(rename = "displayUsernamePublic", default)]
1106 pub display_username_public: bool,
1107 #[serde(rename = "walletAddress", default)]
1109 pub wallet_address: Option<String>,
1110 #[serde(rename = "proxyWallet", default)]
1112 pub proxy_wallet: Option<String>,
1113}
1114
1115impl SearchProfile {
1116 #[must_use]
1118 pub fn get_wallet_address(&self) -> Option<String> {
1119 self.proxy_wallet
1120 .clone()
1121 .or_else(|| self.wallet_address.clone())
1122 }
1123
1124 #[must_use]
1126 pub fn get_profile_image(&self) -> Option<String> {
1127 self.profile_image
1128 .clone()
1129 .or_else(|| self.image_uri.clone())
1130 }
1131
1132 #[must_use]
1134 pub fn get_display_name(&self) -> Option<String> {
1135 self.name.clone().or_else(|| self.pseudonym.clone())
1136 }
1137}
1138
1139#[derive(Debug, Clone, Serialize, Deserialize)]
1141pub struct SearchTag {
1142 pub id: String,
1144 pub label: String,
1146 #[serde(default)]
1148 pub slug: Option<String>,
1149}
1150
1151#[derive(Debug, Clone, Serialize, Deserialize)]
1153pub struct ClosedPosition {
1154 #[serde(default)]
1156 pub id: Option<String>,
1157 #[serde(rename = "proxyWallet", default)]
1159 pub proxy_wallet: Option<String>,
1160 #[serde(default)]
1162 pub asset: Option<String>,
1163 #[serde(rename = "conditionId")]
1165 pub condition_id: String,
1166 pub title: String,
1168 pub slug: String,
1170 #[serde(rename = "eventSlug")]
1172 pub event_slug: String,
1173 pub outcome: String,
1175 #[serde(rename = "outcomeIndex")]
1177 pub outcome_index: i32,
1178 #[serde(rename = "avgPrice")]
1180 pub avg_price: f64,
1181 #[serde(rename = "curPrice", default)]
1183 pub cur_price: Option<f64>,
1184 #[serde(rename = "exitPrice", default)]
1186 pub exit_price: Option<f64>,
1187 #[serde(default)]
1189 pub size: Option<f64>,
1190 #[serde(rename = "totalBought", default)]
1192 pub total_bought: Option<f64>,
1193 #[serde(rename = "realizedPnl", default)]
1195 pub realized_pnl: Option<f64>,
1196 #[serde(rename = "cashOut", default)]
1198 pub cash_out: Option<f64>,
1199 #[serde(rename = "isWinner", default)]
1201 pub is_winner: Option<bool>,
1202 #[serde(default)]
1204 pub timestamp: Option<i64>,
1205 #[serde(rename = "closedAt", default)]
1207 pub closed_at: Option<String>,
1208 #[serde(rename = "endDate", default)]
1210 pub end_date: Option<String>,
1211 #[serde(default)]
1213 pub icon: Option<String>,
1214 #[serde(rename = "oppositeOutcome", default)]
1216 pub opposite_outcome: Option<String>,
1217 #[serde(rename = "oppositeAsset", default)]
1219 pub opposite_asset: Option<String>,
1220}
1221
1222#[cfg(test)]
1223mod tests {
1224 use super::*;
1225
1226 #[test]
1229 fn test_new_order_json_field_order() {
1230 let order = NewOrder {
1231 defer_exec: false,
1232 order: NewOrderData {
1233 salt: 2915952280710976,
1234 maker: "0xc2ca793cf057d48a054bedabf625f301b40d38aa".to_string(),
1235 signer: "0xd13765b3e68431bf2b6e9994a0f4c3d2495799e9".to_string(),
1236 taker: "0x0000000000000000000000000000000000000000".to_string(),
1237 token_id: "21489772516410038586556744342392982044189999368638682594741395650226594484811".to_string(),
1238 maker_amount: "10000".to_string(),
1239 taker_amount: "1000000".to_string(),
1240 side: "BUY".to_string(),
1241 expiration: "0".to_string(),
1242 nonce: "0".to_string(),
1243 fee_rate_bps: "0".to_string(),
1244 signature_type: 1,
1245 signature: "0x0cfb0e318afe33e1189f23d4b11a1092963865d7ff7f7a035110d50d71a2ab484ae4828b3fcfcac2ada92fbd825eedfe4eb21d4e1cdd5aa1a47e23bf5d539b781c".to_string(),
1246 },
1247 owner: "fe9fb6b1-9ae6-6c5b-3cca-1ace6a8b1f29".to_string(),
1248 order_type: OrderType::GTC,
1249 };
1250
1251 let json = serde_json::to_string(&order).unwrap();
1252
1253 let expected = r#"{"deferExec":false,"order":{"salt":2915952280710976,"maker":"0xc2ca793cf057d48a054bedabf625f301b40d38aa","signer":"0xd13765b3e68431bf2b6e9994a0f4c3d2495799e9","taker":"0x0000000000000000000000000000000000000000","tokenId":"21489772516410038586556744342392982044189999368638682594741395650226594484811","makerAmount":"10000","takerAmount":"1000000","side":"BUY","expiration":"0","nonce":"0","feeRateBps":"0","signatureType":1,"signature":"0x0cfb0e318afe33e1189f23d4b11a1092963865d7ff7f7a035110d50d71a2ab484ae4828b3fcfcac2ada92fbd825eedfe4eb21d4e1cdd5aa1a47e23bf5d539b781c"},"owner":"fe9fb6b1-9ae6-6c5b-3cca-1ace6a8b1f29","orderType":"GTC"}"#;
1255
1256 assert_eq!(
1257 json, expected,
1258 "\nJSON field order mismatch!\n\nGot:\n{}\n\nExpected:\n{}\n",
1259 json, expected
1260 );
1261 }
1262}