Skip to main content

kraken_api_client/futures/
types.rs

1//! Futures-specific domain types.
2//!
3//! This module contains types specific to Futures trading that differ from
4//! or extend the Spot API types.
5
6use rust_decimal::Decimal;
7use serde::{Deserialize, Serialize};
8
9use crate::types::common::BuySell;
10
11
12// Contract Types
13
14
15/// Type of futures contract.
16#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
17#[serde(rename_all = "lowercase")]
18pub enum ContractType {
19    /// Perpetual contract (no expiry)
20    #[default]
21    Perpetual,
22    /// Fixed maturity contract (has expiry date)
23    #[serde(alias = "futures_inverse", alias = "futures_vanilla")]
24    FixedMaturity,
25    /// Index (not tradeable)
26    #[serde(alias = "spot index")]
27    Index,
28}
29
30/// Futures order type.
31#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
32#[serde(rename_all = "snake_case")]
33pub enum FuturesOrderType {
34    /// Limit order
35    #[serde(alias = "lmt")]
36    #[default]
37    Limit,
38    /// Market order
39    #[serde(alias = "mkt")]
40    Market,
41    /// Stop order (stop-loss)
42    #[serde(alias = "stp")]
43    Stop,
44    /// Take profit order
45    TakeProfit,
46    /// Immediate or cancel
47    #[serde(alias = "ioc")]
48    ImmediateOrCancel,
49    /// Post-only (maker only)
50    PostOnly,
51}
52
53/// Order status for futures orders.
54#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
55#[serde(rename_all = "lowercase")]
56pub enum FuturesOrderStatus {
57    /// Order is in the book
58    #[serde(alias = "untouched")]
59    #[default]
60    Open,
61    /// Order is partially filled
62    #[serde(alias = "partiallyFilled")]
63    PartiallyFilled,
64    /// Order is fully filled
65    Filled,
66    /// Order was cancelled
67    Cancelled,
68}
69
70/// Fill type classification.
71#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
72#[serde(rename_all = "lowercase")]
73pub enum FillType {
74    /// Maker (provided liquidity)
75    Maker,
76    /// Taker (removed liquidity)
77    Taker,
78    /// Liquidation
79    Liquidation,
80    /// Assignment (options)
81    Assignee,
82    /// Assigned from (options)
83    Assignor,
84}
85
86
87// Account Types
88
89
90/// Futures account type.
91#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
92#[serde(rename_all = "camelCase")]
93pub enum AccountType {
94    /// Cash account (no leverage)
95    #[serde(alias = "cashAccount")]
96    #[default]
97    Cash,
98    /// Margin account (single currency collateral)
99    #[serde(alias = "marginAccount")]
100    Margin,
101    /// Multi-collateral margin account
102    #[serde(alias = "multiCollateralMarginAccount")]
103    MultiCollateral,
104    /// Flex futures (cross-margined)
105    FlexFutures,
106}
107
108
109// Position and Order Structs
110
111
112/// A futures position.
113#[derive(Debug, Clone, Serialize, Deserialize)]
114#[serde(rename_all = "camelCase")]
115pub struct FuturesPosition {
116    /// The futures symbol (e.g., "PI_XBTUSD")
117    pub symbol: String,
118    /// Position side
119    pub side: BuySell,
120    /// Position size (positive)
121    pub size: Decimal,
122    /// Average entry price
123    #[serde(alias = "price")]
124    pub entry_price: Decimal,
125    /// Current mark price
126    #[serde(default)]
127    pub mark_price: Option<Decimal>,
128    /// Liquidation price (None for cash accounts)
129    #[serde(default)]
130    pub liquidation_threshold: Option<Decimal>,
131    /// Unrealized PnL
132    #[serde(default)]
133    pub unrealized_pnl: Option<Decimal>,
134    /// Unrealized funding (perpetuals only)
135    #[serde(default, alias = "unrealizedFunding")]
136    pub unrealized_funding: Option<Decimal>,
137    /// Initial margin requirement
138    #[serde(default)]
139    pub initial_margin: Option<Decimal>,
140    /// Maintenance margin requirement
141    #[serde(default)]
142    pub maintenance_margin: Option<Decimal>,
143    /// Effective leverage
144    #[serde(default)]
145    pub effective_leverage: Option<Decimal>,
146    /// Return on equity percentage
147    #[serde(default)]
148    pub return_on_equity: Option<Decimal>,
149    /// PnL currency for multi-collateral
150    #[serde(default)]
151    pub pnl_currency: Option<String>,
152    /// Maximum fixed leverage for isolated positions
153    #[serde(default, alias = "maxFixedLeverage")]
154    pub max_fixed_leverage: Option<Decimal>,
155    /// Fill time (deprecated but still returned)
156    #[serde(default, alias = "fillTime")]
157    pub fill_time: Option<String>,
158}
159
160/// A futures order.
161#[derive(Debug, Clone, Serialize, Deserialize)]
162#[serde(rename_all = "camelCase")]
163pub struct FuturesOrder {
164    /// Order ID
165    #[serde(alias = "order_id")]
166    pub order_id: String,
167    /// Client order ID (if provided)
168    #[serde(default, alias = "cliOrdId")]
169    pub cli_ord_id: Option<String>,
170    /// Futures symbol
171    pub symbol: String,
172    /// Order side
173    pub side: BuySell,
174    /// Order type
175    #[serde(alias = "orderType")]
176    pub order_type: FuturesOrderType,
177    /// Order status
178    pub status: FuturesOrderStatus,
179    /// Total order size
180    #[serde(alias = "quantity", alias = "qty")]
181    pub size: Decimal,
182    /// Filled size
183    #[serde(default, alias = "filledSize")]
184    pub filled_size: Decimal,
185    /// Unfilled (remaining) size
186    #[serde(default, alias = "unfilledSize")]
187    pub unfilled_size: Decimal,
188    /// Limit price (for limit orders)
189    #[serde(default, alias = "limitPrice")]
190    pub limit_price: Option<Decimal>,
191    /// Stop price (for stop orders)
192    #[serde(default, alias = "stopPrice")]
193    pub stop_price: Option<Decimal>,
194    /// Whether this is a reduce-only order
195    #[serde(default, alias = "reduceOnly")]
196    pub reduce_only: bool,
197    /// Time the order was received
198    #[serde(default, alias = "receivedTime")]
199    pub received_time: Option<String>,
200    /// Last update time
201    #[serde(default, alias = "lastUpdateTime")]
202    pub last_update_time: Option<String>,
203}
204
205/// A fill (trade execution).
206#[derive(Debug, Clone, Serialize, Deserialize)]
207#[serde(rename_all = "camelCase")]
208pub struct FuturesFill {
209    /// Fill ID
210    #[serde(alias = "fill_id")]
211    pub fill_id: String,
212    /// Order ID this fill belongs to
213    #[serde(alias = "order_id")]
214    pub order_id: String,
215    /// Client order ID
216    #[serde(default, alias = "cliOrdId")]
217    pub cli_ord_id: Option<String>,
218    /// Futures symbol
219    pub symbol: String,
220    /// Fill side
221    pub side: BuySell,
222    /// Fill size
223    pub size: Decimal,
224    /// Fill price
225    pub price: Decimal,
226    /// Fill type (maker/taker/liquidation)
227    #[serde(alias = "fillType")]
228    pub fill_type: FillType,
229    /// Fill timestamp
230    #[serde(alias = "fillTime")]
231    pub fill_time: String,
232}
233
234
235// Account Information
236
237
238/// Futures account summary.
239#[derive(Debug, Clone, Serialize, Deserialize)]
240#[serde(rename_all = "camelCase")]
241pub struct FuturesAccount {
242    /// Account type
243    #[serde(alias = "type")]
244    pub account_type: AccountType,
245    /// Base currency for this account
246    #[serde(default)]
247    pub currency: Option<String>,
248    /// Balances by currency
249    #[serde(default)]
250    pub balances: Option<std::collections::HashMap<String, Decimal>>,
251    /// Margin requirements
252    #[serde(default)]
253    pub margin_requirements: Option<MarginRequirements>,
254    /// Trigger estimates (liquidation prices)
255    #[serde(default)]
256    pub trigger_estimates: Option<TriggerEstimates>,
257    /// Auxiliary account info
258    #[serde(default)]
259    pub auxiliary: Option<AuxiliaryInfo>,
260}
261
262/// Margin requirements for an account.
263#[derive(Debug, Clone, Serialize, Deserialize)]
264pub struct MarginRequirements {
265    /// Initial margin
266    pub im: Decimal,
267    /// Maintenance margin
268    pub mm: Decimal,
269    /// Liquidation threshold
270    pub lt: Decimal,
271    /// Termination threshold
272    pub tt: Decimal,
273}
274
275/// Trigger price estimates.
276#[derive(Debug, Clone, Serialize, Deserialize)]
277pub struct TriggerEstimates {
278    /// Initial margin trigger
279    pub im: Decimal,
280    /// Maintenance margin trigger
281    pub mm: Decimal,
282    /// Liquidation trigger
283    pub lt: Decimal,
284    /// Termination trigger
285    pub tt: Decimal,
286}
287
288/// Auxiliary account information.
289#[derive(Debug, Clone, Serialize, Deserialize)]
290pub struct AuxiliaryInfo {
291    /// Available funds
292    pub af: Decimal,
293    /// Funding component
294    #[serde(default)]
295    pub funding: Option<Decimal>,
296    /// Profit/Loss
297    pub pnl: Decimal,
298    /// Portfolio value
299    pub pv: Decimal,
300    /// USD equivalent
301    #[serde(default)]
302    pub usd: Option<Decimal>,
303}
304
305/// Multi-collateral (Flex) account info.
306#[derive(Debug, Clone, Serialize, Deserialize)]
307#[serde(rename_all = "camelCase")]
308pub struct FlexAccountInfo {
309    /// Balances by currency
310    pub currencies: std::collections::HashMap<String, FlexCurrencyBalance>,
311    /// Total balance value in USD
312    pub balance_value: Decimal,
313    /// Total portfolio value
314    pub portfolio_value: Decimal,
315    /// Total collateral value
316    pub collateral_value: Decimal,
317    /// Initial margin requirement
318    pub initial_margin: Decimal,
319    /// Maintenance margin requirement
320    pub maintenance_margin: Decimal,
321    /// Total PnL
322    pub pnl: Decimal,
323    /// Unrealized funding
324    #[serde(default)]
325    pub unrealized_funding: Option<Decimal>,
326    /// Available margin
327    pub available_margin: Decimal,
328    /// Margin equity
329    pub margin_equity: Decimal,
330}
331
332/// Balance info for a single currency in flex account.
333#[derive(Debug, Clone, Serialize, Deserialize)]
334#[serde(rename_all = "camelCase")]
335pub struct FlexCurrencyBalance {
336    /// Quantity held
337    pub quantity: Decimal,
338    /// Value in USD
339    pub value: Decimal,
340    /// Collateral value (after haircut)
341    #[serde(alias = "collateral_value")]
342    pub collateral_value: Decimal,
343    /// Available for withdrawal
344    pub available: Decimal,
345    /// Haircut percentage
346    #[serde(default)]
347    pub haircut: Option<Decimal>,
348    /// Conversion spread
349    #[serde(default)]
350    pub conversion_spread: Option<Decimal>,
351}
352
353
354// Instrument Information
355
356
357/// A futures instrument (contract).
358#[derive(Debug, Clone, Serialize, Deserialize)]
359#[serde(rename_all = "camelCase")]
360pub struct FuturesInstrument {
361    /// Symbol (e.g., "PI_XBTUSD")
362    pub symbol: String,
363    /// Trading pair (e.g., "XBT:USD")
364    #[serde(default)]
365    pub pair: Option<String>,
366    /// Contract type
367    #[serde(default, alias = "type")]
368    pub contract_type: Option<ContractType>,
369    /// Whether the instrument is tradeable
370    #[serde(default)]
371    pub tradeable: Option<bool>,
372    /// Tick size (minimum price increment)
373    #[serde(default, alias = "tickSize")]
374    pub tick_size: Option<Decimal>,
375    /// Contract size (value per unit)
376    #[serde(default, alias = "contractSize")]
377    pub contract_size: Option<Decimal>,
378    /// Maximum leverage
379    #[serde(default)]
380    pub leverage: Option<String>,
381    /// Margin type
382    #[serde(default, alias = "marginLevels")]
383    pub margin_levels: Option<Vec<MarginLevel>>,
384    /// Maturity time (for fixed maturity contracts)
385    #[serde(default, alias = "lastTradingTime")]
386    pub maturity_time: Option<String>,
387    /// Opening time
388    #[serde(default, alias = "openingDate")]
389    pub opening_date: Option<String>,
390    /// Category tag (perpetual, month, quarter)
391    #[serde(default)]
392    pub tag: Option<String>,
393    /// Post-only mode
394    #[serde(default, alias = "postOnly")]
395    pub post_only: Option<bool>,
396}
397
398/// Margin level tier.
399#[derive(Debug, Clone, Serialize, Deserialize)]
400#[serde(rename_all = "camelCase")]
401pub struct MarginLevel {
402    /// Number of contracts
403    pub contracts: Decimal,
404    /// Initial margin percentage
405    #[serde(alias = "initialMargin")]
406    pub initial_margin: Decimal,
407    /// Maintenance margin percentage
408    #[serde(alias = "maintenanceMargin")]
409    pub maintenance_margin: Decimal,
410}
411
412
413// Ticker Data
414
415
416/// Futures ticker data.
417#[derive(Debug, Clone, Serialize, Deserialize)]
418#[serde(rename_all = "camelCase")]
419pub struct FuturesTicker {
420    /// Symbol
421    pub symbol: String,
422    /// Trading pair
423    #[serde(default)]
424    pub pair: Option<String>,
425    /// Last trade price
426    pub last: Decimal,
427    /// Best bid price
428    #[serde(default)]
429    pub bid: Option<Decimal>,
430    /// Best bid size
431    #[serde(default, alias = "bidSize")]
432    pub bid_size: Option<Decimal>,
433    /// Best ask price
434    #[serde(default)]
435    pub ask: Option<Decimal>,
436    /// Best ask size
437    #[serde(default, alias = "askSize")]
438    pub ask_size: Option<Decimal>,
439    /// 24h volume
440    #[serde(default)]
441    pub volume: Option<Decimal>,
442    /// 24h volume in quote currency
443    #[serde(default, alias = "volumeQuote")]
444    pub volume_quote: Option<Decimal>,
445    /// Open interest
446    #[serde(default, alias = "openInterest")]
447    pub open_interest: Option<Decimal>,
448    /// 24h open price
449    #[serde(default)]
450    pub open: Option<Decimal>,
451    /// 24h high price
452    #[serde(default)]
453    pub high: Option<Decimal>,
454    /// 24h low price
455    #[serde(default)]
456    pub low: Option<Decimal>,
457    /// 24h change percentage
458    #[serde(default)]
459    pub change: Option<Decimal>,
460    /// Mark price
461    #[serde(default, alias = "markPrice")]
462    pub mark_price: Option<Decimal>,
463    /// Index price
464    #[serde(default, alias = "index")]
465    pub index_price: Option<Decimal>,
466    /// Current funding rate (perpetuals)
467    #[serde(default, alias = "fundingRate")]
468    pub funding_rate: Option<Decimal>,
469    /// Predicted next funding rate
470    #[serde(default, alias = "fundingRatePrediction")]
471    pub funding_rate_prediction: Option<Decimal>,
472    /// Time until next funding
473    #[serde(default, alias = "nextFundingRateTime")]
474    pub next_funding_rate_time: Option<i64>,
475    /// Days to maturity
476    #[serde(default)]
477    pub dtm: Option<i32>,
478    /// Maturity time
479    #[serde(default, alias = "maturityTime")]
480    pub maturity_time: Option<i64>,
481    /// Contract tag
482    #[serde(default)]
483    pub tag: Option<String>,
484    /// Market suspended
485    #[serde(default)]
486    pub suspended: Option<bool>,
487    /// Post-only mode
488    #[serde(default, alias = "postOnly")]
489    pub post_only: Option<bool>,
490    /// Timestamp
491    #[serde(default)]
492    pub time: Option<i64>,
493}
494
495
496// Order Book
497
498
499/// Futures order book.
500#[derive(Debug, Clone, Serialize, Deserialize)]
501#[serde(rename_all = "camelCase")]
502pub struct FuturesOrderBook {
503    /// Symbol
504    pub symbol: String,
505    /// Bids (price, size)
506    pub bids: Vec<BookLevel>,
507    /// Asks (price, size)
508    pub asks: Vec<BookLevel>,
509    /// Server time
510    #[serde(default, alias = "serverTime")]
511    pub server_time: Option<String>,
512}
513
514/// A single level in the order book.
515#[derive(Debug, Clone, Serialize, Deserialize)]
516pub struct BookLevel {
517    /// Price level
518    pub price: Decimal,
519    /// Size at this price
520    #[serde(alias = "qty", alias = "quantity")]
521    pub size: Decimal,
522}
523
524
525// Trade History
526
527
528/// A public trade.
529#[derive(Debug, Clone, Serialize, Deserialize)]
530#[serde(rename_all = "camelCase")]
531pub struct FuturesTrade {
532    /// Trade ID
533    #[serde(alias = "uid")]
534    pub trade_id: String,
535    /// Trade price
536    pub price: Decimal,
537    /// Trade size
538    #[serde(alias = "qty", alias = "quantity")]
539    pub size: Decimal,
540    /// Trade side
541    pub side: BuySell,
542    /// Trade timestamp
543    #[serde(alias = "time")]
544    pub timestamp: String,
545}
546
547#[cfg(test)]
548mod tests {
549    use super::*;
550
551    #[test]
552    fn test_deserialize_position() {
553        let json = r#"{
554            "symbol": "PI_XBTUSD",
555            "side": "buy",
556            "size": "1000",
557            "price": "50000.0",
558            "unrealizedFunding": "0.001"
559        }"#;
560
561        let pos: FuturesPosition = serde_json::from_str(json).unwrap();
562        assert_eq!(pos.symbol, "PI_XBTUSD");
563        assert_eq!(pos.side, BuySell::Buy);
564        assert_eq!(pos.size, Decimal::from(1000));
565    }
566
567    #[test]
568    fn test_deserialize_order() {
569        let json = r#"{
570            "order_id": "abc123",
571            "symbol": "PI_XBTUSD",
572            "side": "sell",
573            "orderType": "lmt",
574            "status": "open",
575            "quantity": "500",
576            "filledSize": "0",
577            "unfilledSize": "500",
578            "limitPrice": "55000.0",
579            "reduceOnly": true
580        }"#;
581
582        let order: FuturesOrder = serde_json::from_str(json).unwrap();
583        assert_eq!(order.order_id, "abc123");
584        assert!(order.reduce_only);
585        assert_eq!(order.limit_price, Some(Decimal::from(55000)));
586    }
587
588    #[test]
589    fn test_deserialize_fill() {
590        let json = r#"{
591            "fill_id": "fill123",
592            "order_id": "order456",
593            "symbol": "PI_ETHUSD",
594            "side": "buy",
595            "size": "10",
596            "price": "3500.5",
597            "fillType": "taker",
598            "fillTime": "2024-01-15T10:30:00Z"
599        }"#;
600
601        let fill: FuturesFill = serde_json::from_str(json).unwrap();
602        assert_eq!(fill.fill_type, FillType::Taker);
603    }
604
605    #[test]
606    fn test_deserialize_ticker() {
607        let json = r#"{
608            "symbol": "PI_XBTUSD",
609            "last": "50000.0",
610            "bid": "49999.5",
611            "ask": "50000.5",
612            "fundingRate": "0.0001",
613            "openInterest": "1000000"
614        }"#;
615
616        let ticker: FuturesTicker = serde_json::from_str(json).unwrap();
617        assert_eq!(ticker.symbol, "PI_XBTUSD");
618        assert!(ticker.funding_rate.is_some());
619    }
620
621    #[test]
622    fn test_contract_type_serde() {
623        assert_eq!(
624            serde_json::from_str::<ContractType>(r#""perpetual""#).unwrap(),
625            ContractType::Perpetual
626        );
627    }
628
629    #[test]
630    fn test_order_type_serde() {
631        // Test alias
632        assert_eq!(
633            serde_json::from_str::<FuturesOrderType>(r#""lmt""#).unwrap(),
634            FuturesOrderType::Limit
635        );
636        // Test snake_case
637        assert_eq!(
638            serde_json::from_str::<FuturesOrderType>(r#""take_profit""#).unwrap(),
639            FuturesOrderType::TakeProfit
640        );
641    }
642}