Skip to main content

na_kraken_client/models/
websocket.rs

1//! WebSocket models for the Kraken API
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6/// WebSocket message types
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
8#[serde(rename_all = "lowercase")]
9pub enum WebSocketMessageType {
10    /// Subscribe to a channel
11    Subscribe,
12    
13    /// Unsubscribe from a channel
14    Unsubscribe,
15    
16    /// Ping
17    Ping,
18    
19    /// Pong
20    Pong,
21    
22    /// Heartbeat
23    Heartbeat,
24    
25    /// System status
26    #[serde(rename = "systemStatus")]
27    SystemStatus,
28    
29    /// Subscription status
30    #[serde(rename = "subscriptionStatus")]
31    SubscriptionStatus,
32}
33
34/// WebSocket subscription types
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
36#[serde(rename_all = "lowercase")]
37pub enum WebSocketSubscriptionType {
38    /// Ticker
39    Ticker,
40    
41    /// OHLC
42    OHLC,
43    
44    /// Trade
45    Trade,
46    
47    /// Spread
48    Spread,
49    
50    /// Book
51    Book,
52    
53    /// All tickers
54    #[serde(rename = "*")]
55    All,
56}
57
58/// WebSocket subscription
59#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct WebSocketSubscription {
61    /// Subscription name
62    pub name: WebSocketSubscriptionType,
63    
64    /// Interval for OHLC
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub interval: Option<u32>,
67    
68    /// Depth for book
69    #[serde(skip_serializing_if = "Option::is_none")]
70    pub depth: Option<u32>,
71}
72
73/// WebSocket subscription request
74#[derive(Debug, Clone, Serialize)]
75pub struct WebSocketSubscriptionRequest {
76    /// Event type
77    pub event: WebSocketMessageType,
78    
79    /// Subscription
80    pub subscription: WebSocketSubscription,
81    
82    /// Pairs to subscribe to
83    #[serde(skip_serializing_if = "Option::is_none")]
84    pub pair: Option<Vec<String>>,
85}
86
87impl WebSocketSubscriptionRequest {
88    /// Create a new subscription request with default ticker subscription
89    pub fn new() -> Self {
90        Self {
91            event: WebSocketMessageType::Subscribe,
92            subscription: WebSocketSubscription {
93                name: WebSocketSubscriptionType::Ticker,
94                interval: None,
95                depth: None,
96            },
97            pair: None,
98        }
99    }
100    
101    /// Create a new subscription request with specific subscription type
102    pub fn new_with_type(subscription_type: WebSocketSubscriptionType) -> Self {
103        Self {
104            event: WebSocketMessageType::Subscribe,
105            subscription: WebSocketSubscription {
106                name: subscription_type,
107                interval: None,
108                depth: None,
109            },
110            pair: None,
111        }
112    }
113    
114    /// Set the pairs to subscribe to
115    pub fn with_pairs(mut self, pairs: Vec<String>) -> Self {
116        self.pair = Some(pairs);
117        self
118    }
119    
120    /// Add a pair to subscribe to
121    pub fn add_pair<S: Into<String>>(mut self, pair: S) -> Self {
122        if let Some(pairs) = self.pair.as_mut() {
123            pairs.push(pair.into());
124        } else {
125            self.pair = Some(vec![pair.into()]);
126        }
127        self
128    }
129    
130    /// Set the interval for OHLC
131    pub fn with_interval(mut self, interval: u32) -> Self {
132        self.subscription.interval = Some(interval);
133        self
134    }
135    
136    /// Set the depth for book
137    pub fn with_depth(mut self, depth: u32) -> Self {
138        self.subscription.depth = Some(depth);
139        self
140    }
141    
142    /// Set the subscription type
143    pub fn add_subscription<S: AsRef<str>>(mut self, name: S) -> Self {
144        let name_str = name.as_ref();
145        self.subscription.name = match name_str {
146            "ticker" => WebSocketSubscriptionType::Ticker,
147            "ohlc" => WebSocketSubscriptionType::OHLC,
148            "trade" => WebSocketSubscriptionType::Trade,
149            "spread" => WebSocketSubscriptionType::Spread,
150            "book" => WebSocketSubscriptionType::Book,
151            _ => WebSocketSubscriptionType::Ticker, // Default to ticker
152        };
153        self
154    }
155}
156
157/// WebSocket unsubscription request
158#[derive(Debug, Clone, Serialize)]
159pub struct WebSocketUnsubscriptionRequest {
160    /// Event type
161    pub event: WebSocketMessageType,
162    
163    /// Subscription
164    pub subscription: WebSocketSubscription,
165    
166    /// Pairs to unsubscribe from
167    #[serde(skip_serializing_if = "Option::is_none")]
168    pub pair: Option<Vec<String>>,
169}
170
171impl WebSocketUnsubscriptionRequest {
172    /// Create a new unsubscription request
173    pub fn new(subscription_type: WebSocketSubscriptionType) -> Self {
174        Self {
175            event: WebSocketMessageType::Unsubscribe,
176            subscription: WebSocketSubscription {
177                name: subscription_type,
178                interval: None,
179                depth: None,
180            },
181            pair: None,
182        }
183    }
184    
185    /// Set the pairs to unsubscribe from
186    pub fn with_pairs(mut self, pairs: Vec<String>) -> Self {
187        self.pair = Some(pairs);
188        self
189    }
190    
191    /// Set the interval for OHLC
192    pub fn with_interval(mut self, interval: u32) -> Self {
193        self.subscription.interval = Some(interval);
194        self
195    }
196    
197    /// Set the depth for book
198    pub fn with_depth(mut self, depth: u32) -> Self {
199        self.subscription.depth = Some(depth);
200        self
201    }
202}
203
204/// WebSocket message
205#[derive(Debug, Clone, Deserialize)]
206#[serde(untagged)]
207pub enum WebSocketMessage {
208    /// System status
209    SystemStatus {
210        /// Event type
211        event: String,
212        
213        /// Connection ID
214        #[serde(rename = "connectionID")]
215        connection_id: u64,
216        
217        /// Status
218        status: String,
219        
220        /// Version
221        version: String,
222    },
223    
224    /// Subscription status
225    SubscriptionStatus {
226        /// Channel ID
227        #[serde(rename = "channelID")]
228        channel_id: Option<u64>,
229        
230        /// Channel name
231        #[serde(rename = "channelName")]
232        channel_name: Option<String>,
233        
234        /// Event type
235        event: String,
236        
237        /// Pair
238        pair: Option<String>,
239        
240        /// Status
241        status: String,
242        
243        /// Subscription
244        subscription: WebSocketSubscription,
245    },
246    
247    /// Heartbeat
248    Heartbeat {
249        /// Event type
250        #[serde(rename = "event")]
251        event_type: WebSocketMessageType,
252    },
253    
254    /// Ping
255    Ping {
256        /// Event type
257        #[serde(rename = "event")]
258        event_type: WebSocketMessageType,
259        
260        /// Request ID
261        #[serde(rename = "reqid")]
262        req_id: Option<u64>,
263    },
264    
265    /// Pong
266    Pong {
267        /// Event type
268        #[serde(rename = "event")]
269        event_type: WebSocketMessageType,
270        
271        /// Request ID
272        #[serde(rename = "reqid")]
273        req_id: Option<u64>,
274    },
275    
276    /// Error
277    Error {
278        /// Event type
279        event: String,
280        
281        /// Error message
282        #[serde(rename = "errorMessage")]
283        error_message: String,
284        
285        /// Status
286        status: String,
287        
288        /// Subscription
289        subscription: Option<WebSocketSubscription>,
290        
291        /// Pair
292        pair: Option<String>,
293    },
294    
295    /// Data array
296    DataArray(Vec<Value>),
297    
298    /// Generic message
299    Generic(Value),
300}