alpaca_websocket/
config.rs

1//! WebSocket configuration types.
2
3/// Configuration for WebSocket connections.
4#[derive(Debug, Clone)]
5pub struct WebSocketConfig {
6    /// Whether automatic reconnection is enabled.
7    pub reconnect_enabled: bool,
8    /// Maximum number of reconnection attempts.
9    pub reconnect_max_attempts: u32,
10    /// Base delay between reconnection attempts in milliseconds.
11    pub reconnect_base_delay_ms: u64,
12    /// Maximum delay between reconnection attempts in milliseconds.
13    pub reconnect_max_delay_ms: u64,
14    /// Interval for sending ping messages in milliseconds.
15    pub ping_interval_ms: u64,
16    /// Size of the message buffer.
17    pub message_buffer_size: usize,
18    /// Connection timeout in milliseconds.
19    pub connection_timeout_ms: u64,
20}
21
22impl Default for WebSocketConfig {
23    fn default() -> Self {
24        Self {
25            reconnect_enabled: true,
26            reconnect_max_attempts: 10,
27            reconnect_base_delay_ms: 1000,
28            reconnect_max_delay_ms: 60000,
29            ping_interval_ms: 30000,
30            message_buffer_size: 1000,
31            connection_timeout_ms: 10000,
32        }
33    }
34}
35
36impl WebSocketConfig {
37    /// Create a new configuration with default values.
38    #[must_use]
39    pub fn new() -> Self {
40        Self::default()
41    }
42
43    /// Disable automatic reconnection.
44    #[must_use]
45    pub fn no_reconnect(mut self) -> Self {
46        self.reconnect_enabled = false;
47        self
48    }
49
50    /// Set maximum reconnection attempts.
51    #[must_use]
52    pub fn max_reconnect_attempts(mut self, attempts: u32) -> Self {
53        self.reconnect_max_attempts = attempts;
54        self
55    }
56
57    /// Set base delay for reconnection in milliseconds.
58    #[must_use]
59    pub fn reconnect_base_delay(mut self, delay_ms: u64) -> Self {
60        self.reconnect_base_delay_ms = delay_ms;
61        self
62    }
63
64    /// Set ping interval in milliseconds.
65    #[must_use]
66    pub fn ping_interval(mut self, interval_ms: u64) -> Self {
67        self.ping_interval_ms = interval_ms;
68        self
69    }
70
71    /// Set message buffer size.
72    #[must_use]
73    pub fn buffer_size(mut self, size: usize) -> Self {
74        self.message_buffer_size = size;
75        self
76    }
77
78    /// Set connection timeout in milliseconds.
79    #[must_use]
80    pub fn connection_timeout(mut self, timeout_ms: u64) -> Self {
81        self.connection_timeout_ms = timeout_ms;
82        self
83    }
84}
85
86/// WebSocket stream type.
87#[derive(Debug, Clone, Copy, PartialEq, Eq)]
88pub enum StreamType {
89    /// Stock market data stream (IEX or SIP).
90    Stocks,
91    /// Cryptocurrency market data stream.
92    Crypto,
93    /// Options market data stream.
94    Options,
95    /// News stream.
96    News,
97    /// Trading updates stream (order fills, etc.).
98    Trading,
99}
100
101impl StreamType {
102    /// Get the WebSocket URL for this stream type.
103    #[must_use]
104    pub fn url(&self, is_paper: bool) -> &'static str {
105        match self {
106            StreamType::Stocks => {
107                if is_paper {
108                    "wss://stream.data.alpaca.markets/v2/iex"
109                } else {
110                    "wss://stream.data.alpaca.markets/v2/sip"
111                }
112            }
113            StreamType::Crypto => "wss://stream.data.alpaca.markets/v1beta3/crypto/us",
114            StreamType::Options => "wss://stream.data.alpaca.markets/v1beta1/options",
115            StreamType::News => "wss://stream.data.alpaca.markets/v1beta1/news",
116            StreamType::Trading => {
117                if is_paper {
118                    "wss://paper-api.alpaca.markets/stream"
119                } else {
120                    "wss://api.alpaca.markets/stream"
121                }
122            }
123        }
124    }
125}
126
127/// Connection state for WebSocket.
128#[derive(Debug, Clone, Copy, PartialEq, Eq)]
129pub enum ConnectionState {
130    /// Not connected.
131    Disconnected,
132    /// Attempting to connect.
133    Connecting,
134    /// Connected and authenticated.
135    Connected,
136    /// Reconnecting after disconnect.
137    Reconnecting,
138    /// Connection failed permanently.
139    Failed,
140}
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145
146    #[test]
147    fn test_websocket_config_default() {
148        let config = WebSocketConfig::default();
149        assert!(config.reconnect_enabled);
150        assert_eq!(config.reconnect_max_attempts, 10);
151        assert_eq!(config.reconnect_base_delay_ms, 1000);
152    }
153
154    #[test]
155    fn test_websocket_config_builder() {
156        let config = WebSocketConfig::new()
157            .no_reconnect()
158            .max_reconnect_attempts(5)
159            .ping_interval(15000)
160            .buffer_size(500);
161
162        assert!(!config.reconnect_enabled);
163        assert_eq!(config.reconnect_max_attempts, 5);
164        assert_eq!(config.ping_interval_ms, 15000);
165        assert_eq!(config.message_buffer_size, 500);
166    }
167
168    #[test]
169    fn test_stream_type_urls() {
170        assert_eq!(
171            StreamType::Stocks.url(true),
172            "wss://stream.data.alpaca.markets/v2/iex"
173        );
174        assert_eq!(
175            StreamType::Stocks.url(false),
176            "wss://stream.data.alpaca.markets/v2/sip"
177        );
178        assert_eq!(
179            StreamType::Crypto.url(true),
180            "wss://stream.data.alpaca.markets/v1beta3/crypto/us"
181        );
182        assert_eq!(
183            StreamType::Options.url(true),
184            "wss://stream.data.alpaca.markets/v1beta1/options"
185        );
186    }
187}