1use ahash::AHashMap;
19use chrono::{DateTime, Utc};
20use rust_decimal::Decimal;
21use serde::{Deserialize, Serialize};
22use strum::{AsRefStr, Display};
23use ustr::Ustr;
24
25use crate::common::{
26 enums::{
27 AxCandleWidth, AxCategory, AxInstrumentState, AxOrderSide, AxOrderStatus, AxOrderType,
28 AxTimeInForce,
29 },
30 parse::{
31 deserialize_decimal_or_zero, deserialize_optional_decimal_from_str,
32 serialize_decimal_as_str, serialize_optional_decimal_as_str,
33 },
34};
35
36fn default_instrument_state() -> AxInstrumentState {
38 AxInstrumentState::Open
39}
40
41#[derive(Clone, Debug, Serialize, Deserialize)]
46#[serde(rename_all = "snake_case")]
47pub struct AxWhoAmI {
48 pub id: String,
50 pub username: String,
52 pub created_at: DateTime<Utc>,
54 pub enabled_2fa: bool,
56 pub is_onboarded: bool,
58 pub is_frozen: bool,
60 pub is_admin: bool,
62 pub is_close_only: bool,
64 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
66 pub maker_fee: Decimal,
67 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
69 pub taker_fee: Decimal,
70}
71
72#[derive(Clone, Debug, Serialize, Deserialize)]
77#[serde(rename_all = "snake_case")]
78pub struct AxInstrument {
79 pub symbol: Ustr,
81 #[serde(default = "default_instrument_state")]
83 pub state: AxInstrumentState,
84 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
86 pub multiplier: Decimal,
87 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
89 pub minimum_order_size: Decimal,
90 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
92 pub tick_size: Decimal,
93 pub quote_currency: Ustr,
95 pub funding_settlement_currency: Ustr,
97 #[serde(default)]
99 pub category: Option<AxCategory>,
100 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
102 pub maintenance_margin_pct: Decimal,
103 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
105 pub initial_margin_pct: Decimal,
106 #[serde(default)]
108 pub contract_mark_price: Option<String>,
109 #[serde(default)]
111 pub contract_size: Option<String>,
112 #[serde(default)]
114 pub description: Option<String>,
115 #[serde(default)]
117 pub funding_calendar_schedule: Option<String>,
118 #[serde(default)]
120 pub funding_frequency: Option<String>,
121 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
123 pub funding_rate_cap_lower_pct: Option<Decimal>,
124 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
126 pub funding_rate_cap_upper_pct: Option<Decimal>,
127 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
129 pub price_band_lower_deviation_pct: Option<Decimal>,
130 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
132 pub price_band_upper_deviation_pct: Option<Decimal>,
133 #[serde(default)]
135 pub price_bands: Option<String>,
136 #[serde(default)]
138 pub price_quotation: Option<String>,
139 #[serde(default)]
141 pub underlying_benchmark_price: Option<String>,
142}
143
144#[derive(Clone, Debug, Serialize, Deserialize)]
149#[serde(rename_all = "snake_case")]
150pub struct AxInstrumentsResponse {
151 pub instruments: Vec<AxInstrument>,
153}
154
155#[derive(Clone, Debug, Serialize, Deserialize)]
160#[serde(rename_all = "snake_case")]
161pub struct AxBalance {
162 pub symbol: Ustr,
164 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
166 pub amount: Decimal,
167}
168
169#[derive(Clone, Debug, Serialize, Deserialize)]
174#[serde(rename_all = "snake_case")]
175pub struct AxBalancesResponse {
176 pub balances: Vec<AxBalance>,
178}
179
180#[derive(Clone, Debug, Serialize, Deserialize)]
185#[serde(rename_all = "snake_case")]
186pub struct AxPosition {
187 pub user_id: String,
189 pub symbol: Ustr,
191 pub signed_quantity: i64,
193 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
195 pub signed_notional: Decimal,
196 pub timestamp: DateTime<Utc>,
198 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
200 pub realized_pnl: Decimal,
201}
202
203#[derive(Clone, Debug, Serialize, Deserialize)]
208#[serde(rename_all = "snake_case")]
209pub struct AxPositionsResponse {
210 pub positions: Vec<AxPosition>,
212}
213
214#[derive(Clone, Debug, Serialize, Deserialize)]
219#[serde(rename_all = "snake_case")]
220pub struct AxTicker {
221 pub symbol: Ustr,
223 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
225 pub bid: Option<Decimal>,
226 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
228 pub ask: Option<Decimal>,
229 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
231 pub last: Option<Decimal>,
232 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
234 pub mark: Option<Decimal>,
235 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
237 pub index: Option<Decimal>,
238 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
240 pub volume_24h: Option<Decimal>,
241 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
243 pub high_24h: Option<Decimal>,
244 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
246 pub low_24h: Option<Decimal>,
247 #[serde(default)]
249 pub timestamp: Option<DateTime<Utc>>,
250}
251
252#[derive(Clone, Debug, Serialize, Deserialize)]
257#[serde(rename_all = "snake_case")]
258pub struct AxTickersResponse {
259 pub tickers: Vec<AxTicker>,
261}
262
263#[derive(Clone, Debug, Serialize, Deserialize)]
268#[serde(rename_all = "snake_case")]
269pub struct AxAuthenticateResponse {
270 pub token: String,
272}
273
274#[derive(Clone, Debug, Serialize, Deserialize)]
279pub struct AxPlaceOrderResponse {
280 pub oid: String,
282}
283
284#[derive(Clone, Debug, Serialize, Deserialize)]
289pub struct AxCancelOrderResponse {
290 pub cxl_rx: bool,
292}
293
294#[derive(Clone, Debug, Serialize, Deserialize)]
299pub struct AxRestTrade {
300 pub ts: i64,
302 pub tn: i64,
304 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
306 pub p: Decimal,
307 pub q: i64,
309 pub s: Ustr,
311 pub d: AxOrderSide,
313}
314
315#[derive(Clone, Debug, Serialize, Deserialize)]
320pub struct AxTradesResponse {
321 pub trades: Vec<AxRestTrade>,
323}
324
325#[derive(Clone, Debug, Serialize, Deserialize)]
330pub struct AxBookLevel {
331 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
333 pub p: Decimal,
334 pub q: i64,
336 #[serde(default)]
338 pub o: Option<Vec<i64>>,
339}
340
341#[derive(Clone, Debug, Serialize, Deserialize)]
346pub struct AxBook {
347 pub ts: i64,
349 pub tn: i64,
351 pub s: Ustr,
353 pub b: Vec<AxBookLevel>,
355 pub a: Vec<AxBookLevel>,
357}
358
359#[derive(Clone, Debug, Serialize, Deserialize)]
364pub struct AxBookResponse {
365 pub book: AxBook,
367}
368
369#[derive(Clone, Debug, Serialize, Deserialize)]
374pub struct AxOrderStatusDetail {
375 pub symbol: Ustr,
377 pub order_id: String,
379 pub state: AxOrderStatus,
381 #[serde(default)]
383 pub clord_id: Option<u64>,
384 #[serde(default)]
386 pub filled_quantity: Option<i64>,
387 #[serde(default)]
389 pub remaining_quantity: Option<i64>,
390}
391
392#[derive(Clone, Debug, Serialize, Deserialize)]
397pub struct AxOrderStatusQueryResponse {
398 pub status: AxOrderStatusDetail,
400}
401
402#[derive(Clone, Copy, Debug, Display, Eq, PartialEq, Hash, AsRefStr, Serialize, Deserialize)]
407#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
408#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
409pub enum AxOrderRejectReason {
410 CloseOnly,
411 InsufficientMargin,
412 MaxOpenOrdersExceeded,
413 UnknownSymbol,
414 ExchangeClosed,
415 IncorrectQuantity,
416 InvalidPriceIncrement,
417 IncorrectOrderType,
418 PriceOutOfBounds,
419 NoLiquidity,
420 InsufficientCreditLimit,
421 #[serde(other)]
422 Unknown,
423}
424
425#[derive(Clone, Debug, Serialize, Deserialize)]
430pub struct AxOrderDetail {
431 pub ts: i64,
433 #[serde(default)]
435 pub tn: i64,
436 pub oid: String,
438 pub u: String,
440 pub s: Ustr,
442 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
444 pub p: Decimal,
445 pub q: u64,
447 pub xq: u64,
449 pub rq: u64,
451 pub o: AxOrderStatus,
453 pub d: AxOrderSide,
455 pub tif: AxTimeInForce,
457 #[serde(default)]
459 pub cid: Option<u64>,
460 #[serde(default)]
462 pub r: Option<AxOrderRejectReason>,
463 #[serde(default)]
465 pub tag: Option<String>,
466 #[serde(default)]
468 pub txt: Option<String>,
469 #[serde(default)]
471 pub po: bool,
472}
473
474#[derive(Clone, Debug, Serialize, Deserialize)]
479pub struct AxOrdersResponse {
480 pub orders: Vec<AxOrderDetail>,
482 pub total_count: i64,
484 pub limit: i32,
486 pub offset: i32,
488}
489
490#[derive(Clone, Debug, Serialize, Deserialize)]
495pub struct AxInitialMarginRequirementResponse {
496 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
498 pub im: Decimal,
499}
500
501#[derive(Clone, Debug, Serialize, Deserialize)]
506pub struct AxOpenOrder {
507 pub tn: i64,
509 pub ts: i64,
511 pub d: AxOrderSide,
513 pub o: AxOrderStatus,
515 pub oid: String,
517 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
519 pub p: Decimal,
520 pub q: u64,
522 pub rq: u64,
524 pub s: Ustr,
526 pub tif: AxTimeInForce,
528 pub u: String,
530 pub xq: u64,
532 #[serde(default)]
534 pub cid: Option<u64>,
535 #[serde(default)]
537 pub tag: Option<String>,
538 #[serde(default)]
540 pub po: bool,
541}
542
543#[derive(Clone, Debug, Serialize, Deserialize)]
548pub struct AxOpenOrdersResponse {
549 pub orders: Vec<AxOpenOrder>,
551}
552
553#[derive(Clone, Debug, Serialize, Deserialize)]
558#[serde(rename_all = "snake_case")]
559pub struct AxFill {
560 pub trade_id: String,
562 pub order_id: String,
564 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
566 pub fee: Decimal,
567 pub is_taker: bool,
569 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
571 pub price: Decimal,
572 pub quantity: u64,
574 pub side: AxOrderSide,
576 pub symbol: Ustr,
578 pub timestamp: DateTime<Utc>,
580 pub user_id: String,
582 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
584 pub realized_pnl: Option<Decimal>,
585}
586
587#[derive(Clone, Debug, Serialize, Deserialize)]
592#[serde(rename_all = "snake_case")]
593pub struct AxFillsResponse {
594 pub fills: Vec<AxFill>,
596}
597
598#[derive(Clone, Debug, Serialize, Deserialize)]
603#[serde(rename_all = "snake_case")]
604pub struct AxCandle {
605 pub symbol: Ustr,
607 pub ts: i64,
609 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
611 pub open: Decimal,
612 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
614 pub high: Decimal,
615 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
617 pub low: Decimal,
618 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
620 pub close: Decimal,
621 pub buy_volume: u64,
623 pub sell_volume: u64,
625 pub volume: u64,
627 pub width: AxCandleWidth,
629}
630
631#[derive(Clone, Debug, Serialize, Deserialize)]
636#[serde(rename_all = "snake_case")]
637pub struct AxCandlesResponse {
638 pub candles: Vec<AxCandle>,
640}
641
642#[derive(Clone, Debug, Serialize, Deserialize)]
648#[serde(rename_all = "snake_case")]
649pub struct AxCandleResponse {
650 pub candle: AxCandle,
652}
653
654#[derive(Clone, Debug, Serialize, Deserialize)]
659#[serde(rename_all = "snake_case")]
660pub struct AxFundingRate {
661 pub symbol: Ustr,
663 pub timestamp_ns: i64,
665 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
667 pub funding_rate: Decimal,
668 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
670 pub funding_amount: Decimal,
671 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
673 pub benchmark_price: Decimal,
674 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
676 pub settlement_price: Decimal,
677}
678
679#[derive(Clone, Debug, Serialize, Deserialize)]
684#[serde(rename_all = "snake_case")]
685pub struct AxFundingRatesResponse {
686 pub funding_rates: Vec<AxFundingRate>,
688}
689
690#[derive(Clone, Debug, Serialize, Deserialize)]
695#[serde(rename_all = "snake_case")]
696pub struct AxPerSymbolRisk {
697 pub signed_quantity: i64,
699 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
701 pub signed_notional: Decimal,
702 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
704 pub average_price: Decimal,
705 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
707 pub liquidation_price: Option<Decimal>,
708 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
710 pub initial_margin_required: Option<Decimal>,
711 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
713 pub maintenance_margin_required: Option<Decimal>,
714 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
716 pub unrealized_pnl: Option<Decimal>,
717}
718
719#[derive(Clone, Debug, Serialize, Deserialize)]
724#[serde(rename_all = "snake_case")]
725pub struct AxRiskSnapshot {
726 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
728 pub balance_usd: Decimal,
729 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
731 pub equity: Decimal,
732 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
734 pub initial_margin_available: Decimal,
735 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
737 pub initial_margin_required_for_open_orders: Decimal,
738 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
740 pub initial_margin_required_for_positions: Decimal,
741 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
743 pub initial_margin_required_total: Decimal,
744 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
746 pub maintenance_margin_available: Decimal,
747 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
749 pub maintenance_margin_required: Decimal,
750 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
752 pub unrealized_pnl: Decimal,
753 pub timestamp_ns: DateTime<Utc>,
755 pub user_id: String,
757 #[serde(default)]
759 pub per_symbol: AHashMap<String, AxPerSymbolRisk>,
760}
761
762#[derive(Clone, Debug, Serialize, Deserialize)]
767#[serde(rename_all = "snake_case")]
768pub struct AxRiskSnapshotResponse {
769 pub risk_snapshot: AxRiskSnapshot,
771}
772
773#[derive(Clone, Debug, Serialize, Deserialize)]
778#[serde(rename_all = "snake_case")]
779pub struct AxTransaction {
780 #[serde(deserialize_with = "deserialize_decimal_or_zero")]
782 pub amount: Decimal,
783 pub event_id: String,
785 pub symbol: Ustr,
787 pub timestamp: DateTime<Utc>,
789 pub transaction_type: Ustr,
791 pub user_id: String,
793 #[serde(default)]
795 pub reference_id: Option<String>,
796}
797
798#[derive(Clone, Debug, Serialize, Deserialize)]
803#[serde(rename_all = "snake_case")]
804pub struct AxTransactionsResponse {
805 pub transactions: Vec<AxTransaction>,
807}
808
809#[derive(Clone, Debug, Serialize, Deserialize)]
814#[serde(rename_all = "snake_case")]
815pub struct AuthenticateApiKeyRequest {
816 pub api_key: String,
818 pub api_secret: String,
820 pub expiration_seconds: i32,
822}
823
824impl AuthenticateApiKeyRequest {
825 #[must_use]
827 pub fn new(
828 api_key: impl Into<String>,
829 api_secret: impl Into<String>,
830 expiration_seconds: i32,
831 ) -> Self {
832 Self {
833 api_key: api_key.into(),
834 api_secret: api_secret.into(),
835 expiration_seconds,
836 }
837 }
838}
839
840#[derive(Clone, Debug, Serialize, Deserialize)]
845#[serde(rename_all = "snake_case")]
846pub struct AuthenticateUserRequest {
847 pub username: String,
849 pub password: String,
851 pub expiration_seconds: i32,
853}
854
855impl AuthenticateUserRequest {
856 #[must_use]
858 pub fn new(
859 username: impl Into<String>,
860 password: impl Into<String>,
861 expiration_seconds: i32,
862 ) -> Self {
863 Self {
864 username: username.into(),
865 password: password.into(),
866 expiration_seconds,
867 }
868 }
869}
870
871#[derive(Clone, Debug, Serialize, Deserialize)]
876pub struct PlaceOrderRequest {
877 pub d: AxOrderSide,
879 #[serde(serialize_with = "serialize_decimal_as_str")]
881 pub p: Decimal,
882 pub po: bool,
884 pub q: u64,
886 pub s: Ustr,
888 pub tif: AxTimeInForce,
890 #[serde(skip_serializing_if = "Option::is_none")]
892 pub tag: Option<String>,
893 #[serde(skip_serializing_if = "Option::is_none")]
895 pub order_type: Option<AxOrderType>,
896 #[serde(
898 skip_serializing_if = "Option::is_none",
899 serialize_with = "serialize_optional_decimal_as_str"
900 )]
901 pub trigger_price: Option<Decimal>,
902}
903
904impl PlaceOrderRequest {
905 #[must_use]
907 pub fn new(
908 side: AxOrderSide,
909 price: Decimal,
910 quantity: u64,
911 symbol: Ustr,
912 time_in_force: AxTimeInForce,
913 post_only: bool,
914 ) -> Self {
915 Self {
916 d: side,
917 p: price,
918 po: post_only,
919 q: quantity,
920 s: symbol,
921 tif: time_in_force,
922 tag: None,
923 order_type: None,
924 trigger_price: None,
925 }
926 }
927
928 #[must_use]
930 pub fn new_stop_loss(
931 side: AxOrderSide,
932 limit_price: Decimal,
933 trigger_price: Decimal,
934 quantity: u64,
935 symbol: Ustr,
936 time_in_force: AxTimeInForce,
937 ) -> Self {
938 Self {
939 d: side,
940 p: limit_price,
941 po: false,
942 q: quantity,
943 s: symbol,
944 tif: time_in_force,
945 tag: None,
946 order_type: Some(AxOrderType::StopLossLimit),
947 trigger_price: Some(trigger_price),
948 }
949 }
950
951 #[must_use]
953 pub fn with_tag(mut self, tag: impl Into<String>) -> Self {
954 self.tag = Some(tag.into());
955 self
956 }
957
958 #[must_use]
960 pub fn with_order_type(mut self, order_type: AxOrderType) -> Self {
961 self.order_type = Some(order_type);
962 self
963 }
964
965 #[must_use]
967 pub fn with_trigger_price(mut self, trigger_price: Decimal) -> Self {
968 self.trigger_price = Some(trigger_price);
969 self
970 }
971}
972
973#[derive(Clone, Debug, Serialize, Deserialize)]
978pub struct PreviewAggressiveLimitOrderRequest {
979 pub symbol: Ustr,
981 pub quantity: u64,
983 pub side: AxOrderSide,
985}
986
987impl PreviewAggressiveLimitOrderRequest {
988 #[must_use]
990 pub fn new(symbol: Ustr, quantity: u64, side: AxOrderSide) -> Self {
991 Self {
992 symbol,
993 quantity,
994 side,
995 }
996 }
997}
998
999#[derive(Clone, Debug, Serialize, Deserialize)]
1004pub struct AxPreviewAggressiveLimitOrderResponse {
1005 pub filled_quantity: u64,
1007 pub remaining_quantity: u64,
1009 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
1011 pub limit_price: Option<Decimal>,
1012 #[serde(default, deserialize_with = "deserialize_optional_decimal_from_str")]
1014 pub vwap: Option<Decimal>,
1015}
1016
1017#[derive(Clone, Debug, Serialize, Deserialize)]
1022pub struct CancelOrderRequest {
1023 pub oid: String,
1025}
1026
1027impl CancelOrderRequest {
1028 #[must_use]
1030 pub fn new(order_id: impl Into<String>) -> Self {
1031 Self {
1032 oid: order_id.into(),
1033 }
1034 }
1035}
1036
1037#[derive(Clone, Debug, Serialize, Deserialize)]
1045pub struct ReplaceOrderRequest {
1046 pub oid: String,
1048 #[serde(
1050 skip_serializing_if = "Option::is_none",
1051 serialize_with = "serialize_optional_decimal_as_str"
1052 )]
1053 pub p: Option<Decimal>,
1054 #[serde(skip_serializing_if = "Option::is_none")]
1056 pub q: Option<u64>,
1057 #[serde(skip_serializing_if = "Option::is_none")]
1059 pub po: Option<bool>,
1060 #[serde(skip_serializing_if = "Option::is_none")]
1062 pub tif: Option<AxTimeInForce>,
1063 #[serde(
1065 skip_serializing_if = "Option::is_none",
1066 serialize_with = "serialize_optional_decimal_as_str"
1067 )]
1068 pub trigger_price: Option<Decimal>,
1069}
1070
1071impl ReplaceOrderRequest {
1072 #[must_use]
1076 pub fn new(order_id: impl Into<String>) -> Self {
1077 Self {
1078 oid: order_id.into(),
1079 p: None,
1080 q: None,
1081 po: None,
1082 tif: None,
1083 trigger_price: None,
1084 }
1085 }
1086
1087 #[must_use]
1089 pub fn with_price(mut self, price: Decimal) -> Self {
1090 self.p = Some(price);
1091 self
1092 }
1093
1094 #[must_use]
1096 pub fn with_quantity(mut self, quantity: u64) -> Self {
1097 self.q = Some(quantity);
1098 self
1099 }
1100
1101 #[must_use]
1103 pub fn with_trigger_price(mut self, trigger_price: Decimal) -> Self {
1104 self.trigger_price = Some(trigger_price);
1105 self
1106 }
1107}
1108
1109#[derive(Clone, Debug, Serialize, Deserialize)]
1114pub struct AxReplaceOrderResponse {
1115 pub oid: String,
1117}
1118
1119#[derive(Clone, Debug, Default, Serialize, Deserialize)]
1124pub struct CancelAllOrdersRequest {
1125 #[serde(skip_serializing_if = "Option::is_none")]
1127 pub symbol: Option<Ustr>,
1128 #[serde(skip_serializing_if = "Option::is_none")]
1130 pub execution_venue: Option<Ustr>,
1131}
1132
1133impl CancelAllOrdersRequest {
1134 #[must_use]
1136 pub fn new() -> Self {
1137 Self::default()
1138 }
1139
1140 #[must_use]
1142 pub fn with_symbol(mut self, symbol: Ustr) -> Self {
1143 self.symbol = Some(symbol);
1144 self
1145 }
1146
1147 #[must_use]
1149 pub fn with_venue(mut self, venue: Ustr) -> Self {
1150 self.execution_venue = Some(venue);
1151 self
1152 }
1153}
1154
1155#[derive(Clone, Debug, Serialize, Deserialize)]
1160pub struct AxCancelAllOrdersResponse {}
1161
1162#[cfg(test)]
1163mod tests {
1164 use rstest::rstest;
1165
1166 use super::*;
1167
1168 #[rstest]
1169 fn test_deserialize_authenticate_response() {
1170 let json = include_str!("../../test_data/http_authenticate.json");
1171 let response: AxAuthenticateResponse = serde_json::from_str(json).unwrap();
1172 assert!(response.token.starts_with("test-token"));
1173 }
1174
1175 #[rstest]
1176 fn test_deserialize_whoami_response() {
1177 let json = include_str!("../../test_data/http_get_whoami.json");
1178 let response: AxWhoAmI = serde_json::from_str(json).unwrap();
1179 assert_eq!(response.username, "test_user");
1180 assert!(response.enabled_2fa);
1181 }
1182
1183 #[rstest]
1184 fn test_deserialize_instruments_response() {
1185 let json = include_str!("../../test_data/http_get_instruments.json");
1186 let response: AxInstrumentsResponse = serde_json::from_str(json).unwrap();
1187 assert_eq!(response.instruments.len(), 3);
1188 assert_eq!(response.instruments[0].symbol, "EURUSD-PERP");
1189 }
1190
1191 #[rstest]
1192 fn test_deserialize_balances_response() {
1193 let json = include_str!("../../test_data/http_get_balances.json");
1194 let response: AxBalancesResponse = serde_json::from_str(json).unwrap();
1195 assert_eq!(response.balances.len(), 3);
1196 assert_eq!(response.balances[0].symbol, "USD");
1197 }
1198
1199 #[rstest]
1200 fn test_deserialize_positions_response() {
1201 let json = include_str!("../../test_data/http_get_positions.json");
1202 let response: AxPositionsResponse = serde_json::from_str(json).unwrap();
1203 assert_eq!(response.positions.len(), 2);
1204 assert_eq!(response.positions[0].symbol, "BTC-PERP");
1205 assert_eq!(response.positions[1].signed_quantity, -5);
1206 }
1207
1208 #[rstest]
1209 fn test_deserialize_tickers_response() {
1210 let json = include_str!("../../test_data/http_get_tickers.json");
1211 let response: AxTickersResponse = serde_json::from_str(json).unwrap();
1212 assert_eq!(response.tickers.len(), 3);
1213 assert_eq!(response.tickers[0].symbol, "EURUSD-PERP");
1214 assert!(response.tickers[0].bid.is_some());
1215 assert!(response.tickers[2].bid.is_none());
1216 }
1217
1218 #[rstest]
1219 fn test_deserialize_funding_rates_response() {
1220 let json = include_str!("../../test_data/http_get_funding_rates.json");
1221 let response: AxFundingRatesResponse = serde_json::from_str(json).unwrap();
1222 assert_eq!(response.funding_rates.len(), 2);
1223 assert_eq!(response.funding_rates[0].symbol, "JPYUSD-PERP");
1224 }
1225
1226 #[rstest]
1227 fn test_deserialize_open_orders_response() {
1228 let json = include_str!("../../test_data/http_get_open_orders.json");
1229 let response: AxOpenOrdersResponse = serde_json::from_str(json).unwrap();
1230 assert_eq!(response.orders.len(), 2);
1231 assert_eq!(response.orders[0].oid, "O-01ARZ3NDEKTSV4RRFFQ69G5FAV");
1232 assert_eq!(response.orders[0].d, AxOrderSide::Buy);
1233 assert_eq!(response.orders[0].o, AxOrderStatus::Accepted);
1234 assert_eq!(response.orders[1].xq, 300);
1235 }
1236
1237 #[rstest]
1238 fn test_deserialize_fills_response() {
1239 let json = include_str!("../../test_data/http_get_fills.json");
1240 let response: AxFillsResponse = serde_json::from_str(json).unwrap();
1241 assert_eq!(response.fills.len(), 2);
1242 assert_eq!(response.fills[0].side, AxOrderSide::Buy);
1243 assert!(response.fills[0].is_taker);
1244 assert!(!response.fills[1].is_taker);
1245 }
1246
1247 #[rstest]
1248 fn test_deserialize_candles_response() {
1249 let json = include_str!("../../test_data/http_get_candles.json");
1250 let response: AxCandlesResponse = serde_json::from_str(json).unwrap();
1251 assert_eq!(response.candles.len(), 2);
1252 assert_eq!(response.candles[0].symbol, "EURUSD-PERP");
1253 assert_eq!(response.candles[0].width, AxCandleWidth::Minutes1);
1254 }
1255
1256 #[rstest]
1257 fn test_deserialize_candle_response() {
1258 let json = include_str!("../../test_data/http_get_candle.json");
1259 let response: AxCandleResponse = serde_json::from_str(json).unwrap();
1260 assert_eq!(response.candle.symbol, "EURUSD-PERP");
1261 assert_eq!(response.candle.width, AxCandleWidth::Minutes1);
1262 }
1263
1264 #[rstest]
1265 fn test_deserialize_risk_snapshot_response() {
1266 let json = include_str!("../../test_data/http_get_risk_snapshot.json");
1267 let response: AxRiskSnapshotResponse = serde_json::from_str(json).unwrap();
1268 assert_eq!(
1269 response.risk_snapshot.user_id,
1270 "3c90c3cc-0d44-4b50-8888-8dd25736052a"
1271 );
1272 assert_eq!(response.risk_snapshot.per_symbol.len(), 2);
1273 assert!(
1274 response
1275 .risk_snapshot
1276 .per_symbol
1277 .contains_key("EURUSD-PERP")
1278 );
1279 }
1280
1281 #[rstest]
1282 fn test_deserialize_transactions_response() {
1283 let json = include_str!("../../test_data/http_get_transactions.json");
1284 let response: AxTransactionsResponse = serde_json::from_str(json).unwrap();
1285 assert_eq!(response.transactions.len(), 2);
1286 assert_eq!(response.transactions[0].transaction_type, "deposit");
1287 assert!(response.transactions[1].reference_id.is_none());
1288 }
1289
1290 #[rstest]
1291 fn test_deserialize_preview_aggressive_limit_order_response() {
1292 let json = include_str!("../../test_data/http_preview_aggressive_limit_order.json");
1293 let response: AxPreviewAggressiveLimitOrderResponse = serde_json::from_str(json).unwrap();
1294 assert_eq!(response.filled_quantity, 1000);
1295 assert_eq!(response.remaining_quantity, 0);
1296 assert!(response.limit_price.is_some());
1297 assert!(response.vwap.is_some());
1298 }
1299
1300 #[rstest]
1301 fn test_deserialize_place_order_response() {
1302 let json = include_str!("../../test_data/http_place_order.json");
1303 let response: AxPlaceOrderResponse = serde_json::from_str(json).unwrap();
1304 assert_eq!(response.oid, "O-01ARZ3NDEKTSV4RRFFQ69G5FAV");
1305 }
1306
1307 #[rstest]
1308 fn test_deserialize_cancel_order_response() {
1309 let json = include_str!("../../test_data/http_cancel_order.json");
1310 let response: AxCancelOrderResponse = serde_json::from_str(json).unwrap();
1311 assert!(response.cxl_rx);
1312 }
1313
1314 #[rstest]
1315 fn test_deserialize_cancel_all_orders_response() {
1316 let json = include_str!("../../test_data/http_cancel_all_orders.json");
1317 let _response: AxCancelAllOrdersResponse = serde_json::from_str(json).unwrap();
1318 }
1319
1320 #[rstest]
1321 fn test_deserialize_trades_response() {
1322 let json = include_str!("../../test_data/http_get_trades.json");
1323 let response: AxTradesResponse = serde_json::from_str(json).unwrap();
1324 assert_eq!(response.trades.len(), 2);
1325 assert_eq!(response.trades[0].s, "EURUSD-PERP");
1326 assert_eq!(response.trades[0].d, AxOrderSide::Buy);
1327 assert_eq!(response.trades[0].q, 100);
1328 assert_eq!(response.trades[1].d, AxOrderSide::Sell);
1329 }
1330
1331 #[rstest]
1332 fn test_deserialize_book_response() {
1333 let json = include_str!("../../test_data/http_get_book.json");
1334 let response: AxBookResponse = serde_json::from_str(json).unwrap();
1335 assert_eq!(response.book.s, "EURUSD-PERP");
1336 assert_eq!(response.book.b.len(), 3);
1337 assert_eq!(response.book.a.len(), 3);
1338 assert_eq!(response.book.b[0].q, 500);
1339 assert_eq!(response.book.a[0].q, 400);
1340 }
1341
1342 #[rstest]
1343 fn test_deserialize_order_status_query_response() {
1344 let json = include_str!("../../test_data/http_get_order_status.json");
1345 let response: AxOrderStatusQueryResponse = serde_json::from_str(json).unwrap();
1346 assert_eq!(response.status.symbol, "EURUSD-PERP");
1347 assert_eq!(response.status.order_id, "O-01ARZ3NDEKTSV4RRFFQ69G5FAV");
1348 assert_eq!(response.status.state, AxOrderStatus::PartiallyFilled);
1349 assert_eq!(response.status.clord_id, Some(12345));
1350 assert_eq!(response.status.filled_quantity, Some(300));
1351 assert_eq!(response.status.remaining_quantity, Some(700));
1352 }
1353
1354 #[rstest]
1355 fn test_deserialize_orders_response() {
1356 let json = include_str!("../../test_data/http_get_orders.json");
1357 let response: AxOrdersResponse = serde_json::from_str(json).unwrap();
1358 assert_eq!(response.orders.len(), 2);
1359 assert_eq!(response.total_count, 2);
1360 assert_eq!(response.orders[0].o, AxOrderStatus::PartiallyFilled);
1361 assert_eq!(response.orders[0].xq, 300);
1362 assert_eq!(response.orders[1].o, AxOrderStatus::Filled);
1363 assert_eq!(response.orders[1].d, AxOrderSide::Sell);
1364 }
1365
1366 #[rstest]
1367 fn test_deserialize_initial_margin_requirement_response() {
1368 let json = include_str!("../../test_data/http_initial_margin_requirement.json");
1369 let response: AxInitialMarginRequirementResponse = serde_json::from_str(json).unwrap();
1370 assert_eq!(response.im, Decimal::new(125050, 2));
1371 }
1372
1373 #[rstest]
1374 fn test_deserialize_replace_order_response() {
1375 let json = include_str!("../../test_data/http_replace_order.json");
1376 let response: AxReplaceOrderResponse = serde_json::from_str(json).unwrap();
1377 assert_eq!(response.oid, "O-01ARZ3NDEKTSV4RRFFQ69G5NEW");
1378 }
1379
1380 #[rstest]
1381 fn test_replace_order_request_serialization() {
1382 let request = ReplaceOrderRequest::new("O-01ARZ3NDEKTSV4RRFFQ69G5FAV")
1383 .with_price(Decimal::new(10550, 4))
1384 .with_quantity(200);
1385
1386 let json = serde_json::to_value(&request).unwrap();
1387 assert_eq!(json["oid"], "O-01ARZ3NDEKTSV4RRFFQ69G5FAV");
1388 assert_eq!(json["p"], "1.0550");
1389 assert_eq!(json["q"], 200);
1390 assert!(json.get("po").is_none());
1391 assert!(json.get("tif").is_none());
1392 assert!(json.get("trigger_price").is_none());
1393 }
1394
1395 #[rstest]
1396 fn test_replace_order_request_minimal() {
1397 let request = ReplaceOrderRequest::new("O-TEST");
1398 let json = serde_json::to_value(&request).unwrap();
1399 assert_eq!(json["oid"], "O-TEST");
1400 assert!(json.get("p").is_none());
1401 assert!(json.get("q").is_none());
1402 }
1403
1404 #[rstest]
1405 fn test_replace_order_request_with_trigger_price() {
1406 let request = ReplaceOrderRequest::new("O-STOP").with_trigger_price(Decimal::new(49000, 0));
1407 let json = serde_json::to_value(&request).unwrap();
1408 assert_eq!(json["oid"], "O-STOP");
1409 assert_eq!(json["trigger_price"], "49000");
1410 assert!(json.get("p").is_none());
1411 assert!(json.get("q").is_none());
1412 }
1413}