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    pub consolidate_taker: Option<bool>,
448    pub without_count: Option<bool>,
449}
450
451/// A request for the details of up to 50 orders by id.
452///
453/// Optionally including trade ids, filtering by user-ref, and consolidating trades by taker.
454#[derive(Debug, Clone, QueryParams, Builder)]
455pub struct OrderRequest {
456    #[builder(required)]
457    #[query(required, rename = "txid")]
458    pub tx_id: StringCSV,
459    pub trades: Option<bool>,
460    pub userref: Option<i64>,
461    pub consolidate_taker: Option<bool>,
462}
463
464#[derive(Debug, Clone, Serialize, Builder)]
465pub struct OrderAmendsRequest {
466    #[builder(required)]
467    order_id: String,
468}
469
470/// A request for any historical trades for the account.
471///
472/// This request is fully paginated by epoch time using the `start` and `end` parameters, in
473/// conjunction with the `offset` parameter.
474#[derive(Debug, Clone, QueryParams, Builder, PartialEq, Eq)]
475pub struct TradesHistoryRequest {
476    #[query(rename = "type")]
477    pub trade_type: Option<TradeType>,
478    pub trades: Option<bool>,
479    pub start: Option<i64>,
480    pub end: Option<i64>,
481    #[query(rename = "ofs")]
482    pub offset: Option<i64>,
483    pub consolidate_taker: Option<bool>,
484    pub ledgers: Option<bool>,
485}
486
487/// A request for details of up to 50 trades by ref id.
488#[derive(Debug, Clone, QueryParams, Builder)]
489pub struct TradeInfoRequest {
490    #[builder(required)]
491    #[query(required, rename = "txid")]
492    pub tx_id: StringCSV,
493    pub trades: Option<bool>,
494}
495
496/// A request for details about an open margin position.
497#[derive(Debug, Clone, QueryParams, Builder)]
498pub struct OpenPositionsRequest {
499    #[query(rename = "txid")]
500    pub tx_id: Option<String>,
501    #[query(rename = "docalcs")]
502    pub do_calcs: Option<bool>,
503    pub consolidation: Option<String>,
504}
505
506/// A request for 50 ledger entries for the account.
507///
508/// This request is fully paginated by epoch time using the `start` and `end` parameters, in
509/// conjunction with the `offset` parameter.
510#[derive(Debug, Clone, QueryParams, Builder)]
511pub struct LedgersInfoRequest {
512    pub asset: Option<StringCSV>,
513    #[query(rename = "aclass")]
514    pub asset_class: Option<String>,
515    #[query(rename = "type")]
516    pub entry_type: Option<LedgerEntryType>,
517    pub start: Option<i64>,
518    pub end: Option<i64>,
519    #[query(rename = "ofs")]
520    pub offset: Option<i64>,
521    pub without_count: Option<bool>,
522}
523
524/// A request for details of up to 20 ledger entries by id.
525#[derive(Debug, Clone, QueryParams, Builder)]
526pub struct QueryLedgerRequest {
527    #[query(required)]
528    #[builder(required)]
529    pub id: StringCSV,
530    pub trades: Option<bool>,
531}
532
533/// A request for cumulative 30-day USD trading volume for the account.
534///
535/// Optionally including fees if a particular pairs are requested.
536#[derive(Debug, Clone, QueryParams, Builder)]
537pub struct TradeVolumeRequest {
538    pub pair: Option<StringCSV>,
539}
540
541/// A request for the asynchronous generation of a report of "trades" or "ledgers".
542#[derive(Debug, Clone, QueryParams, Builder)]
543pub struct ExportReportRequest {
544    #[builder(required)]
545    #[query(required)]
546    pub report: ReportType,
547    pub format: Option<ReportFormatType>,
548    #[builder(required)]
549    #[query(required)]
550    pub description: String,
551    pub fields: Option<String>,
552    #[query(rename = "starttm")]
553    pub start_time: Option<i64>,
554    #[query(rename = "endtm")]
555    pub end_time: Option<i64>,
556}
557
558/// A request for the status of a requested export report.
559#[derive(Debug, Clone, QueryParams, Builder)]
560pub struct ExportReportStatusRequest {
561    #[builder(required)]
562    #[query(required)]
563    pub report: ReportType,
564}
565
566/// A request to retrieve a specific export report by id.
567#[derive(Debug, Clone, QueryParams, Builder)]
568pub struct RetrieveExportReportRequest {
569    #[builder(required)]
570    #[query(required)]
571    pub id: String,
572}
573
574/// A request to delete an export report by id.
575#[derive(Debug, Clone, QueryParams, Builder)]
576pub struct DeleteExportRequest {
577    #[builder(required)]
578    #[query(required)]
579    pub id: String,
580    #[builder(required)]
581    #[query(required, rename = "type")]
582    pub delete_type: DeleteExportType,
583}
584
585/// A request to create a new spot order.
586#[derive(Debug, Clone, QueryParams, Builder, PartialEq, Eq)]
587pub struct AddOrderRequest {
588    #[query(rename = "userref")]
589    pub user_ref: Option<i64>,
590    #[query(rename = "cl_ord_id")]
591    pub client_order_id: Option<String>,
592    #[builder(required)]
593    #[query(required, rename = "ordertype")]
594    pub order_type: OrderType,
595    #[builder(required)]
596    #[query(required, rename = "type")]
597    pub side: BuySell,
598    #[builder(required)]
599    #[query(required)]
600    pub volume: Decimal,
601    #[query(rename = "displayvol")]
602    pub display_volume: Option<Decimal>,
603    #[builder(required)]
604    #[query(required)]
605    pub pair: String,
606    #[query(rename = "reqid")]
607    pub req_id: Option<i64>,
608    pub price: Option<Decimal>,
609    #[query(rename = "price2")]
610    pub price_2: Option<Decimal>,
611    pub trigger: Option<TriggerType>,
612    pub leverage: Option<i64>,
613    pub reduce_only: Option<bool>,
614    #[query(rename = "stptype")]
615    pub stp_type: Option<SelfTradePrevention>,
616    #[query(rename = "oflags")]
617    pub order_flags: Option<OrderFlags>,
618    #[query(rename = "timeinforce")]
619    pub time_in_force: Option<TimeInForce>,
620    #[query(rename = "starttm")]
621    pub start_time: Option<String>,
622    #[query(rename = "expiretm")]
623    pub expire_time: Option<String>,
624    #[query(rename = "close[ordertype]")]
625    pub close_order_type: Option<String>,
626    #[query(rename = "close[price]")]
627    pub close_price: Option<Decimal>,
628    #[query(rename = "close[price2]")]
629    pub close_price_2: Option<Decimal>,
630    pub deadline: Option<String>,
631    pub validate: Option<bool>,
632}
633
634/// A request to create up to 15 spot orders in a batch.
635#[skip_serializing_none]
636#[derive(Debug, Clone, Serialize, Builder)]
637pub struct AddBatchedOrderRequest {
638    #[builder(required)]
639    pub orders: Vec<BatchedOrderRequest>,
640    #[builder(required)]
641    pub pair: String,
642    pub deadline: Option<String>,
643    pub validate: Option<bool>,
644}
645
646/// An individual order request to be placed in a batch.
647#[serde_as]
648#[skip_serializing_none]
649#[derive(Debug, Clone, Builder, Serialize)]
650pub struct BatchedOrderRequest {
651    #[serde(rename = "userref")]
652    pub user_ref: Option<i64>,
653    #[serde(rename = "cl_ord_id")]
654    pub client_order_id: Option<String>,
655    #[builder(required)]
656    #[serde(rename = "ordertype")]
657    pub order_type: OrderType,
658    #[builder(required)]
659    #[serde(rename = "type")]
660    pub side: BuySell,
661    #[builder(required)]
662    pub volume: Decimal,
663    #[serde(rename = "displayvol")]
664    pub display_volume: Option<Decimal>,
665    pub price: Option<Decimal>,
666    #[serde(rename = "price2")]
667    pub price_2: Option<Decimal>,
668    pub trigger: Option<TriggerType>,
669    pub leverage: Option<i64>,
670    pub reduce_only: Option<bool>,
671    #[serde(rename = "stptype")]
672    pub stp_type: Option<String>,
673    #[serde(rename = "oflags")]
674    #[serde(default)]
675    #[serde_as(as = "Option<StringWithSeparator::<CommaSeparator, OrderFlag>>")]
676    pub order_flags: Option<Vec<OrderFlag>>,
677    #[serde(rename = "timeinforce")]
678    pub time_in_force: Option<TimeInForce>,
679    #[serde(rename = "starttm")]
680    pub start_time: Option<String>,
681    #[serde(rename = "expiretm")]
682    pub expire_time: Option<String>,
683}
684
685#[derive(Debug, Clone, Serialize, Builder)]
686pub struct AmendOrderRequest {
687    #[serde(rename = "txid")]
688    pub tx_id: Option<String>,
689    #[serde(rename = "cl_ord_id")]
690    pub client_order_id: Option<String>,
691    #[serde(rename = "order_qty")]
692    pub order_quantity: Option<Decimal>,
693    #[serde(rename = "display_qty")]
694    pub display_quantity: Option<Decimal>,
695    pub limit_price: Option<String>,
696    pub trigger_price: Option<String>,
697    pub post_only: Option<bool>,
698    pub deadline: Option<String>, // RFC-3339
699}
700
701/// A request to edit an existing order.
702#[derive(Debug, Clone, QueryParams, Builder)]
703pub struct EditOrderRequest {
704    #[query(rename = "userref")]
705    pub user_ref: Option<i64>,
706    #[query(required, rename = "txid")]
707    #[builder(required)]
708    pub tx_id: String,
709    #[builder(required)]
710    #[query(required)]
711    pub volume: Decimal,
712    #[query(rename = "displayvol")]
713    pub display_volume: Option<Decimal>,
714    #[builder(required)]
715    #[query(required)]
716    pub pair: String,
717    pub price: Option<Decimal>,
718    #[query(rename = "price2")]
719    pub price_2: Option<Decimal>,
720    #[query(rename = "oflags")]
721    pub order_flags: Option<OrderFlags>,
722    pub deadline: Option<String>,
723    pub cancel_response: Option<bool>,
724    pub validate: Option<bool>,
725}
726
727/// A request to cancel an order by txid (String) or userref (Int).
728#[derive(Debug, Clone, QueryParams, Builder)]
729pub struct CancelOrderRequest {
730    #[query(required, rename = "txid")]
731    #[builder(required)]
732    pub tx_id: IntOrString,
733    #[query(rename = "cl_ord_id")]
734    pub client_order_id: Option<String>,
735}
736
737/// A "dead man's switch" for all active orders.
738///
739/// Once set to a timestamp, this must be continually called to prevent all orders from being
740/// cancelled.
741#[derive(Debug, Clone, QueryParams, Builder)]
742pub struct CancelAllOrdersAfterRequest {
743    #[builder(required)]
744    #[query(required)]
745    pub timeout: i64,
746}
747
748/// A request to cancel up to 50 orders in a batch by tx id or user ref.
749#[derive(Debug, Clone, Builder, Serialize)]
750pub struct CancelBatchOrdersRequest {
751    #[builder(required)]
752    pub orders: Vec<IntOrString>,
753    #[serde(rename = "cl_ord_ids")]
754    pub client_order_ids: Option<Vec<String>>,
755}
756
757impl CancelBatchOrdersRequest {
758    pub fn from_user_refs(refs: Vec<i64>) -> CancelBatchOrdersRequest {
759        CancelBatchOrdersRequest {
760            orders: refs.into_iter().map(IntOrString::Int).collect(),
761            client_order_ids: None,
762        }
763    }
764
765    pub fn from_tx_ids(ids: Vec<String>) -> CancelBatchOrdersRequest {
766        CancelBatchOrdersRequest {
767            orders: ids.into_iter().map(IntOrString::String).collect(),
768            client_order_ids: None,
769        }
770    }
771
772    pub fn from_client_order_ids(ids: Vec<String>) -> CancelBatchOrdersRequest {
773        CancelBatchOrdersRequest {
774            orders: vec![],
775            client_order_ids: Some(ids),
776        }
777    }
778}
779
780/// A request for all available deposit methods for a given asset.
781#[derive(Debug, Clone, Builder, QueryParams)]
782pub struct DepositMethodsRequest {
783    #[builder(required)]
784    #[query(required)]
785    pub asset: String,
786    pub aclass: Option<String>,
787}
788
789/// A request to retrieve or generate a deposit address for a particular asset and method.
790#[derive(Debug, Clone, Builder, QueryParams)]
791pub struct DepositAddressesRequest {
792    #[query(required)]
793    #[builder(required)]
794    pub asset: String,
795    #[query(required)]
796    #[builder(required)]
797    pub method: String,
798    #[query(rename = "new")]
799    pub is_new: Option<bool>,
800    pub amount: Option<Decimal>, // only for Lightning network
801}
802
803/// A request for all available withdrawal methods for the user.
804#[derive(Debug, Clone, Builder, QueryParams)]
805pub struct WithdrawalMethodsRequest {
806    pub asset: Option<String>,
807    #[query(rename = "aclass")]
808    pub asset_class: Option<String>,
809    pub network: Option<String>,
810}
811
812/// A request to retrieve or generate a withdrawal address for a particular asset and method.
813#[derive(Debug, Clone, Builder, QueryParams)]
814pub struct WithdrawalAddressesRequest {
815    pub asset: Option<String>,
816    #[query(rename = "aclass")]
817    pub asset_class: Option<String>,
818    pub method: Option<String>,
819    pub key: Option<String>,
820    pub verified: Option<bool>,
821}
822
823/// A sub-type for specifying if paginating (Bool), or providing a cursor for the next page (String).
824#[derive(Debug, Clone)]
825pub enum Cursor {
826    String(String),
827    Bool(bool),
828}
829
830impl Display for Cursor {
831    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
832        match self {
833            Cursor::String(str) => write!(f, "{str}"),
834            Cursor::Bool(b) => write!(f, "{b}"),
835        }
836    }
837}
838
839/// A request for the status of a deposit or withdrawal request.
840#[derive(Debug, Clone, Builder, QueryParams)]
841pub struct StatusOfDepositWithdrawRequest {
842    pub asset: Option<String>,
843    #[query(rename = "aclass")]
844    pub asset_class: Option<String>,
845    pub method: Option<String>,
846    pub start: Option<String>,
847    pub end: Option<String>,
848    pub cursor: Option<Cursor>,
849    pub limit: Option<i64>,
850}
851
852/// A request for the limit, amount and fee to withdraw asset.
853#[derive(Debug, Clone, Builder, QueryParams)]
854pub struct WithdrawalInfoRequest {
855    #[builder(required)]
856    #[query(required)]
857    pub asset: String,
858    #[builder(required)]
859    #[query(required)]
860    pub key: String,
861    #[builder(required)]
862    #[query(required)]
863    pub amount: Decimal,
864}
865
866/// A request to withdraw funds.
867#[derive(Debug, Clone, Builder, QueryParams)]
868pub struct WithdrawFundsRequest {
869    #[builder(required)]
870    #[query(required)]
871    pub asset: String,
872    #[builder(required)]
873    #[query(required)]
874    pub key: String,
875    #[builder(required)]
876    #[query(required)]
877    pub amount: Decimal,
878    pub address: Option<String>,
879    pub max_fee: Option<Decimal>,
880}
881
882/// A request to cancel an active withdrawal.
883#[derive(Debug, Clone, Builder, QueryParams)]
884pub struct WithdrawCancelRequest {
885    #[builder(required)]
886    #[query(required)]
887    pub asset: String,
888    #[builder(required)]
889    #[query(required, rename = "refid")]
890    pub ref_id: String,
891}
892
893/// A request to transfer from the account's Spot wallet to Future's wallet.
894#[derive(Debug, Clone, Builder, QueryParams)]
895pub struct WalletTransferRequest {
896    #[builder(required)]
897    #[query(required)]
898    pub asset: String,
899    #[builder(required)]
900    #[query(required)]
901    pub from: String,
902    #[builder(required)]
903    #[query(required)]
904    pub to: String,
905    #[builder(required)]
906    #[query(required)]
907    pub amount: Decimal,
908}
909
910/// A request to create a sub-account for trading.
911#[derive(Debug, Clone, Builder, QueryParams)]
912pub struct CreateSubAccountRequest {
913    #[builder(required)]
914    #[query(required)]
915    pub username: String,
916    #[builder(required)]
917    #[query(required)]
918    pub email: String,
919}
920
921/// A request to transfer assets between sub-accounts.
922#[derive(Debug, Clone, Builder, QueryParams)]
923pub struct AccountTransferRequest {
924    #[builder(required)]
925    #[query(required)]
926    pub asset: String,
927    #[builder(required)]
928    #[query(required)]
929    pub amount: Decimal,
930    #[builder(required)]
931    #[query(required)]
932    pub from: String,
933    #[builder(required)]
934    #[query(required)]
935    pub to: String,
936}
937
938/// A request to allocate funds to a particular Earn strategy.
939#[derive(Debug, Clone, Builder, QueryParams)]
940pub struct AllocateEarnFundsRequest {
941    #[builder(required)]
942    #[query(required)]
943    pub amount: Decimal,
944    #[builder(required)]
945    #[query(required)]
946    pub strategy_id: String,
947}
948
949/// A request for the allocation status for a given strategy.
950#[derive(Debug, Clone, Builder, QueryParams)]
951pub struct EarnAllocationStatusRequest {
952    #[builder(required)]
953    #[query(required)]
954    pub strategy_id: String,
955}
956
957/// A request for all earn strategies.
958///
959/// Pagination is available via the `cursor` and `limit` parameters.
960#[derive(Debug, Clone, Builder, QueryParams)]
961pub struct ListEarnStrategiesRequest {
962    pub ascending: Option<bool>,
963    pub asset: Option<String>,
964    pub cursor: Option<String>,
965    pub limit: Option<u16>,
966    pub lock_type: Option<LockType>,
967}
968
969/// A request to list all current earn strategy allocations.
970#[derive(Debug, Clone, Builder, QueryParams)]
971pub struct ListEarnAllocationsRequest {
972    pub ascending: Option<bool>,
973    pub converted_asset: Option<String>,
974    pub hide_zero_allocations: Option<bool>,
975}
976
977#[cfg(test)]
978mod tests {
979    use crate::request_types::{CancelBatchOrdersRequest, IntOrString, OrderFlags, StringCSV};
980    use crate::response_types::OrderFlag;
981
982    #[test]
983    fn test_cancel_batch_order_request_ids() {
984        let request =
985            CancelBatchOrdersRequest::from_tx_ids(vec!["M97YKE-HHCTY-2GRVXU".to_string()]);
986
987        let expected = vec![IntOrString::String("M97YKE-HHCTY-2GRVXU".to_string())];
988        assert_eq!(expected, request.orders);
989    }
990
991    #[test]
992    fn test_cancel_batch_order_request_user_refs() {
993        let request = CancelBatchOrdersRequest::from_user_refs(vec![42]);
994
995        let expected = vec![IntOrString::Int(42)];
996        assert_eq!(expected, request.orders);
997    }
998
999    #[test]
1000    fn test_string_csv_conversions() {
1001        let expected_string_csv = StringCSV::new(vec!["post".to_string()]);
1002
1003        let from_str: StringCSV = "post".into();
1004        let from_string: StringCSV = "post".to_string().into();
1005
1006        let string_ref: &String = &("post".to_string());
1007        let from_string_ref: StringCSV = string_ref.into();
1008
1009        assert_eq!(expected_string_csv, from_str);
1010        assert_eq!(expected_string_csv, from_string);
1011        assert_eq!(expected_string_csv, from_string_ref);
1012    }
1013
1014    #[test]
1015    fn test_order_flag_conversions() {
1016        let expected_order_flag = OrderFlags::new(vec![OrderFlag::NoMarketPriceProtection]);
1017
1018        let order_flags: OrderFlags = OrderFlag::NoMarketPriceProtection.into();
1019
1020        assert_eq!(expected_order_flag, order_flags);
1021    }
1022
1023    #[test]
1024    fn test_int_or_string_conversions() {
1025        let expected_int = IntOrString::Int(42);
1026        let expected_string = IntOrString::String("someString".to_string());
1027
1028        let int: IntOrString = 42.into();
1029        let str: IntOrString = "someString".into();
1030        let string: IntOrString = "someString".to_string().into();
1031
1032        assert_eq!(expected_int, int);
1033        assert_eq!(expected_string, str);
1034        assert_eq!(expected_string, string);
1035    }
1036}