#[derive(Debug, Clone)]
pub struct WebSocketConfig {
pub reconnect_enabled: bool,
pub reconnect_max_attempts: u32,
pub reconnect_base_delay_ms: u64,
pub reconnect_max_delay_ms: u64,
pub ping_interval_ms: u64,
pub message_buffer_size: usize,
pub connection_timeout_ms: u64,
}
impl Default for WebSocketConfig {
fn default() -> Self {
Self {
reconnect_enabled: true,
reconnect_max_attempts: 10,
reconnect_base_delay_ms: 1000,
reconnect_max_delay_ms: 60000,
ping_interval_ms: 30000,
message_buffer_size: 1000,
connection_timeout_ms: 10000,
}
}
}
impl WebSocketConfig {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn no_reconnect(mut self) -> Self {
self.reconnect_enabled = false;
self
}
#[must_use]
pub fn max_reconnect_attempts(mut self, attempts: u32) -> Self {
self.reconnect_max_attempts = attempts;
self
}
#[must_use]
pub fn reconnect_base_delay(mut self, delay_ms: u64) -> Self {
self.reconnect_base_delay_ms = delay_ms;
self
}
#[must_use]
pub fn ping_interval(mut self, interval_ms: u64) -> Self {
self.ping_interval_ms = interval_ms;
self
}
#[must_use]
pub fn buffer_size(mut self, size: usize) -> Self {
self.message_buffer_size = size;
self
}
#[must_use]
pub fn connection_timeout(mut self, timeout_ms: u64) -> Self {
self.connection_timeout_ms = timeout_ms;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StreamType {
Stocks,
Crypto,
Options,
News,
Trading,
}
impl StreamType {
#[must_use]
pub fn url(&self, is_paper: bool) -> &'static str {
match self {
StreamType::Stocks => {
if is_paper {
"wss://stream.data.alpaca.markets/v2/iex"
} else {
"wss://stream.data.alpaca.markets/v2/sip"
}
}
StreamType::Crypto => "wss://stream.data.alpaca.markets/v1beta3/crypto/us",
StreamType::Options => "wss://stream.data.alpaca.markets/v1beta1/options",
StreamType::News => "wss://stream.data.alpaca.markets/v1beta1/news",
StreamType::Trading => {
if is_paper {
"wss://paper-api.alpaca.markets/stream"
} else {
"wss://api.alpaca.markets/stream"
}
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConnectionState {
Disconnected,
Connecting,
Connected,
Reconnecting,
Failed,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_websocket_config_default() {
let config = WebSocketConfig::default();
assert!(config.reconnect_enabled);
assert_eq!(config.reconnect_max_attempts, 10);
assert_eq!(config.reconnect_base_delay_ms, 1000);
}
#[test]
fn test_websocket_config_builder() {
let config = WebSocketConfig::new()
.no_reconnect()
.max_reconnect_attempts(5)
.ping_interval(15000)
.buffer_size(500);
assert!(!config.reconnect_enabled);
assert_eq!(config.reconnect_max_attempts, 5);
assert_eq!(config.ping_interval_ms, 15000);
assert_eq!(config.message_buffer_size, 500);
}
#[test]
fn test_stream_type_urls() {
assert_eq!(
StreamType::Stocks.url(true),
"wss://stream.data.alpaca.markets/v2/iex"
);
assert_eq!(
StreamType::Stocks.url(false),
"wss://stream.data.alpaca.markets/v2/sip"
);
assert_eq!(
StreamType::Crypto.url(true),
"wss://stream.data.alpaca.markets/v1beta3/crypto/us"
);
assert_eq!(
StreamType::Options.url(true),
"wss://stream.data.alpaca.markets/v1beta1/options"
);
}
}