alpaca_fix/
config.rs

1//! FIX protocol configuration types.
2
3use serde::{Deserialize, Serialize};
4
5/// FIX protocol version.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
7pub enum FixVersion {
8    /// FIX 4.2.
9    #[serde(rename = "FIX.4.2")]
10    Fix42,
11    /// FIX 4.4.
12    #[default]
13    #[serde(rename = "FIX.4.4")]
14    Fix44,
15}
16
17impl FixVersion {
18    /// Get the BeginString value for this version.
19    #[must_use]
20    pub fn begin_string(&self) -> &'static str {
21        match self {
22            Self::Fix42 => "FIX.4.2",
23            Self::Fix44 => "FIX.4.4",
24        }
25    }
26}
27
28impl std::fmt::Display for FixVersion {
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        write!(f, "{}", self.begin_string())
31    }
32}
33
34/// FIX session configuration.
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct FixConfig {
37    /// FIX protocol version.
38    pub version: FixVersion,
39    /// Sender CompID.
40    pub sender_comp_id: String,
41    /// Target CompID.
42    pub target_comp_id: String,
43    /// FIX server host.
44    pub host: String,
45    /// FIX server port.
46    pub port: u16,
47    /// Heartbeat interval in seconds.
48    pub heartbeat_interval_secs: u32,
49    /// Enable automatic reconnection.
50    pub reconnect_enabled: bool,
51    /// Maximum reconnection attempts.
52    pub reconnect_max_attempts: u32,
53    /// Reconnection delay in milliseconds.
54    pub reconnect_delay_ms: u64,
55    /// Enable message logging.
56    pub message_logging: bool,
57    /// Reset sequence numbers on logon.
58    pub reset_on_logon: bool,
59}
60
61impl Default for FixConfig {
62    fn default() -> Self {
63        Self {
64            version: FixVersion::Fix44,
65            sender_comp_id: String::new(),
66            target_comp_id: "ALPACA".to_string(),
67            host: "fix.alpaca.markets".to_string(),
68            port: 5001,
69            heartbeat_interval_secs: 30,
70            reconnect_enabled: true,
71            reconnect_max_attempts: 5,
72            reconnect_delay_ms: 1000,
73            message_logging: false,
74            reset_on_logon: false,
75        }
76    }
77}
78
79impl FixConfig {
80    /// Create a new configuration builder.
81    #[must_use]
82    pub fn builder() -> FixConfigBuilder {
83        FixConfigBuilder::default()
84    }
85}
86
87/// Builder for FIX configuration.
88#[derive(Debug, Default)]
89pub struct FixConfigBuilder {
90    config: FixConfig,
91}
92
93impl FixConfigBuilder {
94    /// Set FIX version.
95    #[must_use]
96    pub fn version(mut self, version: FixVersion) -> Self {
97        self.config.version = version;
98        self
99    }
100
101    /// Set sender CompID.
102    #[must_use]
103    pub fn sender_comp_id(mut self, id: &str) -> Self {
104        self.config.sender_comp_id = id.to_string();
105        self
106    }
107
108    /// Set target CompID.
109    #[must_use]
110    pub fn target_comp_id(mut self, id: &str) -> Self {
111        self.config.target_comp_id = id.to_string();
112        self
113    }
114
115    /// Set server host.
116    #[must_use]
117    pub fn host(mut self, host: &str) -> Self {
118        self.config.host = host.to_string();
119        self
120    }
121
122    /// Set server port.
123    #[must_use]
124    pub fn port(mut self, port: u16) -> Self {
125        self.config.port = port;
126        self
127    }
128
129    /// Set heartbeat interval in seconds.
130    #[must_use]
131    pub fn heartbeat_interval_secs(mut self, secs: u32) -> Self {
132        self.config.heartbeat_interval_secs = secs;
133        self
134    }
135
136    /// Enable or disable reconnection.
137    #[must_use]
138    pub fn reconnect_enabled(mut self, enabled: bool) -> Self {
139        self.config.reconnect_enabled = enabled;
140        self
141    }
142
143    /// Set maximum reconnection attempts.
144    #[must_use]
145    pub fn reconnect_max_attempts(mut self, attempts: u32) -> Self {
146        self.config.reconnect_max_attempts = attempts;
147        self
148    }
149
150    /// Set reconnection delay in milliseconds.
151    #[must_use]
152    pub fn reconnect_delay_ms(mut self, ms: u64) -> Self {
153        self.config.reconnect_delay_ms = ms;
154        self
155    }
156
157    /// Enable or disable message logging.
158    #[must_use]
159    pub fn message_logging(mut self, enabled: bool) -> Self {
160        self.config.message_logging = enabled;
161        self
162    }
163
164    /// Enable or disable sequence reset on logon.
165    #[must_use]
166    pub fn reset_on_logon(mut self, reset: bool) -> Self {
167        self.config.reset_on_logon = reset;
168        self
169    }
170
171    /// Build the configuration.
172    #[must_use]
173    pub fn build(self) -> FixConfig {
174        self.config
175    }
176}
177
178#[cfg(test)]
179mod tests {
180    use super::*;
181
182    #[test]
183    fn test_fix_version_begin_string() {
184        assert_eq!(FixVersion::Fix42.begin_string(), "FIX.4.2");
185        assert_eq!(FixVersion::Fix44.begin_string(), "FIX.4.4");
186    }
187
188    #[test]
189    fn test_fix_config_builder() {
190        let config = FixConfig::builder()
191            .version(FixVersion::Fix42)
192            .sender_comp_id("SENDER")
193            .target_comp_id("TARGET")
194            .host("localhost")
195            .port(9000)
196            .heartbeat_interval_secs(60)
197            .build();
198
199        assert_eq!(config.version, FixVersion::Fix42);
200        assert_eq!(config.sender_comp_id, "SENDER");
201        assert_eq!(config.target_comp_id, "TARGET");
202        assert_eq!(config.host, "localhost");
203        assert_eq!(config.port, 9000);
204        assert_eq!(config.heartbeat_interval_secs, 60);
205    }
206
207    #[test]
208    fn test_fix_config_default() {
209        let config = FixConfig::default();
210        assert_eq!(config.version, FixVersion::Fix44);
211        assert_eq!(config.target_comp_id, "ALPACA");
212        assert_eq!(config.port, 5001);
213    }
214}