kraken_async_rs/
request_types.rs

1//! REST request types
2//!
3use crate::response_types::{BuySell, LedgerEntryType, OrderFlag, OrderType};
4use rust_decimal::Decimal;
5use serde::{Deserialize, Serialize};
6use serde_with::formats::CommaSeparator;
7use serde_with::StringWithSeparator;
8use serde_with::{serde_as, skip_serializing_none};
9use simple_builder::Builder;
10use std::fmt::{Display, Formatter};
11use to_query_params::{QueryParams, ToQueryParams};
12
13/// Wrapper type for submitting order cancels by Kraken id (String) or user-ref (Int).
14#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
15#[serde(untagged)]
16pub enum IntOrString {
17    Int(i64),
18    String(String),
19}
20
21impl From<i64> for IntOrString {
22    fn from(value: i64) -> Self {
23        IntOrString::Int(value)
24    }
25}
26
27impl From<&str> for IntOrString {
28    fn from(value: &str) -> Self {
29        IntOrString::String(value.to_string())
30    }
31}
32
33impl From<String> for IntOrString {
34    fn from(value: String) -> Self {
35        IntOrString::String(value)
36    }
37}
38
39impl Display for IntOrString {
40    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
41        match self {
42            IntOrString::Int(i) => write!(f, "{i}"),
43            IntOrString::String(s) => write!(f, "{s}"),
44        }
45    }
46}
47
48/// Time to use when searching for closed orders by start and end timestamps.
49#[derive(Debug, Clone, PartialEq, Eq)]
50pub enum CloseTime {
51    Open,
52    Close,
53    Both,
54}
55
56impl Display for CloseTime {
57    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
58        match self {
59            CloseTime::Open => write!(f, "open"),
60            CloseTime::Close => write!(f, "close"),
61            CloseTime::Both => write!(f, "both"),
62        }
63    }
64}
65
66/// Type of information to request for asset pairs.
67///
68/// Defaults to Info, which is all info.
69#[derive(Debug, Clone, PartialEq, Eq)]
70pub enum AssetPairInfo {
71    Info,
72    Leverage,
73    Fees,
74    Margin,
75}
76
77impl Display for AssetPairInfo {
78    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
79        match self {
80            AssetPairInfo::Info => write!(f, "info"),
81            AssetPairInfo::Leverage => write!(f, "leverage"),
82            AssetPairInfo::Fees => write!(f, "fees"),
83            AssetPairInfo::Margin => write!(f, "margin"),
84        }
85    }
86}
87
88/// All possible candlestick intervals for requesting OHLC data.
89#[derive(Debug, Clone, PartialEq, Eq)]
90pub enum CandlestickInterval {
91    Minute,
92    Minutes5,
93    Minutes15,
94    Minutes30,
95    Hour,
96    Hours4,
97    Day,
98    Week,
99    Days15,
100}
101
102impl Display for CandlestickInterval {
103    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
104        match self {
105            CandlestickInterval::Minute => write!(f, "1"),
106            CandlestickInterval::Minutes5 => write!(f, "5"),
107            CandlestickInterval::Minutes15 => write!(f, "15"),
108            CandlestickInterval::Minutes30 => write!(f, "30"),
109            CandlestickInterval::Hour => write!(f, "60"),
110            CandlestickInterval::Hours4 => write!(f, "240"),
111            CandlestickInterval::Day => write!(f, "1440"),
112            CandlestickInterval::Week => write!(f, "10080"),
113            CandlestickInterval::Days15 => write!(f, "21600"),
114        }
115    }
116}
117
118/// Types of trades to filter for when requesting user's trade history.
119#[derive(Debug, Clone, PartialEq, Eq)]
120pub enum TradeType {
121    All,
122    AnyPosition,
123    ClosedPosition,
124    ClosingPosition,
125    NoPosition,
126}
127
128impl Display for TradeType {
129    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
130        match self {
131            TradeType::All => write!(f, "all"),
132            TradeType::AnyPosition => write!(f, "any position"),
133            TradeType::ClosedPosition => write!(f, "closed position"),
134            TradeType::ClosingPosition => write!(f, "closing position"),
135            TradeType::NoPosition => write!(f, "no position"),
136        }
137    }
138}
139
140/// Wrapper type for a `Vec<OrderFlag>` that serializes to a comma-separated string.
141#[derive(Debug, Clone, PartialEq, Eq)]
142pub struct OrderFlags(Vec<OrderFlag>);
143
144impl OrderFlags {
145    pub fn new(order_flags: Vec<OrderFlag>) -> OrderFlags {
146        OrderFlags(order_flags)
147    }
148}
149
150impl From<OrderFlag> for OrderFlags {
151    fn from(value: OrderFlag) -> Self {
152        OrderFlags::new(vec![value])
153    }
154}
155
156impl Display for OrderFlags {
157    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
158        let strings: Vec<String> = self.0.iter().map(|flag| flag.to_string()).collect();
159        write!(f, "{}", strings.join(","))
160    }
161}
162
163/// Type of report to request generation for.
164#[derive(Debug, Clone, PartialEq)]
165pub enum ReportType {
166    Trades,
167    Ledgers,
168}
169
170impl Display for ReportType {
171    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
172        match self {
173            ReportType::Trades => write!(f, "trades"),
174            ReportType::Ledgers => write!(f, "ledgers"),
175        }
176    }
177}
178
179/// Format of report, either comma or tab separated values.
180#[derive(Debug, Clone, PartialEq)]
181pub enum ReportFormatType {
182    Csv,
183    Tsv,
184}
185
186impl Display for ReportFormatType {
187    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
188        match self {
189            ReportFormatType::Csv => write!(f, "CSV"),
190            ReportFormatType::Tsv => write!(f, "TSV"),
191        }
192    }
193}
194
195/// Whether to cancel or delete a requested export report.
196#[derive(Debug, Clone, PartialEq)]
197pub enum DeleteExportType {
198    Cancel,
199    Delete,
200}
201
202impl Display for DeleteExportType {
203    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
204        match self {
205            DeleteExportType::Cancel => write!(f, "cancel"),
206            DeleteExportType::Delete => write!(f, "delete"),
207        }
208    }
209}
210
211/// Type of price to use for conditional orders.
212///
213/// `Index` uses an external price feed while `Last` uses the most recent trade on Kraken.
214/// `Last` is the default and fallback if external feeds are unavailable.
215#[derive(Debug, Eq, PartialEq, Clone, Copy, Serialize, Deserialize)]
216#[serde(rename_all = "lowercase")]
217pub enum TriggerType {
218    Index,
219    Last,
220}
221
222impl Display for TriggerType {
223    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
224        match self {
225            TriggerType::Index => write!(f, "index"),
226            TriggerType::Last => write!(f, "last"),
227        }
228    }
229}
230
231/// Strategy for exchange to take when handling a self-crossing order.
232#[derive(Debug, Eq, PartialEq, Clone, Copy, Serialize, Deserialize)]
233#[serde(rename_all = "snake_case")]
234pub enum SelfTradePrevention {
235    CancelNewest,
236    CancelOldest,
237    CancelBoth,
238}
239
240impl Display for SelfTradePrevention {
241    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
242        match self {
243            SelfTradePrevention::CancelNewest => write!(f, "cancel_newest"),
244            SelfTradePrevention::CancelOldest => write!(f, "cancel_oldest"),
245            SelfTradePrevention::CancelBoth => write!(f, "cancel_both"),
246        }
247    }
248}
249
250/// Time in Force for the given order.
251///
252/// Good 'til Cancelled
253/// Immediate or Cancel (aka Fill or Kill)
254/// Good 'til Date (must come with an expiration time in the request)
255#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy)]
256pub enum TimeInForce {
257    GTC,
258    IOC,
259    GTD,
260}
261
262/// Time in Force for the given order.
263///
264/// Good 'til Cancelled
265/// Immediate or Cancel (aka Fill or Kill)
266/// Good 'til Date (must come with an expiration time in the request)
267#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy)]
268#[serde(rename_all = "lowercase")]
269pub enum TimeInForceV2 {
270    GTC,
271    IOC,
272    GTD,
273}
274
275impl Display for TimeInForce {
276    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
277        match self {
278            TimeInForce::GTC => write!(f, "GTC"),
279            TimeInForce::IOC => write!(f, "IOC"),
280            TimeInForce::GTD => write!(f, "GTD"),
281        }
282    }
283}
284
285/// Type of lock-up for a given Earn strategy.
286#[derive(Debug, Clone, Copy, Eq, PartialEq)]
287pub enum LockType {
288    Flex,
289    Bonded,
290    Timed,
291    Instant,
292}
293
294impl Display for LockType {
295    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
296        match self {
297            LockType::Flex => write!(f, "flex"),
298            LockType::Bonded => write!(f, "bonded"),
299            LockType::Timed => write!(f, "timed"),
300            LockType::Instant => write!(f, "instant"),
301        }
302    }
303}
304
305/// Wrapper type for a `Vec<String>` that serializes to comma-separated.
306#[derive(Debug, Clone, PartialEq, Deserialize)]
307pub struct StringCSV(pub Vec<String>);
308
309impl StringCSV {
310    pub fn new(strings: Vec<String>) -> StringCSV {
311        StringCSV(strings)
312    }
313}
314
315impl From<&str> for StringCSV {
316    fn from(value: &str) -> Self {
317        StringCSV::new(vec![value.to_string()])
318    }
319}
320
321impl From<String> for StringCSV {
322    fn from(value: String) -> Self {
323        StringCSV::new(vec![value])
324    }
325}
326
327impl From<&String> for StringCSV {
328    fn from(value: &String) -> Self {
329        StringCSV::new(vec![value.clone()])
330    }
331}
332
333impl Display for StringCSV {
334    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
335        write!(f, "{}", self.0.join(","))
336    }
337}
338
339/// A request for details on a particular asset, such as "BTC", "ETH", or "USDC".
340///
341/// [StringCSV] takes a `Vec<String>` and formats them in queries as comma-separated.
342#[derive(Debug, Clone, QueryParams, Builder)]
343pub struct AssetInfoRequest {
344    pub asset: Option<StringCSV>,
345    #[query(rename = "aclass")]
346    pub asset_class: Option<String>,
347}
348
349/// A request for details on a particular trading pair, such as "BTCUSD", "DOGEUSDT", or "ETHUSD".
350///
351/// [StringCSV] takes a `Vec<String>` and formats them in queries as comma-separated.
352#[derive(Debug, Clone, QueryParams, Builder)]
353pub struct TradableAssetPairsRequest {
354    pub pair: Option<StringCSV>,
355    pub info: Option<AssetPairInfo>,
356    pub country_code: Option<String>,
357}
358
359/// A request for common ticker info for one or many pairs.
360///
361/// [StringCSV] takes a `Vec<String>` and formats them in queries as comma-separated.
362#[derive(Debug, Clone, QueryParams, Builder)]
363pub struct TickerRequest {
364    pub pair: Option<StringCSV>,
365}
366
367/// A request for OHLC data for a single pair, optionally providing a `since` to retrieve
368/// incremental updates.
369#[derive(Debug, Clone, QueryParams, Builder)]
370pub struct OHLCRequest {
371    #[query(required)]
372    #[builder(required)]
373    pub pair: String,
374    pub interval: Option<CandlestickInterval>,
375    pub since: Option<i64>,
376}
377
378/// A request for the orderbook of a pair, optionally at a given depth of bids and asks
379/// (`count` parameter).
380#[derive(Debug, Clone, QueryParams, Builder)]
381pub struct OrderbookRequest {
382    #[query(required)]
383    #[builder(required)]
384    pub pair: String,
385    pub count: Option<i64>,
386}
387
388/// A fully-paginated request for trades from a particular pair.
389///
390/// `since` can be set to 0 to get the very first trades recorded on Kraken, or set to the `last`
391/// value provided in the response for full pagination.
392///
393/// See examples/live_retrieving_recent_traders.rs for an example of completing a paginated request.
394#[derive(Debug, Clone, QueryParams, Builder)]
395pub struct RecentTradesRequest {
396    #[query(required)]
397    #[builder(required)]
398    pub pair: String,
399    pub since: Option<String>,
400    pub count: Option<i64>,
401}
402
403/// Retrieve the most recent bid/ask spreads for a given pair, optionally with a `since` parameter
404/// to receive only incremental updates.
405#[derive(Debug, Clone, QueryParams, Builder)]
406pub struct RecentSpreadsRequest {
407    #[query(required)]
408    #[builder(required)]
409    pub pair: String,
410    pub since: Option<i64>,
411}
412
413/// A request for margin trading data, optionally only for a specific pair.
414#[derive(Debug, Clone, QueryParams, Builder)]
415pub struct TradeBalanceRequest {
416    pub asset: Option<String>,
417}
418
419/// A request for all open orders on the account.
420///
421/// Optionally returns trades associated with each order if `trades` is true, and can be filtered by
422/// a provided user ref value.
423#[derive(Debug, Clone, QueryParams, Builder)]
424pub struct OpenOrdersRequest {
425    pub trades: Option<bool>,
426    pub userref: Option<i64>,
427    #[query(rename = "cl_ord_id")]
428    pub client_order_id: Option<String>,
429}
430
431/// A request to retrieve historical orders, 50 at a time.
432///
433/// `start` and `end` provide epoch-time bounds to query, while offset provides pagination within
434/// that window.
435#[derive(Debug, Clone, QueryParams, Builder)]
436pub struct ClosedOrdersRequest {
437    pub trades: Option<bool>,
438    pub userref: Option<i64>,
439    pub start: Option<i64>,
440    pub end: Option<i64>,
441    #[query(rename = "ofs")]
442    pub offset: Option<i64>,
443    #[query(rename = "closetime")]
444    pub close_time: Option<CloseTime>,
445    #[query(rename = "cl_ord_id")]
446    pub client_order_id: Option<String>,
447}
448
449/// A request for the details of up to 50 orders by id.
450///
451/// Optionally including trade ids, filtering by user-ref, and consolidating trades by taker.
452#[derive(Debug, Clone, QueryParams, Builder)]
453pub struct OrderRequest {
454    #[builder(required)]
455    #[query(required, rename = "txid")]
456    pub tx_id: StringCSV,
457    pub trades: Option<bool>,
458    pub userref: Option<i64>,
459    pub consolidate_taker: Option<bool>,
460}
461
462#[derive(Debug, Clone, Serialize, Builder)]
463pub struct OrderAmendsRequest {
464    #[builder(required)]
465    order_id: String,
466}
467
468/// A request for any historical trades for the account.
469///
470/// This request is fully paginated by epoch time using the `start` and `end` parameters, in
471/// conjunction with the `offset` parameter.
472#[derive(Debug, Clone, QueryParams, Builder, PartialEq, Eq)]
473pub struct TradesHistoryRequest {
474    #[query(rename = "type")]
475    pub trade_type: Option<TradeType>,
476    pub trades: Option<bool>,
477    pub start: Option<i64>,
478    pub end: Option<i64>,
479    #[query(rename = "ofs")]
480    pub offset: Option<i64>,
481    pub consolidate_taker: Option<bool>,
482    pub ledgers: Option<bool>,
483}
484
485/// A request for details of up to 50 trades by ref id.
486#[derive(Debug, Clone, QueryParams, Builder)]
487pub struct TradeInfoRequest {
488    #[builder(required)]
489    #[query(required, rename = "txid")]
490    pub tx_id: StringCSV,
491    pub trades: Option<bool>,
492}
493
494/// A request for details about an open margin position.
495#[derive(Debug, Clone, QueryParams, Builder)]
496pub struct OpenPositionsRequest {
497    #[query(rename = "txid")]
498    pub tx_id: Option<String>,
499    #[query(rename = "docalcs")]
500    pub do_calcs: Option<bool>,
501    pub consolidation: Option<String>,
502}
503
504/// A request for 50 ledger entries for the account.
505///
506/// This request is fully paginated by epoch time using the `start` and `end` parameters, in
507/// conjunction with the `offset` parameter.
508#[derive(Debug, Clone, QueryParams, Builder)]
509pub struct LedgersInfoRequest {
510    pub asset: Option<StringCSV>,
511    #[query(rename = "aclass")]
512    pub asset_class: Option<String>,
513    #[query(rename = "type")]
514    pub entry_type: Option<LedgerEntryType>,
515    pub start: Option<i64>,
516    pub end: Option<i64>,
517    #[query(rename = "ofs")]
518    pub offset: Option<i64>,
519    pub without_count: Option<bool>,
520}
521
522/// A request for details of up to 20 ledger entries by id.
523#[derive(Debug, Clone, QueryParams, Builder)]
524pub struct QueryLedgerRequest {
525    #[query(required)]
526    #[builder(required)]
527    pub id: StringCSV,
528    pub trades: Option<bool>,
529}
530
531/// A request for cumulative 30-day USD trading volume for the account.
532///
533/// Optionally including fees if a particular pairs are requested.
534#[derive(Debug, Clone, QueryParams, Builder)]
535pub struct TradeVolumeRequest {
536    pub pair: Option<StringCSV>,
537}
538
539/// A request for the asynchronous generation of a report of "trades" or "ledgers".
540#[derive(Debug, Clone, QueryParams, Builder)]
541pub struct ExportReportRequest {
542    #[builder(required)]
543    #[query(required)]
544    pub report: ReportType,
545    pub format: Option<ReportFormatType>,
546    #[builder(required)]
547    #[query(required)]
548    pub description: String,
549    pub fields: Option<String>,
550    #[query(rename = "starttm")]
551    pub start_time: Option<i64>,
552    #[query(rename = "endtm")]
553    pub end_time: Option<i64>,
554}
555
556/// A request for the status of a requested export report.
557#[derive(Debug, Clone, QueryParams, Builder)]
558pub struct ExportReportStatusRequest {
559    #[builder(required)]
560    #[query(required)]
561    pub report: ReportType,
562}
563
564/// A request to retrieve a specific export report by id.
565#[derive(Debug, Clone, QueryParams, Builder)]
566pub struct RetrieveExportReportRequest {
567    #[builder(required)]
568    #[query(required)]
569    pub id: String,
570}
571
572/// A request to delete an export report by id.
573#[derive(Debug, Clone, QueryParams, Builder)]
574pub struct DeleteExportRequest {
575    #[builder(required)]
576    #[query(required)]
577    pub id: String,
578    #[builder(required)]
579    #[query(required, rename = "type")]
580    pub delete_type: DeleteExportType,
581}
582
583/// A request to create a new spot order.
584#[derive(Debug, Clone, QueryParams, Builder, PartialEq, Eq)]
585pub struct AddOrderRequest {
586    #[query(rename = "userref")]
587    pub user_ref: Option<i64>,
588    #[query(rename = "cl_ord_id")]
589    pub client_order_id: Option<String>,
590    #[builder(required)]
591    #[query(required, rename = "ordertype")]
592    pub order_type: OrderType,
593    #[builder(required)]
594    #[query(required, rename = "type")]
595    pub side: BuySell,
596    #[builder(required)]
597    #[query(required)]
598    pub volume: Decimal,
599    #[query(rename = "displayvol")]
600    pub display_volume: Option<Decimal>,
601    #[builder(required)]
602    #[query(required)]
603    pub pair: String,
604    #[query(rename = "reqid")]
605    pub req_id: Option<i64>,
606    pub price: Option<Decimal>,
607    #[query(rename = "price2")]
608    pub price_2: Option<Decimal>,
609    pub trigger: Option<TriggerType>,
610    pub leverage: Option<i64>,
611    pub reduce_only: Option<bool>,
612    #[query(rename = "stptype")]
613    pub stp_type: Option<SelfTradePrevention>,
614    #[query(rename = "oflags")]
615    pub order_flags: Option<OrderFlags>,
616    #[query(rename = "timeinforce")]
617    pub time_in_force: Option<TimeInForce>,
618    #[query(rename = "starttm")]
619    pub start_time: Option<String>,
620    #[query(rename = "expiretm")]
621    pub expire_time: Option<String>,
622    #[query(rename = "close[ordertype]")]
623    pub close_order_type: Option<String>,
624    #[query(rename = "close[price]")]
625    pub close_price: Option<Decimal>,
626    #[query(rename = "close[price2]")]
627    pub close_price_2: Option<Decimal>,
628    pub deadline: Option<String>,
629    pub validate: Option<bool>,
630}
631
632/// A request to create up to 15 spot orders in a batch.
633#[skip_serializing_none]
634#[derive(Debug, Clone, Serialize, Builder)]
635pub struct AddBatchedOrderRequest {
636    #[builder(required)]
637    pub orders: Vec<BatchedOrderRequest>,
638    #[builder(required)]
639    pub pair: String,
640    pub deadline: Option<String>,
641    pub validate: Option<bool>,
642}
643
644/// An individual order request to be placed in a batch.
645#[serde_as]
646#[skip_serializing_none]
647#[derive(Debug, Clone, Builder, Serialize)]
648pub struct BatchedOrderRequest {
649    #[serde(rename = "userref")]
650    pub user_ref: Option<i64>,
651    #[serde(rename = "cl_ord_id")]
652    pub client_order_id: Option<String>,
653    #[builder(required)]
654    #[serde(rename = "ordertype")]
655    pub order_type: OrderType,
656    #[builder(required)]
657    #[serde(rename = "type")]
658    pub side: BuySell,
659    #[builder(required)]
660    pub volume: Decimal,
661    #[serde(rename = "displayvol")]
662    pub display_volume: Option<Decimal>,
663    pub price: Option<Decimal>,
664    #[serde(rename = "price2")]
665    pub price_2: Option<Decimal>,
666    pub trigger: Option<TriggerType>,
667    pub leverage: Option<i64>,
668    pub reduce_only: Option<bool>,
669    #[serde(rename = "stptype")]
670    pub stp_type: Option<String>,
671    #[serde(rename = "oflags")]
672    #[serde(default)]
673    #[serde_as(as = "Option<StringWithSeparator::<CommaSeparator, OrderFlag>>")]
674    pub order_flags: Option<Vec<OrderFlag>>,
675    #[serde(rename = "timeinforce")]
676    pub time_in_force: Option<TimeInForce>,
677    #[serde(rename = "starttm")]
678    pub start_time: Option<String>,
679    #[serde(rename = "expiretm")]
680    pub expire_time: Option<String>,
681}
682
683#[derive(Debug, Clone, Serialize, Builder)]
684pub struct AmendOrderRequest {
685    #[serde(rename = "txid")]
686    pub tx_id: Option<String>,
687    #[serde(rename = "cl_ord_id")]
688    pub client_order_id: Option<String>,
689    #[serde(rename = "order_qty")]
690    pub order_quantity: Option<Decimal>,
691    #[serde(rename = "display_qty")]
692    pub display_quantity: Option<Decimal>,
693    pub limit_price: Option<String>,
694    pub trigger_price: Option<String>,
695    pub post_only: Option<bool>,
696    pub deadline: Option<String>, // RFC-3339
697}
698
699/// A request to edit an existing order.
700#[derive(Debug, Clone, QueryParams, Builder)]
701pub struct EditOrderRequest {
702    #[query(rename = "userref")]
703    pub user_ref: Option<i64>,
704    #[query(required, rename = "txid")]
705    #[builder(required)]
706    pub tx_id: String,
707    #[builder(required)]
708    #[query(required)]
709    pub volume: Decimal,
710    #[query(rename = "displayvol")]
711    pub display_volume: Option<Decimal>,
712    #[builder(required)]
713    #[query(required)]
714    pub pair: String,
715    pub price: Option<Decimal>,
716    #[query(rename = "price2")]
717    pub price_2: Option<Decimal>,
718    #[query(rename = "oflags")]
719    pub order_flags: Option<OrderFlags>,
720    pub deadline: Option<String>,
721    pub cancel_response: Option<bool>,
722    pub validate: Option<bool>,
723}
724
725/// A request to cancel an order by txid (String) or userref (Int).
726#[derive(Debug, Clone, QueryParams, Builder)]
727pub struct CancelOrderRequest {
728    #[query(required, rename = "txid")]
729    #[builder(required)]
730    pub tx_id: IntOrString,
731    #[query(rename = "cl_ord_id")]
732    pub client_order_id: Option<String>,
733}
734
735/// A "dead man's switch" for all active orders.
736///
737/// Once set to a timestamp, this must be continually called to prevent all orders from being
738/// cancelled.
739#[derive(Debug, Clone, QueryParams, Builder)]
740pub struct CancelAllOrdersAfterRequest {
741    #[builder(required)]
742    #[query(required)]
743    pub timeout: i64,
744}
745
746/// A request to cancel up to 50 orders in a batch by tx id or user ref.
747#[derive(Debug, Clone, Builder, Serialize)]
748pub struct CancelBatchOrdersRequest {
749    #[builder(required)]
750    pub orders: Vec<IntOrString>,
751    #[serde(rename = "cl_ord_ids")]
752    pub client_order_ids: Option<Vec<String>>,
753}
754
755impl CancelBatchOrdersRequest {
756    pub fn from_user_refs(refs: Vec<i64>) -> CancelBatchOrdersRequest {
757        CancelBatchOrdersRequest {
758            orders: refs.into_iter().map(IntOrString::Int).collect(),
759            client_order_ids: None,
760        }
761    }
762
763    pub fn from_tx_ids(ids: Vec<String>) -> CancelBatchOrdersRequest {
764        CancelBatchOrdersRequest {
765            orders: ids.into_iter().map(IntOrString::String).collect(),
766            client_order_ids: None,
767        }
768    }
769
770    pub fn from_client_order_ids(ids: Vec<String>) -> CancelBatchOrdersRequest {
771        CancelBatchOrdersRequest {
772            orders: vec![],
773            client_order_ids: Some(ids),
774        }
775    }
776}
777
778/// A request for all available deposit methods for a given asset.
779#[derive(Debug, Clone, Builder, QueryParams)]
780pub struct DepositMethodsRequest {
781    #[builder(required)]
782    #[query(required)]
783    pub asset: String,
784    pub aclass: Option<String>,
785}
786
787/// A request to retrieve or generate a deposit address for a particular asset and method.
788#[derive(Debug, Clone, Builder, QueryParams)]
789pub struct DepositAddressesRequest {
790    #[query(required)]
791    #[builder(required)]
792    pub asset: String,
793    #[query(required)]
794    #[builder(required)]
795    pub method: String,
796    #[query(rename = "new")]
797    pub is_new: Option<bool>,
798    pub amount: Option<Decimal>, // only for Lightning network
799}
800
801/// A request for all available withdrawal methods for the user.
802#[derive(Debug, Clone, Builder, QueryParams)]
803pub struct WithdrawalMethodsRequest {
804    pub asset: Option<String>,
805    #[query(rename = "aclass")]
806    pub asset_class: Option<String>,
807    pub network: Option<String>,
808}
809
810/// A request to retrieve or generate a withdrawal address for a particular asset and method.
811#[derive(Debug, Clone, Builder, QueryParams)]
812pub struct WithdrawalAddressesRequest {
813    pub asset: Option<String>,
814    #[query(rename = "aclass")]
815    pub asset_class: Option<String>,
816    pub method: Option<String>,
817    pub key: Option<String>,
818    pub verified: Option<bool>,
819}
820
821/// A sub-type for specifying if paginating (Bool), or providing a cursor for the next page (String).
822#[derive(Debug, Clone)]
823pub enum Cursor {
824    String(String),
825    Bool(bool),
826}
827
828impl Display for Cursor {
829    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
830        match self {
831            Cursor::String(str) => write!(f, "{}", str),
832            Cursor::Bool(b) => write!(f, "{}", b),
833        }
834    }
835}
836
837/// A request for the status of a deposit or withdrawal request.
838#[derive(Debug, Clone, Builder, QueryParams)]
839pub struct StatusOfDepositWithdrawRequest {
840    pub asset: Option<String>,
841    #[query(rename = "aclass")]
842    pub asset_class: Option<String>,
843    pub method: Option<String>,
844    pub start: Option<String>,
845    pub end: Option<String>,
846    pub cursor: Option<Cursor>,
847    pub limit: Option<i64>,
848}
849
850/// A request for the limit, amount and fee to withdraw asset.
851#[derive(Debug, Clone, Builder, QueryParams)]
852pub struct WithdrawalInfoRequest {
853    #[builder(required)]
854    #[query(required)]
855    pub asset: String,
856    #[builder(required)]
857    #[query(required)]
858    pub key: String,
859    #[builder(required)]
860    #[query(required)]
861    pub amount: Decimal,
862}
863
864/// A request to withdraw funds.
865#[derive(Debug, Clone, Builder, QueryParams)]
866pub struct WithdrawFundsRequest {
867    #[builder(required)]
868    #[query(required)]
869    pub asset: String,
870    #[builder(required)]
871    #[query(required)]
872    pub key: String,
873    #[builder(required)]
874    #[query(required)]
875    pub amount: Decimal,
876    pub address: Option<String>,
877    pub max_fee: Option<Decimal>,
878}
879
880/// A request to cancel an active withdrawal.
881#[derive(Debug, Clone, Builder, QueryParams)]
882pub struct WithdrawCancelRequest {
883    #[builder(required)]
884    #[query(required)]
885    pub asset: String,
886    #[builder(required)]
887    #[query(required, rename = "refid")]
888    pub ref_id: String,
889}
890
891/// A request to transfer from the account's Spot wallet to Future's wallet.
892#[derive(Debug, Clone, Builder, QueryParams)]
893pub struct WalletTransferRequest {
894    #[builder(required)]
895    #[query(required)]
896    pub asset: String,
897    #[builder(required)]
898    #[query(required)]
899    pub from: String,
900    #[builder(required)]
901    #[query(required)]
902    pub to: String,
903    #[builder(required)]
904    #[query(required)]
905    pub amount: Decimal,
906}
907
908/// A request to create a sub-account for trading.
909#[derive(Debug, Clone, Builder, QueryParams)]
910pub struct CreateSubAccountRequest {
911    #[builder(required)]
912    #[query(required)]
913    pub username: String,
914    #[builder(required)]
915    #[query(required)]
916    pub email: String,
917}
918
919/// A request to transfer assets between sub-accounts.
920#[derive(Debug, Clone, Builder, QueryParams)]
921pub struct AccountTransferRequest {
922    #[builder(required)]
923    #[query(required)]
924    pub asset: String,
925    #[builder(required)]
926    #[query(required)]
927    pub amount: Decimal,
928    #[builder(required)]
929    #[query(required)]
930    pub from: String,
931    #[builder(required)]
932    #[query(required)]
933    pub to: String,
934}
935
936/// A request to allocate funds to a particular Earn strategy.
937#[derive(Debug, Clone, Builder, QueryParams)]
938pub struct AllocateEarnFundsRequest {
939    #[builder(required)]
940    #[query(required)]
941    pub amount: Decimal,
942    #[builder(required)]
943    #[query(required)]
944    pub strategy_id: String,
945}
946
947/// A request for the allocation status for a given strategy.
948#[derive(Debug, Clone, Builder, QueryParams)]
949pub struct EarnAllocationStatusRequest {
950    #[builder(required)]
951    #[query(required)]
952    pub strategy_id: String,
953}
954
955/// A request for all earn strategies.
956///
957/// Pagination is available via the `cursor` and `limit` parameters.
958#[derive(Debug, Clone, Builder, QueryParams)]
959pub struct ListEarnStrategiesRequest {
960    pub ascending: Option<bool>,
961    pub asset: Option<String>,
962    pub cursor: Option<String>,
963    pub limit: Option<u16>,
964    pub lock_type: Option<LockType>,
965}
966
967/// A request to list all current earn strategy allocations.
968#[derive(Debug, Clone, Builder, QueryParams)]
969pub struct ListEarnAllocationsRequest {
970    pub ascending: Option<bool>,
971    pub converted_asset: Option<String>,
972    pub hide_zero_allocations: Option<bool>,
973}
974
975#[cfg(test)]
976mod tests {
977    use crate::request_types::{CancelBatchOrdersRequest, IntOrString, OrderFlags, StringCSV};
978    use crate::response_types::OrderFlag;
979
980    #[test]
981    fn test_cancel_batch_order_request_ids() {
982        let request =
983            CancelBatchOrdersRequest::from_tx_ids(vec!["M97YKE-HHCTY-2GRVXU".to_string()]);
984
985        let expected = vec![IntOrString::String("M97YKE-HHCTY-2GRVXU".to_string())];
986        assert_eq!(expected, request.orders);
987    }
988
989    #[test]
990    fn test_cancel_batch_order_request_user_refs() {
991        let request = CancelBatchOrdersRequest::from_user_refs(vec![42]);
992
993        let expected = vec![IntOrString::Int(42)];
994        assert_eq!(expected, request.orders);
995    }
996
997    #[test]
998    fn test_string_csv_conversions() {
999        let expected_string_csv = StringCSV::new(vec!["post".to_string()]);
1000
1001        let from_str: StringCSV = "post".into();
1002        let from_string: StringCSV = "post".to_string().into();
1003
1004        let string_ref: &String = &("post".to_string());
1005        let from_string_ref: StringCSV = string_ref.into();
1006
1007        assert_eq!(expected_string_csv, from_str);
1008        assert_eq!(expected_string_csv, from_string);
1009        assert_eq!(expected_string_csv, from_string_ref);
1010    }
1011
1012    #[test]
1013    fn test_order_flag_conversions() {
1014        let expected_order_flag = OrderFlags::new(vec![OrderFlag::NoMarketPriceProtection]);
1015
1016        let order_flags: OrderFlags = OrderFlag::NoMarketPriceProtection.into();
1017
1018        assert_eq!(expected_order_flag, order_flags);
1019    }
1020
1021    #[test]
1022    fn test_int_or_string_conversions() {
1023        let expected_int = IntOrString::Int(42);
1024        let expected_string = IntOrString::String("someString".to_string());
1025
1026        let int: IntOrString = 42.into();
1027        let str: IntOrString = "someString".into();
1028        let string: IntOrString = "someString".to_string().into();
1029
1030        assert_eq!(expected_int, int);
1031        assert_eq!(expected_string, str);
1032        assert_eq!(expected_string, string);
1033    }
1034}