kraken_async_rs/
response_types.rs

1//! REST response types
2use crate::clients::errors::ClientError;
3use crate::crypto::secrets::Token;
4use crate::request_types::TriggerType;
5use rust_decimal::Decimal;
6use serde::{Deserialize, Serialize};
7use serde_this_or_that::as_i64;
8use serde_tuple::Deserialize_tuple;
9use serde_with::formats::CommaSeparator;
10use serde_with::serde_as;
11use serde_with::StringWithSeparator;
12use std::collections::HashMap;
13use std::fmt::{Debug, Display, Formatter};
14use std::str::FromStr;
15
16/// A user's level of KYC verification with Kraken
17///
18/// Determines rate limits for the user, as well as deposit, withdrawal, and banking limits.
19#[derive(Debug, Deserialize, Clone, PartialEq, Eq, Copy)]
20#[serde(rename_all = "snake_case")]
21pub enum VerificationTier {
22    Intermediate,
23    Pro,
24}
25
26/// Status of the exchange
27#[derive(Debug, Deserialize, Clone, PartialEq, Eq, Copy)]
28#[serde(rename_all = "snake_case")]
29pub enum SystemStatus {
30    Online,
31    Maintenance,
32    CancelOnly,
33    PostOnly,
34}
35
36/// Status of a given asset pair for trading (e.g. BTC-USD, ATOM-USD)
37#[derive(Debug, Deserialize, Clone, PartialEq, Eq, Copy)]
38#[serde(rename_all = "snake_case")]
39pub enum TradableAssetStatus {
40    Online,
41    CancelOnly,
42    PostOnly,
43    LimitOnly,
44    ReduceOnly,
45}
46
47/// Status for an asset (e.g. ETH, ATOM, USDC)
48#[derive(Debug, Deserialize, Clone, PartialEq, Eq, Copy)]
49#[serde(rename_all = "snake_case")]
50pub enum AssetStatus {
51    Enabled,
52    DepositOnly,
53    WithdrawalOnly,
54    FundingTemporarilyDisabled,
55}
56
57/// Order side
58#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Copy)]
59#[serde(rename_all = "lowercase")]
60pub enum BuySell {
61    Buy,
62    Sell,
63}
64
65impl Display for BuySell {
66    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
67        match self {
68            BuySell::Buy => write!(f, "buy"),
69            BuySell::Sell => write!(f, "sell"),
70        }
71    }
72}
73
74/// Flags that can be applied to order requests.
75#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Copy)]
76pub enum OrderFlag {
77    /// Post only order will be rejected if it would pay maker fees
78    #[serde(rename = "post")]
79    Post,
80    /// Fees should be taken in the base currency (default for sell)
81    #[serde(rename = "fcib")]
82    FeesInBase,
83    /// Fees should be taken in the quote currency (default for buy)
84    #[serde(rename = "fciq")]
85    FeesInQuote,
86    /// Disable extreme slippage protection for this order
87    #[serde(rename = "nompp")]
88    NoMarketPriceProtection,
89    /// For market orders, give order volume in quote currency
90    #[serde(rename = "viqc")]
91    OrderVolumeInQuote,
92}
93
94impl Display for OrderFlag {
95    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
96        match self {
97            OrderFlag::Post => write!(f, "post"),
98            OrderFlag::FeesInBase => write!(f, "fcib"),
99            OrderFlag::FeesInQuote => write!(f, "fciq"),
100            OrderFlag::NoMarketPriceProtection => write!(f, "nompp"),
101            OrderFlag::OrderVolumeInQuote => write!(f, "viqc"),
102        }
103    }
104}
105
106impl FromStr for OrderFlag {
107    type Err = ClientError;
108
109    fn from_str(s: &str) -> Result<Self, Self::Err> {
110        match s {
111            "post" => Ok(OrderFlag::Post),
112            "fcib" => Ok(OrderFlag::FeesInBase),
113            "fciq" => Ok(OrderFlag::FeesInQuote),
114            "nompp" => Ok(OrderFlag::NoMarketPriceProtection),
115            "viqc" => Ok(OrderFlag::OrderVolumeInQuote),
116            _ => Err(ClientError::Parse("Failed to parse order flag")),
117        }
118    }
119}
120
121/// Whether a given [BidAsk] is a `Bid` or an `Ask`
122#[derive(Debug, Deserialize, Clone, PartialEq, Eq, Copy)]
123#[serde(rename_all = "lowercase")]
124pub enum BidOrAsk {
125    Bid,
126    Ask,
127}
128
129/// Single-character enum for buy and sell
130#[derive(Debug, Deserialize, Clone, PartialEq, Eq, Copy)]
131pub enum BuySellChar {
132    #[serde(rename(deserialize = "b"))]
133    Buy,
134    #[serde(rename(deserialize = "s"))]
135    Sell,
136}
137
138/// Single-character enum for market and limit orders
139#[derive(Debug, Deserialize, Clone, PartialEq, Eq, Copy)]
140pub enum MarketLimitChar {
141    #[serde(rename(deserialize = "m"))]
142    Market,
143    #[serde(rename(deserialize = "l"))]
144    Limit,
145}
146
147/// Order type, e.g. `Market`, `Limit`, `StopLossLimit`
148#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Copy)]
149#[serde(rename_all = "kebab-case")]
150pub enum OrderType {
151    Limit,
152    Market,
153    Iceberg, // TODO: maybe not available on WSS AddOrder?
154    StopLoss,
155    StopLossLimit,
156    TakeProfit,
157    TakeProfitLimit,
158    TrailingStop,
159    TrailingStopLimit,
160    SettlePosition,
161}
162
163/// Trade type, separate from [OrderType] due to different serialization semantics
164#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, Copy)]
165pub enum TradeType {
166    #[serde(rename = "market")]
167    Market,
168    #[serde(rename = "limit")]
169    Limit,
170    #[serde(rename = "stop loss")]
171    StopLoss,
172    #[serde(rename = "stop limit")]
173    StopLimit,
174    #[serde(rename = "take profit")]
175    TakeProfit,
176    #[serde(rename = "stop loss limit")]
177    StopLossLimit,
178    #[serde(rename = "take profit limit")]
179    TakeProfitLimit,
180    #[serde(rename = "settle position")]
181    SettlePosition,
182}
183
184impl Display for OrderType {
185    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
186        match self {
187            OrderType::Market => write!(f, "market"),
188            OrderType::Limit => write!(f, "limit"),
189            OrderType::StopLoss => write!(f, "stop-loss"),
190            OrderType::TakeProfit => write!(f, "take-profit"),
191            OrderType::StopLossLimit => write!(f, "stop-loss-limit"),
192            OrderType::TakeProfitLimit => write!(f, "take-profit-limit"),
193            OrderType::SettlePosition => write!(f, "settle-position"),
194            OrderType::Iceberg => write!(f, "iceberg"),
195            OrderType::TrailingStop => write!(f, "trailing-stop"),
196            OrderType::TrailingStopLimit => write!(f, "trailing-stop-limit"),
197        }
198    }
199}
200
201/// Status of an order
202#[derive(Debug, Deserialize, Clone, PartialEq, Eq, Copy)]
203#[serde(rename_all = "lowercase")]
204pub enum OrderStatus {
205    Pending,
206    Open,
207    Closed,
208    Canceled,
209    Expired,
210}
211
212/// Status of an order
213#[derive(Debug, Deserialize, Clone, PartialEq, Eq, Copy)]
214#[serde(rename_all = "snake_case")]
215pub enum OrderStatusV2 {
216    PendingNew,
217    New,
218    PartiallyFilled,
219    Filled,
220    Canceled,
221    Expired,
222}
223
224/// Status of a position
225#[derive(Debug, Deserialize, Clone, PartialEq, Eq, Copy)]
226#[serde(rename_all = "lowercase")]
227pub enum PositionStatus {
228    Open,
229    Closed,
230}
231
232/// Status of a position
233#[derive(Debug, Deserialize, Clone, PartialEq, Eq, Copy)]
234#[serde(rename_all = "lowercase")]
235pub enum PositionStatusV2 {
236    Opened,
237    Closing,
238    Closed,
239}
240
241/// Type of ledger entry in user's ledger
242#[derive(Debug, Deserialize, Clone, PartialEq, Eq, Copy)]
243#[serde(rename_all = "lowercase")]
244pub enum LedgerEntryType {
245    None,
246    Trade,
247    Credit,
248    Deposit,
249    Withdrawal,
250    Transfer,
251    Margin,
252    Rollover,
253    Spend,
254    Receive,
255    Settled,
256    Adjustment,
257    Staking,
258    Sale,
259    Dividend,
260    NftRebate,
261    NftTrade,
262    NftCreatorFee,
263    CustodyTransfer,
264}
265
266impl Display for LedgerEntryType {
267    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
268        match self {
269            LedgerEntryType::None => write!(f, "none"),
270            LedgerEntryType::Trade => write!(f, "trade"),
271            LedgerEntryType::Credit => write!(f, "credit"),
272            LedgerEntryType::Deposit => write!(f, "deposit"),
273            LedgerEntryType::Withdrawal => write!(f, "withdrawal"),
274            LedgerEntryType::Transfer => write!(f, "transfer"),
275            LedgerEntryType::Margin => write!(f, "margin"),
276            LedgerEntryType::Rollover => write!(f, "rollover"),
277            LedgerEntryType::Spend => write!(f, "spend"),
278            LedgerEntryType::Receive => write!(f, "receive"),
279            LedgerEntryType::Settled => write!(f, "settled"),
280            LedgerEntryType::Adjustment => write!(f, "adjustment"),
281            LedgerEntryType::Staking => write!(f, "staking"),
282            LedgerEntryType::Sale => write!(f, "sale"),
283            LedgerEntryType::Dividend => write!(f, "dividend"),
284            LedgerEntryType::NftRebate => write!(f, "nftrebate"),
285            LedgerEntryType::NftTrade => write!(f, "nfttrade"),
286            LedgerEntryType::NftCreatorFee => write!(f, "nftcreatorfee"),
287            LedgerEntryType::CustodyTransfer => write!(f, "custodytransfer"),
288        }
289    }
290}
291
292/// Status of a requested export report
293#[derive(Debug, Deserialize, Clone, PartialEq, Eq, Copy)]
294pub enum ExportReportStatusType {
295    Queued,
296    Processing,
297    Processed,
298}
299
300impl Display for ExportReportStatusType {
301    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
302        match self {
303            ExportReportStatusType::Queued => write!(f, "Queued"),
304            ExportReportStatusType::Processing => write!(f, "Processing"),
305            ExportReportStatusType::Processed => write!(f, "Processed"),
306        }
307    }
308}
309
310/// Status of an edit requested for an order
311#[derive(Debug, Deserialize, Clone, PartialEq, Eq, Copy)]
312#[serde(rename_all = "lowercase")]
313pub enum OrderEditStatus {
314    Ok,
315    Err,
316}
317
318/// Wrapper type for odd responses that contain either a `bool` or a `String`
319///
320/// For example, the limit of a deposit method can be `false` for no limit, or a String value of the
321/// numeric limit.
322#[derive(Debug, Deserialize, Clone, PartialEq, Eq)]
323#[serde(untagged)]
324pub enum BoolOrString {
325    Bool(bool),
326    String(String),
327}
328
329/// Additional status properties about a deposit or withdrawal
330#[derive(Debug, Deserialize, Clone, Copy, PartialEq, Eq)]
331#[serde(rename_all = "kebab-case")]
332pub enum StatusProp {
333    CancelPending,
334    Canceled,
335    CancelDenied,
336    Return,
337    #[serde(rename = "onhold")]
338    OnHold,
339}
340
341/// Status of a requested transfer
342#[derive(Debug, Deserialize, Clone, Copy, PartialEq, Eq)]
343pub enum TransferStatus {
344    Initial,
345    Pending,
346    Settled,
347    Success,
348    Failure,
349}
350
351/// Status of a transfer between accounts
352#[derive(Debug, Deserialize, Clone, Copy, PartialEq, Eq)]
353#[serde(rename_all = "lowercase")]
354pub enum AccountTransferStatus {
355    Pending,
356    Complete,
357}
358
359/// Wrapper type for loose typing of allocation/earn fees
360#[derive(Debug, Deserialize, PartialEq, Clone, Copy)]
361#[serde(untagged)]
362pub enum EarnFee {
363    Decimal(Decimal),
364    Integer(i64),
365    Float(f64),
366}
367
368/// Source of yield for a given earn strategy
369#[derive(Debug, Deserialize, PartialEq, Eq, Clone, Copy)]
370#[serde(rename_all = "snake_case")]
371pub enum YieldSourceType {
372    Staking,
373    OffChain,
374    OptInRewards,
375}
376
377/// Type of compounding for a given strategy
378#[derive(Debug, Deserialize, PartialEq, Eq, Clone, Copy)]
379#[serde(rename_all = "lowercase")]
380pub enum AutoCompoundType {
381    Enabled,
382    Disabled,
383    Optional,
384}
385
386/// Type of asset lock-up for a given earn strategy
387#[derive(Debug, Deserialize, PartialEq, Eq, Clone, Copy)]
388#[serde(rename_all = "lowercase")]
389pub enum LockType {
390    Flex,
391    Bonded,
392    Instant,
393}
394
395/// The type of Order Amend
396#[derive(Debug, Deserialize, Clone, PartialEq, Eq, Copy)]
397#[serde(rename_all = "lowercase")]
398pub enum AmendType {
399    Original,
400    User,
401    Restated,
402}
403
404/// Kraken server time given in both unix timestamp and RFC1123
405#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
406pub struct SystemTime {
407    #[serde(rename = "unixtime")]
408    pub unix_time: i64,
409    pub rfc1123: String,
410}
411
412/// Kraken server status, including an RFC3339 timestamp.
413#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
414pub struct SystemStatusInfo {
415    pub status: SystemStatus,
416    pub timestamp: String,
417}
418
419/// Asset details (e.g. for ETH, USDC, BTC, etc)
420#[derive(Debug, Deserialize, PartialEq, Clone)]
421pub struct AssetInfo {
422    #[serde(rename = "aclass")]
423    pub asset_class: String,
424    #[serde(rename = "altname")]
425    pub alt_name: String,
426    pub decimals: i64,
427    pub display_decimals: i64,
428    pub collateral_value: Option<f64>,
429    pub status: AssetStatus,
430}
431
432/// Tiered fee description
433#[derive(Debug, Deserialize_tuple, PartialEq, Clone)]
434pub struct FeeByVolume {
435    pub volume: f64,
436    pub fee: f64,
437}
438
439/// Trading pair details, including all necessary details for formatting orders
440#[derive(Debug, Deserialize, PartialEq, Clone)]
441pub struct TradableAssetPair {
442    #[serde(rename = "altname")]
443    pub alt_name: String,
444    #[serde(rename = "wsname")]
445    pub ws_name: String,
446    #[serde(rename = "aclass_base")]
447    pub asset_class_base: String,
448    pub base: String,
449    #[serde(rename = "aclass_quote")]
450    pub asset_class_quote: String,
451    pub quote: String,
452    pub lot: String,
453    pub cost_decimals: i64,
454    pub pair_decimals: i64,
455    pub lot_decimals: i64,
456    pub lot_multiplier: i64,
457    pub leverage_buy: Vec<i64>,
458    pub leverage_sell: Vec<i64>,
459    pub fees: Vec<FeeByVolume>,
460    pub fees_maker: Vec<FeeByVolume>,
461    pub fee_volume_currency: String,
462    pub margin_call: i64,
463    pub margin_stop: i64,
464    #[serde(rename = "ordermin")]
465    pub order_min: Decimal,
466    #[serde(rename = "costmin")]
467    pub cost_min: Decimal,
468    pub tick_size: Decimal,
469    pub status: TradableAssetStatus,
470    pub long_position_limit: Option<i64>,
471    pub short_position_limit: Option<i64>,
472}
473
474/// Ticker containing trade count data for the last 24 hours
475#[derive(Debug, Deserialize_tuple, PartialEq, Clone)]
476pub struct TickerTrades {
477    pub today: i64,
478    pub last_24_h: i64,
479}
480
481/// Ticker helper type to serve differently typed data for the last 24 hours.
482#[derive(Debug, Deserialize_tuple, PartialEq, Clone)]
483pub struct TickerDecimal {
484    pub today: Decimal,
485    pub last_24_h: Decimal,
486}
487
488/// Best bid or ask
489///
490/// Separate type needed for varying data format from REST API.
491#[derive(Debug, Deserialize_tuple, PartialEq, Clone)]
492pub struct RestTickerBidAsk {
493    pub price: Decimal,
494    pub whole_lot_volume: Decimal,
495    pub lot_volume: Decimal,
496}
497
498/// Best bid or ask
499///
500/// Separate type needed for different format from WSS API.
501#[derive(Debug, Deserialize_tuple, PartialEq, Clone)]
502pub struct TickerBidAsk {
503    pub price: Decimal,
504    #[serde(deserialize_with = "as_i64")]
505    pub whole_lot_volume: i64,
506    pub lot_volume: Decimal,
507}
508
509/// Price and volume for the most recent trade
510#[derive(Debug, Deserialize_tuple, PartialEq, Clone)]
511pub struct LastTrade {
512    pub price: Decimal,
513    pub volume: Decimal,
514}
515
516/// Complete ticker information for an asset
517#[derive(Debug, Deserialize, PartialEq, Clone)]
518pub struct RestTickerInfo {
519    #[serde(rename(deserialize = "a"))]
520    pub asks: TickerBidAsk,
521    #[serde(rename(deserialize = "b"))]
522    pub bids: TickerBidAsk,
523    #[serde(rename(deserialize = "c"))]
524    pub closed: LastTrade,
525    #[serde(rename(deserialize = "v"))]
526    pub volume: TickerDecimal,
527    #[serde(rename(deserialize = "p"))]
528    pub vwap: TickerDecimal,
529    #[serde(rename(deserialize = "t"))]
530    pub trades: TickerTrades,
531    #[serde(rename(deserialize = "l"))]
532    pub low: TickerDecimal,
533    #[serde(rename(deserialize = "h"))]
534    pub high: TickerDecimal,
535    #[serde(rename(deserialize = "o"))]
536    pub open: Decimal,
537}
538
539/// Candlestick data for the given interval
540#[derive(Debug, Deserialize_tuple, PartialEq, Clone)]
541pub struct OHLC {
542    pub time: i64,
543    pub open: Decimal,
544    pub high: Decimal,
545    pub low: Decimal,
546    pub close: Decimal,
547    pub vwap: Decimal,
548    pub volume: Decimal,
549    pub count: i64,
550}
551
552/// OHLC data by pair
553///
554/// Includes `last` value for use in incremental updates
555#[derive(Debug, Deserialize, PartialEq, Clone)]
556pub struct OhlcResponse {
557    pub last: i64,
558    #[serde(flatten)]
559    pub ohlc: HashMap<String, Vec<OHLC>>,
560}
561
562/// Bid or Ask
563///
564/// Identical data for bids and asks, only context determines if it's a bid or ask.
565#[derive(Debug, Deserialize_tuple, PartialEq, Clone)]
566pub struct BidAsk {
567    pub price: Decimal,
568    pub volume: Decimal,
569    pub time: i64,
570}
571
572/// Orderbook containing some depth of bids and asks
573#[derive(Debug, Deserialize, PartialEq, Clone)]
574pub struct Orderbook {
575    pub asks: Vec<BidAsk>,
576    pub bids: Vec<BidAsk>,
577}
578
579/// A public trade
580///
581/// The model is the same regardless of if request to be consolidated by taker
582#[derive(Debug, Deserialize_tuple, PartialEq, Clone)]
583pub struct RecentTrade {
584    pub price: Decimal,
585    pub volume: Decimal,
586    pub time: f64,
587    pub buy_sell: BuySellChar,
588    pub market_limit: MarketLimitChar,
589    pub misc: String,
590    pub trade_id: i64,
591}
592
593/// Wrapper type for recent trade response
594///
595/// `last` parameter allows for pagination.
596#[serde_as]
597#[derive(Debug, Deserialize, PartialEq, Clone)]
598pub struct RecentTrades {
599    pub last: String,
600    #[serde(flatten)]
601    pub trades: HashMap<String, Vec<RecentTrade>>,
602}
603
604/// Bid-ask spread at a given time
605#[derive(Debug, Deserialize_tuple, PartialEq, Clone)]
606pub struct Spread {
607    pub time: i64,
608    pub bid: Decimal,
609    pub ask: Decimal,
610}
611
612/// Spreads for one or many assets
613///
614/// `last` parameter allows for incremental updates
615#[derive(Debug, Deserialize, PartialEq, Clone)]
616pub struct RecentSpreads {
617    pub last: i64,
618    #[serde(flatten)]
619    pub spreads: HashMap<String, Vec<Spread>>,
620}
621
622/// Convenience type for asset: amount balances
623pub type AccountBalances = HashMap<String, Decimal>;
624
625/// Convenience type for asset: extended balances
626pub type ExtendedBalances = HashMap<String, ExtendedBalance>;
627
628/// Detailed balance data, including holds and credit (if available)
629#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
630pub struct ExtendedBalance {
631    pub balance: Decimal,
632    pub hold_trade: Decimal,
633    pub credit: Option<Decimal>,
634    pub credit_used: Option<Decimal>,
635}
636
637/// Detailed margin balance data
638#[derive(Debug, Deserialize, PartialEq, Clone)]
639pub struct TradeBalances {
640    #[serde(rename(deserialize = "eb"))]
641    pub equivalent_balance: Decimal,
642    #[serde(rename(deserialize = "tb"))]
643    pub trade_balance: Decimal,
644    #[serde(rename(deserialize = "m"))]
645    pub margin: Decimal,
646    #[serde(rename(deserialize = "n"))]
647    pub net_pnl_open: Decimal,
648    #[serde(rename(deserialize = "c"))]
649    pub cost_basis_open: Decimal,
650    #[serde(rename(deserialize = "v"))]
651    pub floating_valuation: Decimal,
652    #[serde(rename(deserialize = "e"))]
653    pub equity: Decimal,
654    #[serde(rename(deserialize = "mf"))]
655    pub free_margin: Decimal,
656    #[serde(rename(deserialize = "ml"))]
657    pub margin_level: Option<Decimal>,
658    #[serde(rename(deserialize = "uv"))]
659    pub unexecuted_value: Option<Decimal>,
660}
661
662/// Details of individual order
663#[derive(Debug, Deserialize, PartialEq, Clone)]
664pub struct OrderDescription {
665    pub pair: String,
666    #[serde(rename(deserialize = "type"))]
667    pub side: BuySell,
668    #[serde(rename(deserialize = "ordertype"))]
669    pub order_type: OrderType,
670    pub price: Decimal,
671    pub price2: Decimal,
672    pub leverage: String,
673    pub order: String,
674    pub close: String,
675}
676
677/// Wrapper to map open orders by Kraken ref-id
678#[derive(Debug, Deserialize, PartialEq, Clone)]
679pub struct OpenOrders {
680    pub open: HashMap<String, Order>,
681}
682
683/// Order object for OpenOrders and QueryOrders
684#[serde_as]
685#[derive(Debug, Deserialize, PartialEq, Clone)]
686pub struct Order {
687    #[serde(rename = "refid")]
688    pub ref_id: Option<String>,
689    pub userref: Option<i64>,
690    #[serde(rename = "cl_ord_id")]
691    pub client_order_id: Option<String>,
692    pub status: OrderStatus,
693    #[serde(rename = "opentm")]
694    pub open_time: f64,
695    #[serde(rename = "starttm")]
696    pub start_time: f64,
697    #[serde(rename = "expiretm")]
698    pub expire_time: f64,
699    #[serde(rename = "closetm")]
700    pub close_time: Option<f64>,
701    pub descr: OrderDescription,
702    #[serde(rename(deserialize = "vol"))]
703    pub volume: Decimal,
704    #[serde(rename(deserialize = "vol_exec"))]
705    pub volume_executed: Decimal,
706    pub cost: Decimal,
707    pub fee: Decimal,
708    pub price: Decimal,
709    #[serde(rename = "stopprice")]
710    pub stop_price: Decimal,
711    #[serde(rename = "limitprice")]
712    pub limit_price: Decimal,
713    pub trigger: Option<TriggerType>,
714    pub margin: Option<bool>,
715    pub misc: String,
716    pub sender_sub_id: Option<String>,
717    #[serde(rename = "oflags")]
718    #[serde_as(as = "StringWithSeparator::<CommaSeparator, OrderFlag>")]
719    pub order_flags: Vec<OrderFlag>,
720    pub trades: Option<Vec<String>>,
721    pub reason: Option<String>,
722}
723
724/// Order object for closed orders
725#[serde_as]
726#[derive(Debug, Deserialize, PartialEq, Clone)]
727pub struct ClosedOrder {
728    #[serde(rename = "refid")]
729    pub ref_id: Option<String>,
730    pub userref: Option<i64>,
731    #[serde(rename = "cl_ord_id")]
732    pub client_order_id: Option<String>,
733    pub status: OrderStatus,
734    #[serde(rename = "opentm")]
735    pub open_time: f64,
736    #[serde(rename = "starttm")]
737    pub start_time: f64,
738    #[serde(rename = "expiretm")]
739    pub expire_time: f64,
740    #[serde(rename = "closetm")]
741    pub close_time: Option<f64>,
742    #[serde(rename(deserialize = "vol"))]
743    pub volume: Decimal,
744    #[serde(rename(deserialize = "vol_exec"))]
745    pub volume_executed: Decimal,
746    pub cost: Decimal,
747    pub fee: Decimal,
748    pub price: Decimal,
749    #[serde(rename = "stopprice")]
750    pub stop_price: Decimal,
751    #[serde(rename = "limitprice")]
752    pub limit_price: Decimal,
753    pub trigger: Option<TriggerType>,
754    pub margin: Option<bool>,
755    pub misc: String,
756    #[serde(rename = "oflags")]
757    #[serde_as(as = "StringWithSeparator::<CommaSeparator, OrderFlag>")]
758    pub order_flags: Vec<OrderFlag>,
759    pub trades: Option<Vec<String>>,
760    pub sender_sub_id: Option<String>,
761    pub reason: Option<String>,
762}
763
764/// Response type for mapping order ids to orders
765#[derive(Debug, Deserialize, PartialEq, Clone)]
766pub struct ClosedOrders {
767    pub closed: HashMap<String, ClosedOrder>,
768    pub count: i64,
769}
770
771/// A private trade
772///
773/// Includes fees paid, ledger entries, related order and position ids, etc.
774#[derive(Debug, Deserialize, PartialEq, Clone)]
775pub struct Trade {
776    #[serde(rename = "ordertxid")]
777    pub order_tx_id: String,
778    #[serde(rename = "postxid")]
779    pub post_xid: String,
780    pub pair: String,
781    pub time: f64,
782    #[serde(rename(deserialize = "type"))]
783    pub side: BuySell,
784    #[serde(rename = "ordertype")]
785    pub order_type: TradeType,
786    pub price: Decimal,
787    pub cost: Decimal,
788    pub fee: Decimal,
789    #[serde(rename(deserialize = "vol"))]
790    pub volume: Decimal,
791    pub margin: Decimal,
792    pub misc: String,
793    pub ledgers: Option<Vec<String>>,
794    pub maker: bool,
795}
796
797/// Mapping of trade-id: trade object
798pub type TradesInfo = HashMap<String, Trade>;
799
800/// Response type for user's trade history
801#[derive(Debug, Deserialize, PartialEq, Clone)]
802pub struct TradesHistory {
803    pub trades: TradesInfo,
804    pub count: i64,
805}
806
807#[derive(Debug, Deserialize, PartialEq, Clone)]
808pub struct OrderAmends {
809    pub amends: Vec<OrderAmend>,
810    pub count: u32,
811}
812
813#[derive(Debug, Deserialize, PartialEq, Clone)]
814pub struct OrderAmend {
815    pub amend_id: String,
816    pub amend_type: AmendType,
817    #[serde(rename = "order_qty")]
818    pub order_quantity: Decimal,
819    #[serde(rename = "display_qty")]
820    pub display_quantity: Option<Decimal>,
821    #[serde(rename = "remaining_qty")]
822    pub remaining_quantity: Decimal,
823    pub limit_price: Decimal,
824    pub trigger_price: Option<Decimal>,
825    pub reason: Option<String>,
826    pub post_only: bool,
827    pub timestamp: u64,
828}
829
830/// Mapping of position id: OpenPosition
831pub type OpenPositions = HashMap<String, OpenPosition>;
832
833/// Details of an open margin position
834#[serde_as]
835#[derive(Debug, Deserialize, PartialEq, Clone)]
836pub struct OpenPosition {
837    #[serde(rename = "ordertxid")]
838    pub order_tx_id: String,
839    #[serde(rename = "posstatus")]
840    pub pos_status: PositionStatus,
841    pub pair: String,
842    pub time: f64,
843    #[serde(rename(deserialize = "type"))]
844    pub side: BuySell,
845    #[serde(rename = "ordertype")]
846    pub order_type: OrderType,
847    pub cost: Decimal,
848    pub fee: Decimal,
849    #[serde(rename(deserialize = "vol"))]
850    pub volume: Decimal,
851    #[serde(rename(deserialize = "vol_closed"))]
852    pub volume_closed: Decimal,
853    pub margin: Decimal,
854    pub value: Option<Decimal>,
855    pub net: Option<Decimal>,
856    pub terms: String,
857    #[serde(rename = "rollovertm")]
858    pub rollover_time: String,
859    pub misc: String,
860    #[serde(rename = "oflags")]
861    #[serde_as(as = "StringWithSeparator::<CommaSeparator, OrderFlag>")]
862    pub order_flags: Vec<OrderFlag>,
863}
864
865/// Entry in the user's ledger
866#[derive(Debug, Deserialize, PartialEq, Clone)]
867pub struct LedgerEntry {
868    #[serde(rename = "refid")]
869    pub ref_id: String,
870    pub time: f64,
871    #[serde(rename(deserialize = "type"))]
872    pub entry_type: LedgerEntryType,
873    pub subtype: String,
874    #[serde(rename = "aclass")]
875    pub asset_class: String,
876    pub asset: String,
877    pub amount: Decimal,
878    pub fee: Decimal,
879    pub balance: Decimal,
880}
881
882/// Mapping of ledger id: ledger entry
883pub type QueryLedgerInfo = HashMap<String, LedgerEntry>;
884
885/// Response type for Ledgers and QueryLedgers
886#[derive(Debug, Deserialize, PartialEq, Clone)]
887pub struct LedgerInfo {
888    pub ledger: QueryLedgerInfo,
889    pub count: i64,
890}
891
892/// Description of fee tier
893#[derive(Debug, Deserialize, PartialEq, Clone)]
894pub struct Fees {
895    pub fee: Decimal,
896    #[serde(rename = "minfee")]
897    pub min_fee: Decimal,
898    #[serde(rename = "maxfee")]
899    pub max_fee: Decimal,
900    #[serde(rename = "nextfee")]
901    pub next_fee: Option<Decimal>,
902    #[serde(rename = "nextvolume")]
903    pub next_volume: Option<Decimal>,
904    #[serde(rename = "tiervolume")]
905    pub tier_volume: Option<Decimal>,
906}
907
908/// Response type for TradeVolume
909///
910/// In the case of maker-taker fees, `fees` maps trading pairs to taker fees. Otherwise, it
911/// represents fees more broadly.
912#[derive(Debug, Deserialize, PartialEq, Clone)]
913pub struct TradeVolume {
914    pub currency: String,
915    pub volume: Decimal,
916    pub fees: Option<HashMap<String, Fees>>,
917    pub fees_maker: Option<HashMap<String, Fees>>,
918}
919
920/// Response type for ExportReport
921#[derive(Debug, Deserialize, PartialEq, Clone)]
922pub struct ExportReport {
923    pub id: String,
924}
925
926/// Description of an export report
927#[derive(Debug, Deserialize, PartialEq, Clone)]
928pub struct ExportReportStatus {
929    pub id: String,
930    pub descr: String,
931    pub format: String,
932    pub report: String,
933    pub subtype: String,
934    pub status: ExportReportStatusType,
935    pub fields: String,
936    #[serde(rename = "createdtm")]
937    pub created_time: String,
938    #[serde(rename = "starttm")]
939    pub start_time: String,
940    #[serde(rename = "completedtm")]
941    pub completed_time: String,
942    #[serde(rename = "datastarttm")]
943    pub data_start_time: String,
944    #[serde(rename = "dataendtm")]
945    pub data_end_time: String,
946    pub asset: String,
947}
948
949/// Response type for deleting an export report
950#[derive(Debug, Deserialize, PartialEq, Clone)]
951pub struct DeleteExportReport {
952    pub delete: Option<bool>,
953    pub cancel: Option<bool>,
954}
955
956/// English description of an added order and closing order instruction (if given)
957///
958/// Such as "buy 5.00000000 USDCUSD @ limit 1.0000"
959#[derive(Debug, Deserialize, PartialEq, Clone)]
960pub struct AddOrderDescription {
961    pub order: String,
962    pub close: Option<String>,
963}
964
965/// Response type for AddOrder
966#[derive(Debug, Deserialize, PartialEq, Clone)]
967pub struct AddOrder {
968    #[serde(rename = "txid")]
969    pub tx_id: Vec<String>,
970    pub descr: AddOrderDescription,
971    pub error: Option<String>,
972}
973
974/// Description of an added batch order, including potential error value.
975#[derive(Debug, Deserialize, PartialEq, Clone)]
976pub struct BatchedOrder {
977    #[serde(rename = "txid")]
978    pub tx_id: String,
979    pub descr: AddOrderDescription,
980    pub error: Option<String>,
981}
982
983/// Response type for AddOrderBatch
984#[derive(Debug, Deserialize, PartialEq, Clone)]
985pub struct AddOrderBatch {
986    pub orders: Vec<BatchedOrder>,
987}
988
989#[derive(Debug, Deserialize, PartialEq, Clone)]
990pub struct AmendOrder {
991    pub amend_id: String,
992}
993
994/// Response type for an edited order
995#[derive(Debug, Deserialize, PartialEq, Clone)]
996pub struct OrderEdit {
997    pub status: OrderEditStatus,
998    #[serde(rename = "txid")]
999    pub tx_id: String,
1000    #[serde(rename = "originaltxid")]
1001    pub original_tx_id: String,
1002    pub volume: Decimal,
1003    pub price: Decimal,
1004    pub price2: Option<Decimal>,
1005    pub orders_cancelled: i64,
1006    pub descr: AddOrderDescription,
1007}
1008
1009/// Response for CancelOrder
1010#[derive(Debug, Deserialize, PartialEq, Clone)]
1011pub struct CancelOrder {
1012    pub count: i64,
1013    pub pending: Option<bool>,
1014}
1015
1016/// Response for CancelAllOrdersAfter
1017#[derive(Debug, Deserialize, PartialEq, Clone)]
1018#[serde(rename_all = "camelCase")]
1019pub struct CancelAllOrdersAfter {
1020    pub current_time: String,
1021    pub trigger_time: String,
1022}
1023
1024/// Description of a deposit method
1025#[derive(Debug, Deserialize, PartialEq, Clone)]
1026#[serde(rename_all = "kebab-case")]
1027pub struct DepositMethod {
1028    pub method: String,
1029    pub limit: BoolOrString,
1030    pub fee: Option<Decimal>,
1031    pub address_setup_fee: Option<Decimal>,
1032    pub gen_address: Option<bool>,
1033    pub minimum: Decimal,
1034}
1035
1036/// Description of a withdrawal method
1037#[derive(Debug, Deserialize, PartialEq, Clone)]
1038pub struct WithdrawMethod {
1039    pub asset: String,
1040    pub method: String,
1041    pub network: Option<String>,
1042    pub minimum: Decimal,
1043}
1044
1045/// Description of a deposit address
1046#[derive(Debug, Deserialize, PartialEq, Clone)]
1047pub struct DepositAddress {
1048    pub address: String,
1049    #[serde(rename = "expiretm")]
1050    pub expire_time: String,
1051    pub new: Option<bool>,
1052    pub memo: Option<String>,
1053    pub tag: Option<String>,
1054}
1055
1056/// Description of a withdrawal method
1057#[derive(Debug, Deserialize, PartialEq, Clone)]
1058pub struct WithdrawalAddress {
1059    pub address: String,
1060    pub asset: String,
1061    pub method: String,
1062    pub key: String,
1063    pub memo: Option<String>,
1064    pub verified: bool,
1065}
1066
1067/// Response type for status of a deposit or withdrawal
1068///
1069/// Response can either be bare (Response) or be a wrapper containing a cursor for the next page (Cursor)
1070#[derive(Debug, Deserialize, PartialEq, Clone)]
1071#[serde(untagged)]
1072pub enum DepositWithdrawResponse {
1073    Cursor(DepositWithdrawalCursor),
1074    Response(Vec<DepositWithdrawal>),
1075}
1076
1077/// Cursor response that wraps a deposit
1078#[derive(Debug, Deserialize, PartialEq, Clone)]
1079pub struct DepositWithdrawalCursor {
1080    deposit: Vec<DepositWithdrawal>,
1081    cursor: BoolOrString,
1082}
1083
1084/// Description of a deposit or withdrawal
1085#[derive(Debug, Deserialize, PartialEq, Clone)]
1086pub struct DepositWithdrawal {
1087    pub method: String,
1088    #[serde(rename = "aclass")]
1089    pub asset_class: String,
1090    pub asset: String,
1091    #[serde(rename = "refid")]
1092    pub ref_id: String,
1093    #[serde(rename = "txid")]
1094    pub tx_id: String,
1095    pub info: String,
1096    pub amount: Decimal,
1097    pub fee: Decimal,
1098    pub time: i64,
1099    pub status: TransferStatus,
1100    #[serde(rename = "status-prop")]
1101    pub status_prop: Option<StatusProp>,
1102    pub orginators: Option<Vec<String>>,
1103}
1104
1105/// Description of a withdrawal
1106#[derive(Debug, Deserialize, PartialEq, Clone)]
1107pub struct Withdrawal {
1108    pub method: String,
1109    pub limit: BoolOrString,
1110    pub fee: Decimal,
1111    pub amount: Decimal,
1112}
1113
1114/// Response type containing only a ref id for confirmation
1115#[derive(Debug, Deserialize, PartialEq, Clone)]
1116pub struct ConfirmationRefId {
1117    #[serde(rename = "refid")]
1118    pub ref_id: String,
1119}
1120
1121/// Response type for a transfer to a linked Futures account
1122#[derive(Debug, Deserialize, PartialEq, Clone)]
1123pub struct AccountTransfer {
1124    pub transfer_id: String,
1125    pub status: AccountTransferStatus,
1126}
1127
1128/// Response type for AllocateStatus
1129#[derive(Debug, Deserialize, PartialEq, Clone)]
1130pub struct AllocationStatus {
1131    pub pending: bool,
1132}
1133
1134/// Paginated response type for /Earn/Strategies
1135#[derive(Debug, Deserialize, PartialEq, Clone)]
1136pub struct EarnStrategies {
1137    pub items: Vec<EarnStrategy>,
1138    pub next_cursor: Option<String>,
1139}
1140
1141/// Description of an individual earn strategy
1142#[derive(Debug, Deserialize, PartialEq, Clone)]
1143pub struct EarnStrategy {
1144    pub allocation_fee: EarnFee,
1145    pub allocation_restriction_info: Vec<String>,
1146    pub apr_estimate: Option<AprEstimate>,
1147    pub asset: String,
1148    pub auto_compound: AutoCompound,
1149    pub can_allocate: bool,
1150    pub can_deallocate: bool,
1151    pub deallocation_fee: EarnFee,
1152    pub id: String,
1153    pub lock_type: LockTypeDetail,
1154    pub user_cap: Option<Decimal>,
1155    pub user_min_allocation: Option<Decimal>,
1156    pub yield_source: YieldSource,
1157}
1158
1159/// Details of how funds are locked by an earn strategy
1160#[derive(Debug, Deserialize, PartialEq, Clone)]
1161pub struct LockTypeDetail {
1162    #[serde(rename = "type")]
1163    pub lock_type: LockType,
1164    #[serde(flatten)]
1165    pub bonding: Option<BondingDetail>,
1166}
1167
1168/// Details of an earn strategy's commitments and rewards
1169#[derive(Debug, Deserialize, PartialEq, Clone)]
1170pub struct BondingDetail {
1171    pub payout_frequency: Option<i64>,
1172    pub bonding_period: Option<i64>,
1173    pub bonding_period_variable: Option<bool>,
1174    pub bonding_rewards: Option<bool>,
1175    pub exit_queue_period: Option<i64>,
1176    pub unbonding_period: Option<i64>,
1177    pub unbonding_period_variable: Option<bool>,
1178    pub unbonding_rewards: Option<bool>,
1179}
1180
1181/// Bracketed estimate for a strategy's APR
1182#[derive(Debug, Deserialize, PartialEq, Clone)]
1183pub struct AprEstimate {
1184    pub low: Decimal,
1185    pub high: Decimal,
1186}
1187
1188/// Wrapper type for compounding nature of a strategy
1189#[derive(Debug, Deserialize, PartialEq, Clone)]
1190pub struct AutoCompound {
1191    #[serde(rename = "type")]
1192    pub auto_compound_type: AutoCompoundType,
1193    pub default: Option<bool>,
1194}
1195
1196/// Wrapper type for the origin of rewards from a strategy
1197#[derive(Debug, Deserialize, PartialEq, Clone)]
1198pub struct YieldSource {
1199    #[serde(rename = "type")]
1200    pub yield_type: YieldSourceType,
1201}
1202
1203/// Response type for Earn/Allocations
1204#[derive(Debug, Deserialize, PartialEq, Clone)]
1205pub struct EarnAllocations {
1206    pub converted_asset: String,
1207    pub items: Vec<EarnAllocation>,
1208    pub total_allocated: Decimal,
1209    pub total_rewarded: Decimal,
1210}
1211
1212/// Description of an allocation to an earn strategy
1213#[derive(Debug, Deserialize, PartialEq, Clone)]
1214pub struct EarnAllocation {
1215    pub amount_allocated: AmountAllocated,
1216    pub native_asset: String,
1217    pub payout: Option<Payout>,
1218    pub strategy_id: String,
1219    pub total_rewarded: EarnAmount,
1220}
1221
1222/// Details of an allocation to a particular strategy
1223#[derive(Debug, Deserialize, PartialEq, Clone)]
1224pub struct AmountAllocated {
1225    pub bonding: Option<AllocationState>,
1226    pub exit_queue: Option<AllocationState>,
1227    pub pending: Option<EarnAmount>,
1228    pub total: EarnAmount,
1229    pub unbonding: Option<AllocationState>,
1230}
1231
1232/// State of a single allocation to a strategy
1233#[derive(Debug, Deserialize, PartialEq, Clone)]
1234pub struct AllocationState {
1235    pub allocation_count: i64,
1236    pub allocations: Vec<Allocation>,
1237    pub converted: Decimal,
1238    pub native: Decimal,
1239}
1240
1241/// Description of assets allocated to a strategy
1242#[derive(Debug, Deserialize, PartialEq, Clone)]
1243pub struct Allocation {
1244    pub created_at: String,
1245    pub expires: String,
1246    pub converted: Decimal,
1247    pub native: Decimal,
1248}
1249
1250/// Description of the payout for a particular allocation
1251#[derive(Debug, Deserialize, PartialEq, Clone)]
1252pub struct Payout {
1253    pub period_end: String,
1254    pub period_start: String,
1255    pub accumulated_reward: EarnAmount,
1256    pub estimated_reward: EarnAmount,
1257}
1258
1259/// Amount earned by an allocation in the requested and native assets
1260#[derive(Debug, Deserialize, PartialEq, Clone, Copy)]
1261pub struct EarnAmount {
1262    pub converted: Decimal,
1263    pub native: Decimal,
1264}
1265
1266/// Response type for GetWebSocketsToken
1267#[derive(Debug, Deserialize, Clone)]
1268pub struct WebsocketToken {
1269    pub token: Token,
1270    pub expires: i64,
1271}
1272
1273#[cfg(test)]
1274mod tests {
1275    use crate::response_types::ExtendedBalance;
1276    use rust_decimal_macros::dec;
1277
1278    #[test]
1279    fn test_deserializing_extended_balance_full() {
1280        let balance =
1281            r#"{"balance": "0.01", "hold_trade": "0.02", "credit": "0.03", "credit_used": "0.04"}"#;
1282
1283        let expected_balance = ExtendedBalance {
1284            balance: dec!(0.01),
1285            hold_trade: dec!(0.02),
1286            credit: Some(dec!(0.03)),
1287            credit_used: Some(dec!(0.04)),
1288        };
1289
1290        assert_eq!(expected_balance, serde_json::from_str(balance).unwrap());
1291    }
1292
1293    #[test]
1294    fn test_deserializing_extended_balance_some_none() {
1295        let balance_missing = r#"{"balance": "0.01", "hold_trade": "0.02"}"#;
1296
1297        let expected_balance = ExtendedBalance {
1298            balance: dec!(0.01),
1299            hold_trade: dec!(0.02),
1300            credit: None,
1301            credit_used: None,
1302        };
1303
1304        assert_eq!(
1305            expected_balance,
1306            serde_json::from_str(balance_missing).unwrap()
1307        );
1308    }
1309
1310    #[test]
1311    fn test_deserializing_extended_balance_some_gibberish() {
1312        let gibberish = r#"{"balance": "0.01", "hold_trade": "0.02", "credit": "soNotANumber"}"#;
1313
1314        assert!(serde_json::from_str::<ExtendedBalance>(gibberish).is_err())
1315    }
1316}