1use ahash::AHashMap;
19use chrono::{DateTime, Utc};
20use rust_decimal::Decimal;
21use serde::{Deserialize, Serialize};
22use ustr::Ustr;
23
24use crate::common::{
25 enums::{
26 AxCandleWidth, AxInstrumentState, AxOrderSide, AxOrderStatus, AxOrderType, AxTimeInForce,
27 },
28 parse::{
29 deserialize_decimal_or_zero, deserialize_optional_decimal_from_str,
30 serialize_decimal_as_str, serialize_optional_decimal_as_str,
31 },
32};
33
34fn default_instrument_state() -> AxInstrumentState {
36 AxInstrumentState::Open
37}
38
39#[derive(Clone, Debug, Serialize, Deserialize)]
44#[serde(rename_all = "snake_case")]
45pub struct AxWhoAmI {
46 pub id: String,
48 pub username: String,
50 pub created_at: DateTime<Utc>,
52 pub enabled_2fa: bool,
54 pub is_onboarded: bool,
56 pub is_frozen: bool,
58 pub is_admin: bool,
60 pub is_close_only: bool,
62 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
64 pub maker_fee: Decimal,
65 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
67 pub taker_fee: Decimal,
68}
69
70#[derive(Clone, Debug, Serialize, Deserialize)]
75#[serde(rename_all = "snake_case")]
76pub struct AxInstrument {
77 pub symbol: Ustr,
79 #[serde(default = "default_instrument_state")]
81 pub state: AxInstrumentState,
82 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
84 pub multiplier: Decimal,
85 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
87 pub minimum_order_size: Decimal,
88 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
90 pub tick_size: Decimal,
91 pub quote_currency: Ustr,
93 pub funding_settlement_currency: Ustr,
95 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
97 pub maintenance_margin_pct: Decimal,
98 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
100 pub initial_margin_pct: Decimal,
101 #[serde(default)]
103 pub contract_mark_price: Option<String>,
104 #[serde(default)]
106 pub contract_size: Option<String>,
107 #[serde(default)]
109 pub description: Option<String>,
110 #[serde(default)]
112 pub funding_calendar_schedule: Option<String>,
113 #[serde(default)]
115 pub funding_frequency: Option<String>,
116 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
118 pub funding_rate_cap_lower_pct: Option<Decimal>,
119 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
121 pub funding_rate_cap_upper_pct: Option<Decimal>,
122 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
124 pub price_band_lower_deviation_pct: Option<Decimal>,
125 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
127 pub price_band_upper_deviation_pct: Option<Decimal>,
128 #[serde(default)]
130 pub price_bands: Option<String>,
131 #[serde(default)]
133 pub price_quotation: Option<String>,
134 #[serde(default)]
136 pub underlying_benchmark_price: Option<String>,
137}
138
139#[derive(Clone, Debug, Serialize, Deserialize)]
144#[serde(rename_all = "snake_case")]
145pub struct AxInstrumentsResponse {
146 pub instruments: Vec<AxInstrument>,
148}
149
150#[derive(Clone, Debug, Serialize, Deserialize)]
155#[serde(rename_all = "snake_case")]
156pub struct AxBalance {
157 pub symbol: Ustr,
159 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
161 pub amount: Decimal,
162}
163
164#[derive(Clone, Debug, Serialize, Deserialize)]
169#[serde(rename_all = "snake_case")]
170pub struct AxBalancesResponse {
171 pub balances: Vec<AxBalance>,
173}
174
175#[derive(Clone, Debug, Serialize, Deserialize)]
180#[serde(rename_all = "snake_case")]
181pub struct AxPosition {
182 pub user_id: String,
184 pub symbol: Ustr,
186 pub signed_quantity: i64,
188 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
190 pub signed_notional: Decimal,
191 pub timestamp: DateTime<Utc>,
193 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
195 pub realized_pnl: Decimal,
196}
197
198#[derive(Clone, Debug, Serialize, Deserialize)]
203#[serde(rename_all = "snake_case")]
204pub struct AxPositionsResponse {
205 pub positions: Vec<AxPosition>,
207}
208
209#[derive(Clone, Debug, Serialize, Deserialize)]
214#[serde(rename_all = "snake_case")]
215pub struct AxTicker {
216 pub symbol: Ustr,
218 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
220 pub bid: Option<Decimal>,
221 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
223 pub ask: Option<Decimal>,
224 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
226 pub last: Option<Decimal>,
227 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
229 pub mark: Option<Decimal>,
230 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
232 pub index: Option<Decimal>,
233 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
235 pub volume_24h: Option<Decimal>,
236 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
238 pub high_24h: Option<Decimal>,
239 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
241 pub low_24h: Option<Decimal>,
242 #[serde(default)]
244 pub timestamp: Option<DateTime<Utc>>,
245}
246
247#[derive(Clone, Debug, Serialize, Deserialize)]
252#[serde(rename_all = "snake_case")]
253pub struct AxTickersResponse {
254 pub tickers: Vec<AxTicker>,
256}
257
258#[derive(Clone, Debug, Serialize, Deserialize)]
263#[serde(rename_all = "snake_case")]
264pub struct AxAuthenticateResponse {
265 pub token: String,
267}
268
269#[derive(Clone, Debug, Serialize, Deserialize)]
274pub struct AxPlaceOrderResponse {
275 pub oid: String,
277}
278
279#[derive(Clone, Debug, Serialize, Deserialize)]
284pub struct AxCancelOrderResponse {
285 pub cxl_rx: bool,
287}
288
289#[derive(Clone, Debug, Serialize, Deserialize)]
294pub struct AxRestTrade {
295 pub ts: i64,
297 pub tn: i64,
299 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
301 pub p: Decimal,
302 pub q: i64,
304 pub s: Ustr,
306 pub d: AxOrderSide,
308}
309
310#[derive(Clone, Debug, Serialize, Deserialize)]
315pub struct AxTradesResponse {
316 pub trades: Vec<AxRestTrade>,
318}
319
320#[derive(Clone, Debug, Serialize, Deserialize)]
325pub struct AxBookLevel {
326 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
328 pub p: Decimal,
329 pub q: i64,
331 #[serde(default)]
333 pub o: Option<Vec<i64>>,
334}
335
336#[derive(Clone, Debug, Serialize, Deserialize)]
341pub struct AxBook {
342 pub ts: i64,
344 pub tn: i64,
346 pub s: String,
348 pub b: Vec<AxBookLevel>,
350 pub a: Vec<AxBookLevel>,
352}
353
354#[derive(Clone, Debug, Serialize, Deserialize)]
359pub struct AxBookResponse {
360 pub book: AxBook,
362}
363
364#[derive(Clone, Debug, Serialize, Deserialize)]
369pub struct AxOrderStatusDetail {
370 pub symbol: Ustr,
372 pub order_id: String,
374 pub state: AxOrderStatus,
376 #[serde(default)]
378 pub clord_id: Option<u64>,
379 #[serde(default)]
381 pub filled_quantity: Option<i64>,
382 #[serde(default)]
384 pub remaining_quantity: Option<i64>,
385}
386
387#[derive(Clone, Debug, Serialize, Deserialize)]
392pub struct AxOrderStatusQueryResponse {
393 pub status: AxOrderStatusDetail,
395}
396
397#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
402#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
403pub enum AxOrderRejectReason {
404 CloseOnly,
405 InsufficientMargin,
406 MaxOpenOrdersExceeded,
407 UnknownSymbol,
408 ExchangeClosed,
409 IncorrectQuantity,
410 InvalidPriceIncrement,
411 IncorrectOrderType,
412 PriceOutOfBounds,
413 NoLiquidity,
414 InsufficientCreditLimit,
415 #[serde(other)]
416 Unknown,
417}
418
419#[derive(Clone, Debug, Serialize, Deserialize)]
424pub struct AxOrderDetail {
425 pub ts: i64,
427 #[serde(default)]
429 pub tn: i64,
430 pub oid: String,
432 pub u: String,
434 pub s: Ustr,
436 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
438 pub p: Decimal,
439 pub q: u64,
441 pub xq: u64,
443 pub rq: u64,
445 pub o: AxOrderStatus,
447 pub d: AxOrderSide,
449 pub tif: AxTimeInForce,
451 #[serde(default)]
453 pub cid: Option<u64>,
454 #[serde(default)]
456 pub r: Option<AxOrderRejectReason>,
457 #[serde(default)]
459 pub tag: Option<String>,
460 #[serde(default)]
462 pub txt: Option<String>,
463}
464
465#[derive(Clone, Debug, Serialize, Deserialize)]
470pub struct AxOrdersResponse {
471 pub orders: Vec<AxOrderDetail>,
473 pub total_count: i64,
475 pub limit: i32,
477 pub offset: i32,
479}
480
481#[derive(Clone, Debug, Serialize, Deserialize)]
486pub struct AxInitialMarginRequirementResponse {
487 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
489 pub im: Decimal,
490}
491
492#[derive(Clone, Debug, Serialize, Deserialize)]
497pub struct AxOpenOrder {
498 pub tn: i64,
500 pub ts: i64,
502 pub d: AxOrderSide,
504 pub o: AxOrderStatus,
506 pub oid: String,
508 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
510 pub p: Decimal,
511 pub q: u64,
513 pub rq: u64,
515 pub s: Ustr,
517 pub tif: AxTimeInForce,
519 pub u: String,
521 pub xq: u64,
523 #[serde(default)]
525 pub cid: Option<u64>,
526 #[serde(default)]
528 pub tag: Option<String>,
529}
530
531#[derive(Clone, Debug, Serialize, Deserialize)]
536pub struct AxOpenOrdersResponse {
537 pub orders: Vec<AxOpenOrder>,
539}
540
541#[derive(Clone, Debug, Serialize, Deserialize)]
546#[serde(rename_all = "snake_case")]
547pub struct AxFill {
548 pub trade_id: String,
550 pub order_id: String,
552 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
554 pub fee: Decimal,
555 pub is_taker: bool,
557 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
559 pub price: Decimal,
560 pub quantity: u64,
562 pub side: AxOrderSide,
564 pub symbol: Ustr,
566 pub timestamp: DateTime<Utc>,
568 pub user_id: String,
570}
571
572#[derive(Clone, Debug, Serialize, Deserialize)]
577#[serde(rename_all = "snake_case")]
578pub struct AxFillsResponse {
579 pub fills: Vec<AxFill>,
581}
582
583#[derive(Clone, Debug, Serialize, Deserialize)]
588#[serde(rename_all = "snake_case")]
589pub struct AxCandle {
590 pub symbol: Ustr,
592 pub ts: i64,
594 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
596 pub open: Decimal,
597 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
599 pub high: Decimal,
600 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
602 pub low: Decimal,
603 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
605 pub close: Decimal,
606 pub buy_volume: u64,
608 pub sell_volume: u64,
610 pub volume: u64,
612 pub width: AxCandleWidth,
614}
615
616#[derive(Clone, Debug, Serialize, Deserialize)]
621#[serde(rename_all = "snake_case")]
622pub struct AxCandlesResponse {
623 pub candles: Vec<AxCandle>,
625}
626
627#[derive(Clone, Debug, Serialize, Deserialize)]
633#[serde(rename_all = "snake_case")]
634pub struct AxCandleResponse {
635 pub candle: AxCandle,
637}
638
639#[derive(Clone, Debug, Serialize, Deserialize)]
644#[serde(rename_all = "snake_case")]
645pub struct AxFundingRate {
646 pub symbol: Ustr,
648 pub timestamp_ns: i64,
650 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
652 pub funding_rate: Decimal,
653 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
655 pub funding_amount: Decimal,
656 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
658 pub benchmark_price: Decimal,
659 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
661 pub settlement_price: Decimal,
662}
663
664#[derive(Clone, Debug, Serialize, Deserialize)]
669#[serde(rename_all = "snake_case")]
670pub struct AxFundingRatesResponse {
671 pub funding_rates: Vec<AxFundingRate>,
673}
674
675#[derive(Clone, Debug, Serialize, Deserialize)]
680#[serde(rename_all = "snake_case")]
681pub struct AxPerSymbolRisk {
682 pub signed_quantity: i64,
684 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
686 pub signed_notional: Decimal,
687 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
689 pub average_price: Decimal,
690 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
692 pub liquidation_price: Option<Decimal>,
693 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
695 pub initial_margin_required: Option<Decimal>,
696 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
698 pub maintenance_margin_required: Option<Decimal>,
699 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
701 pub unrealized_pnl: Option<Decimal>,
702}
703
704#[derive(Clone, Debug, Serialize, Deserialize)]
709#[serde(rename_all = "snake_case")]
710pub struct AxRiskSnapshot {
711 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
713 pub balance_usd: Decimal,
714 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
716 pub equity: Decimal,
717 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
719 pub initial_margin_available: Decimal,
720 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
722 pub initial_margin_required_for_open_orders: Decimal,
723 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
725 pub initial_margin_required_for_positions: Decimal,
726 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
728 pub initial_margin_required_total: Decimal,
729 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
731 pub maintenance_margin_available: Decimal,
732 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
734 pub maintenance_margin_required: Decimal,
735 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
737 pub unrealized_pnl: Decimal,
738 pub timestamp_ns: DateTime<Utc>,
740 pub user_id: String,
742 #[serde(default)]
744 pub per_symbol: AHashMap<String, AxPerSymbolRisk>,
745}
746
747#[derive(Clone, Debug, Serialize, Deserialize)]
752#[serde(rename_all = "snake_case")]
753pub struct AxRiskSnapshotResponse {
754 pub risk_snapshot: AxRiskSnapshot,
756}
757
758#[derive(Clone, Debug, Serialize, Deserialize)]
763#[serde(rename_all = "snake_case")]
764pub struct AxTransaction {
765 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
767 pub amount: Decimal,
768 pub event_id: String,
770 pub symbol: Ustr,
772 pub timestamp: DateTime<Utc>,
774 pub transaction_type: Ustr,
776 pub user_id: String,
778 #[serde(default)]
780 pub reference_id: Option<String>,
781}
782
783#[derive(Clone, Debug, Serialize, Deserialize)]
788#[serde(rename_all = "snake_case")]
789pub struct AxTransactionsResponse {
790 pub transactions: Vec<AxTransaction>,
792}
793
794#[derive(Clone, Debug, Serialize, Deserialize)]
799#[serde(rename_all = "snake_case")]
800pub struct AuthenticateApiKeyRequest {
801 pub api_key: String,
803 pub api_secret: String,
805 pub expiration_seconds: i32,
807}
808
809impl AuthenticateApiKeyRequest {
810 #[must_use]
812 pub fn new(
813 api_key: impl Into<String>,
814 api_secret: impl Into<String>,
815 expiration_seconds: i32,
816 ) -> Self {
817 Self {
818 api_key: api_key.into(),
819 api_secret: api_secret.into(),
820 expiration_seconds,
821 }
822 }
823}
824
825#[derive(Clone, Debug, Serialize, Deserialize)]
830#[serde(rename_all = "snake_case")]
831pub struct AuthenticateUserRequest {
832 pub username: String,
834 pub password: String,
836 pub expiration_seconds: i32,
838}
839
840impl AuthenticateUserRequest {
841 #[must_use]
843 pub fn new(
844 username: impl Into<String>,
845 password: impl Into<String>,
846 expiration_seconds: i32,
847 ) -> Self {
848 Self {
849 username: username.into(),
850 password: password.into(),
851 expiration_seconds,
852 }
853 }
854}
855
856#[derive(Clone, Debug, Serialize, Deserialize)]
861pub struct PlaceOrderRequest {
862 pub d: AxOrderSide,
864 #[serde(serialize_with = "serialize_decimal_as_str")]
866 pub p: Decimal,
867 pub po: bool,
869 pub q: u64,
871 pub s: Ustr,
873 pub tif: AxTimeInForce,
875 #[serde(skip_serializing_if = "Option::is_none")]
877 pub tag: Option<String>,
878 #[serde(skip_serializing_if = "Option::is_none")]
880 pub order_type: Option<AxOrderType>,
881 #[serde(
883 skip_serializing_if = "Option::is_none",
884 serialize_with = "serialize_optional_decimal_as_str"
885 )]
886 pub trigger_price: Option<Decimal>,
887}
888
889impl PlaceOrderRequest {
890 #[must_use]
892 pub fn new(
893 side: AxOrderSide,
894 price: Decimal,
895 quantity: u64,
896 symbol: Ustr,
897 time_in_force: AxTimeInForce,
898 post_only: bool,
899 ) -> Self {
900 Self {
901 d: side,
902 p: price,
903 po: post_only,
904 q: quantity,
905 s: symbol,
906 tif: time_in_force,
907 tag: None,
908 order_type: None,
909 trigger_price: None,
910 }
911 }
912
913 #[must_use]
915 pub fn new_stop_loss(
916 side: AxOrderSide,
917 limit_price: Decimal,
918 trigger_price: Decimal,
919 quantity: u64,
920 symbol: Ustr,
921 time_in_force: AxTimeInForce,
922 ) -> Self {
923 Self {
924 d: side,
925 p: limit_price,
926 po: false,
927 q: quantity,
928 s: symbol,
929 tif: time_in_force,
930 tag: None,
931 order_type: Some(AxOrderType::StopLossLimit),
932 trigger_price: Some(trigger_price),
933 }
934 }
935
936 #[must_use]
938 pub fn with_tag(mut self, tag: impl Into<String>) -> Self {
939 self.tag = Some(tag.into());
940 self
941 }
942
943 #[must_use]
945 pub fn with_order_type(mut self, order_type: AxOrderType) -> Self {
946 self.order_type = Some(order_type);
947 self
948 }
949
950 #[must_use]
952 pub fn with_trigger_price(mut self, trigger_price: Decimal) -> Self {
953 self.trigger_price = Some(trigger_price);
954 self
955 }
956}
957
958#[derive(Clone, Debug, Serialize, Deserialize)]
963pub struct PreviewAggressiveLimitOrderRequest {
964 pub symbol: Ustr,
966 pub quantity: u64,
968 pub side: AxOrderSide,
970}
971
972impl PreviewAggressiveLimitOrderRequest {
973 #[must_use]
975 pub fn new(symbol: Ustr, quantity: u64, side: AxOrderSide) -> Self {
976 Self {
977 symbol,
978 quantity,
979 side,
980 }
981 }
982}
983
984#[derive(Clone, Debug, Serialize, Deserialize)]
989pub struct AxPreviewAggressiveLimitOrderResponse {
990 pub filled_quantity: u64,
992 pub remaining_quantity: u64,
994 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
996 pub limit_price: Option<Decimal>,
997 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
999 pub vwap: Option<Decimal>,
1000}
1001
1002#[derive(Clone, Debug, Serialize, Deserialize)]
1007pub struct CancelOrderRequest {
1008 pub oid: String,
1010}
1011
1012impl CancelOrderRequest {
1013 #[must_use]
1015 pub fn new(order_id: impl Into<String>) -> Self {
1016 Self {
1017 oid: order_id.into(),
1018 }
1019 }
1020}
1021
1022#[derive(Clone, Debug, Default, Serialize, Deserialize)]
1027pub struct CancelAllOrdersRequest {
1028 #[serde(skip_serializing_if = "Option::is_none")]
1030 pub symbol: Option<Ustr>,
1031 #[serde(skip_serializing_if = "Option::is_none")]
1033 pub execution_venue: Option<Ustr>,
1034}
1035
1036impl CancelAllOrdersRequest {
1037 #[must_use]
1039 pub fn new() -> Self {
1040 Self::default()
1041 }
1042
1043 #[must_use]
1045 pub fn with_symbol(mut self, symbol: Ustr) -> Self {
1046 self.symbol = Some(symbol);
1047 self
1048 }
1049
1050 #[must_use]
1052 pub fn with_venue(mut self, venue: Ustr) -> Self {
1053 self.execution_venue = Some(venue);
1054 self
1055 }
1056}
1057
1058#[derive(Clone, Debug, Serialize, Deserialize)]
1063pub struct AxCancelAllOrdersResponse {
1064 #[serde(default)]
1066 pub canceled_count: i64,
1067}
1068
1069#[derive(Clone, Debug, Serialize, Deserialize)]
1074pub struct BatchCancelOrdersRequest {
1075 pub order_ids: Vec<String>,
1077}
1078
1079impl BatchCancelOrdersRequest {
1080 #[must_use]
1082 pub fn new(order_ids: Vec<String>) -> Self {
1083 Self { order_ids }
1084 }
1085}
1086
1087#[derive(Clone, Debug, Serialize, Deserialize)]
1092pub struct AxBatchCancelOrdersResponse {
1093 #[serde(default)]
1095 pub canceled_count: i64,
1096 #[serde(default)]
1098 pub failed_order_ids: Vec<String>,
1099}
1100
1101#[cfg(test)]
1102mod tests {
1103 use rstest::rstest;
1104
1105 use super::*;
1106
1107 #[rstest]
1108 fn test_deserialize_authenticate_response() {
1109 let json = include_str!("../../test_data/http_authenticate.json");
1110 let response: AxAuthenticateResponse = serde_json::from_str(json).unwrap();
1111 assert!(response.token.starts_with("test-token"));
1112 }
1113
1114 #[rstest]
1115 fn test_deserialize_whoami_response() {
1116 let json = include_str!("../../test_data/http_get_whoami.json");
1117 let response: AxWhoAmI = serde_json::from_str(json).unwrap();
1118 assert_eq!(response.username, "test_user");
1119 assert!(response.enabled_2fa);
1120 }
1121
1122 #[rstest]
1123 fn test_deserialize_instruments_response() {
1124 let json = include_str!("../../test_data/http_get_instruments.json");
1125 let response: AxInstrumentsResponse = serde_json::from_str(json).unwrap();
1126 assert_eq!(response.instruments.len(), 4);
1127 assert_eq!(response.instruments[0].symbol, "BTCUSD-PERP");
1128 }
1129
1130 #[rstest]
1131 fn test_deserialize_balances_response() {
1132 let json = include_str!("../../test_data/http_get_balances.json");
1133 let response: AxBalancesResponse = serde_json::from_str(json).unwrap();
1134 assert_eq!(response.balances.len(), 3);
1135 assert_eq!(response.balances[0].symbol, "USD");
1136 }
1137
1138 #[rstest]
1139 fn test_deserialize_positions_response() {
1140 let json = include_str!("../../test_data/http_get_positions.json");
1141 let response: AxPositionsResponse = serde_json::from_str(json).unwrap();
1142 assert_eq!(response.positions.len(), 2);
1143 assert_eq!(response.positions[0].symbol, "BTC-PERP");
1144 assert_eq!(response.positions[1].signed_quantity, -5);
1145 }
1146
1147 #[rstest]
1148 fn test_deserialize_tickers_response() {
1149 let json = include_str!("../../test_data/http_get_tickers.json");
1150 let response: AxTickersResponse = serde_json::from_str(json).unwrap();
1151 assert_eq!(response.tickers.len(), 3);
1152 assert_eq!(response.tickers[0].symbol, "EURUSD-PERP");
1153 assert!(response.tickers[0].bid.is_some());
1154 assert!(response.tickers[2].bid.is_none());
1155 }
1156
1157 #[rstest]
1158 fn test_deserialize_funding_rates_response() {
1159 let json = include_str!("../../test_data/http_get_funding_rates.json");
1160 let response: AxFundingRatesResponse = serde_json::from_str(json).unwrap();
1161 assert_eq!(response.funding_rates.len(), 2);
1162 assert_eq!(response.funding_rates[0].symbol, "JPYUSD-PERP");
1163 }
1164
1165 #[rstest]
1166 fn test_deserialize_open_orders_response() {
1167 let json = include_str!("../../test_data/http_get_open_orders.json");
1168 let response: AxOpenOrdersResponse = serde_json::from_str(json).unwrap();
1169 assert_eq!(response.orders.len(), 2);
1170 assert_eq!(response.orders[0].oid, "O-01ARZ3NDEKTSV4RRFFQ69G5FAV");
1171 assert_eq!(response.orders[0].d, AxOrderSide::Buy);
1172 assert_eq!(response.orders[0].o, AxOrderStatus::Accepted);
1173 assert_eq!(response.orders[1].xq, 300);
1174 }
1175
1176 #[rstest]
1177 fn test_deserialize_fills_response() {
1178 let json = include_str!("../../test_data/http_get_fills.json");
1179 let response: AxFillsResponse = serde_json::from_str(json).unwrap();
1180 assert_eq!(response.fills.len(), 2);
1181 assert_eq!(response.fills[0].side, AxOrderSide::Buy);
1182 assert!(response.fills[0].is_taker);
1183 assert!(!response.fills[1].is_taker);
1184 }
1185
1186 #[rstest]
1187 fn test_deserialize_candles_response() {
1188 let json = include_str!("../../test_data/http_get_candles.json");
1189 let response: AxCandlesResponse = serde_json::from_str(json).unwrap();
1190 assert_eq!(response.candles.len(), 2);
1191 assert_eq!(response.candles[0].symbol, "EURUSD-PERP");
1192 assert_eq!(response.candles[0].width, AxCandleWidth::Minutes1);
1193 }
1194
1195 #[rstest]
1196 fn test_deserialize_candle_response() {
1197 let json = include_str!("../../test_data/http_get_candle.json");
1198 let response: AxCandleResponse = serde_json::from_str(json).unwrap();
1199 assert_eq!(response.candle.symbol, "EURUSD-PERP");
1200 assert_eq!(response.candle.width, AxCandleWidth::Minutes1);
1201 }
1202
1203 #[rstest]
1204 fn test_deserialize_risk_snapshot_response() {
1205 let json = include_str!("../../test_data/http_get_risk_snapshot.json");
1206 let response: AxRiskSnapshotResponse = serde_json::from_str(json).unwrap();
1207 assert_eq!(
1208 response.risk_snapshot.user_id,
1209 "3c90c3cc-0d44-4b50-8888-8dd25736052a"
1210 );
1211 assert_eq!(response.risk_snapshot.per_symbol.len(), 2);
1212 assert!(
1213 response
1214 .risk_snapshot
1215 .per_symbol
1216 .contains_key("EURUSD-PERP")
1217 );
1218 }
1219
1220 #[rstest]
1221 fn test_deserialize_transactions_response() {
1222 let json = include_str!("../../test_data/http_get_transactions.json");
1223 let response: AxTransactionsResponse = serde_json::from_str(json).unwrap();
1224 assert_eq!(response.transactions.len(), 2);
1225 assert_eq!(response.transactions[0].transaction_type, "deposit");
1226 assert!(response.transactions[1].reference_id.is_none());
1227 }
1228
1229 #[rstest]
1230 fn test_deserialize_preview_aggressive_limit_order_response() {
1231 let json = include_str!("../../test_data/http_preview_aggressive_limit_order.json");
1232 let response: AxPreviewAggressiveLimitOrderResponse = serde_json::from_str(json).unwrap();
1233 assert_eq!(response.filled_quantity, 1000);
1234 assert_eq!(response.remaining_quantity, 0);
1235 assert!(response.limit_price.is_some());
1236 assert!(response.vwap.is_some());
1237 }
1238
1239 #[rstest]
1240 fn test_deserialize_place_order_response() {
1241 let json = include_str!("../../test_data/http_place_order.json");
1242 let response: AxPlaceOrderResponse = serde_json::from_str(json).unwrap();
1243 assert_eq!(response.oid, "O-01ARZ3NDEKTSV4RRFFQ69G5FAV");
1244 }
1245
1246 #[rstest]
1247 fn test_deserialize_cancel_order_response() {
1248 let json = include_str!("../../test_data/http_cancel_order.json");
1249 let response: AxCancelOrderResponse = serde_json::from_str(json).unwrap();
1250 assert!(response.cxl_rx);
1251 }
1252
1253 #[rstest]
1254 fn test_deserialize_cancel_all_orders_response() {
1255 let json = include_str!("../../test_data/http_cancel_all_orders.json");
1256 let response: AxCancelAllOrdersResponse = serde_json::from_str(json).unwrap();
1257 assert_eq!(response.canceled_count, 3);
1258 }
1259
1260 #[rstest]
1261 fn test_deserialize_batch_cancel_orders_response() {
1262 let json = include_str!("../../test_data/http_batch_cancel_orders.json");
1263 let response: AxBatchCancelOrdersResponse = serde_json::from_str(json).unwrap();
1264 assert_eq!(response.canceled_count, 2);
1265 assert_eq!(response.failed_order_ids.len(), 1);
1266 }
1267
1268 #[rstest]
1269 fn test_deserialize_trades_response() {
1270 let json = include_str!("../../test_data/http_get_trades.json");
1271 let response: AxTradesResponse = serde_json::from_str(json).unwrap();
1272 assert_eq!(response.trades.len(), 2);
1273 assert_eq!(response.trades[0].s, "EURUSD-PERP");
1274 assert_eq!(response.trades[0].d, AxOrderSide::Buy);
1275 assert_eq!(response.trades[0].q, 100);
1276 assert_eq!(response.trades[1].d, AxOrderSide::Sell);
1277 }
1278
1279 #[rstest]
1280 fn test_deserialize_book_response() {
1281 let json = include_str!("../../test_data/http_get_book.json");
1282 let response: AxBookResponse = serde_json::from_str(json).unwrap();
1283 assert_eq!(response.book.s, "EURUSD-PERP");
1284 assert_eq!(response.book.b.len(), 3);
1285 assert_eq!(response.book.a.len(), 3);
1286 assert_eq!(response.book.b[0].q, 500);
1287 assert_eq!(response.book.a[0].q, 400);
1288 }
1289
1290 #[rstest]
1291 fn test_deserialize_order_status_query_response() {
1292 let json = include_str!("../../test_data/http_get_order_status.json");
1293 let response: AxOrderStatusQueryResponse = serde_json::from_str(json).unwrap();
1294 assert_eq!(response.status.symbol, "EURUSD-PERP");
1295 assert_eq!(response.status.order_id, "O-01ARZ3NDEKTSV4RRFFQ69G5FAV");
1296 assert_eq!(response.status.state, AxOrderStatus::PartiallyFilled);
1297 assert_eq!(response.status.clord_id, Some(12345));
1298 assert_eq!(response.status.filled_quantity, Some(300));
1299 assert_eq!(response.status.remaining_quantity, Some(700));
1300 }
1301
1302 #[rstest]
1303 fn test_deserialize_orders_response() {
1304 let json = include_str!("../../test_data/http_get_orders.json");
1305 let response: AxOrdersResponse = serde_json::from_str(json).unwrap();
1306 assert_eq!(response.orders.len(), 2);
1307 assert_eq!(response.total_count, 2);
1308 assert_eq!(response.orders[0].o, AxOrderStatus::PartiallyFilled);
1309 assert_eq!(response.orders[0].xq, 300);
1310 assert_eq!(response.orders[1].o, AxOrderStatus::Filled);
1311 assert_eq!(response.orders[1].d, AxOrderSide::Sell);
1312 }
1313
1314 #[rstest]
1315 fn test_deserialize_initial_margin_requirement_response() {
1316 let json = include_str!("../../test_data/http_initial_margin_requirement.json");
1317 let response: AxInitialMarginRequirementResponse = serde_json::from_str(json).unwrap();
1318 assert_eq!(response.im, Decimal::new(125050, 2));
1319 }
1320}