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