1use crate::config::utils::{get_env_optional, get_env_or_default};
8use crate::constants::{
9 DEFAULT_CONNECTION_TIMEOUT_SECS, DEFAULT_HEARTBEAT_INTERVAL, DEFAULT_LOG_LEVEL,
10 DEFAULT_PROD_HOST, DEFAULT_PROD_PORT, DEFAULT_RECONNECT_ATTEMPTS, DEFAULT_RECONNECT_DELAY_SECS,
11 DEFAULT_SENDER_COMP_ID, DEFAULT_SSL_PORT, DEFAULT_TARGET_COMP_ID, DEFAULT_TEST_HOST,
12 DEFAULT_TEST_PORT,
13};
14use crate::error::{DeribitFixError, Result};
15use dotenv::dotenv;
16use serde::{Deserialize, Serialize};
17use std::{fmt::Debug, time::Duration};
18use tracing::debug;
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct DeribitFixConfig {
23 pub username: String,
25 pub password: String,
27 pub host: String,
29 pub port: u16,
31 pub use_ssl: bool,
33 pub test_mode: bool,
35 pub heartbeat_interval: u32,
37 pub connection_timeout: Duration,
39 pub reconnect_attempts: u32,
41 pub reconnect_delay: Duration,
43 pub enable_logging: bool,
45 pub log_level: String,
47 pub sender_comp_id: String,
49 pub target_comp_id: String,
51 pub cancel_on_disconnect: bool,
53 pub app_id: Option<String>,
55 pub app_secret: Option<String>,
57 pub use_wordsafe_tags: Option<bool>,
59 pub deribit_sequential: Option<bool>,
61 pub unsubscribe_execution_reports: Option<bool>,
63 pub connection_only_execution_reports: Option<bool>,
65 pub report_fills_as_exec_reports: Option<bool>,
67 pub display_increment_steps: Option<bool>,
69}
70
71impl DeribitFixConfig {
72 pub fn new() -> Self {
74 match dotenv() {
76 Ok(_) => debug!("Successfully loaded .env file"),
77 Err(e) => debug!("Failed to load .env file: {}", e),
78 }
79
80 let test_mode = get_env_or_default("DERIBIT_TEST_MODE", true);
81 let use_ssl = get_env_or_default("DERIBIT_USE_SSL", false);
82
83 let (default_host, default_port) = if test_mode {
84 if use_ssl {
85 (DEFAULT_TEST_HOST, DEFAULT_SSL_PORT)
86 } else {
87 (DEFAULT_TEST_HOST, DEFAULT_TEST_PORT)
88 }
89 } else if use_ssl {
90 (DEFAULT_PROD_HOST, DEFAULT_SSL_PORT)
91 } else {
92 (DEFAULT_PROD_HOST, DEFAULT_PROD_PORT)
93 };
94
95 Self {
96 username: get_env_or_default("DERIBIT_USERNAME", String::new()),
97 password: get_env_or_default("DERIBIT_PASSWORD", String::new()),
98 host: get_env_or_default("DERIBIT_HOST", default_host.to_string()),
99 port: get_env_or_default("DERIBIT_PORT", default_port),
100 use_ssl,
101 test_mode,
102 heartbeat_interval: get_env_or_default(
103 "DERIBIT_HEARTBEAT_INTERVAL",
104 DEFAULT_HEARTBEAT_INTERVAL,
105 ),
106 connection_timeout: Duration::from_secs(get_env_or_default(
107 "DERIBIT_CONNECTION_TIMEOUT",
108 DEFAULT_CONNECTION_TIMEOUT_SECS,
109 )),
110 reconnect_attempts: get_env_or_default(
111 "DERIBIT_RECONNECT_ATTEMPTS",
112 DEFAULT_RECONNECT_ATTEMPTS,
113 ),
114 reconnect_delay: Duration::from_secs(get_env_or_default(
115 "DERIBIT_RECONNECT_DELAY",
116 DEFAULT_RECONNECT_DELAY_SECS,
117 )),
118 enable_logging: get_env_or_default("DERIBIT_ENABLE_LOGGING", true),
119 log_level: get_env_or_default("DERIBIT_LOG_LEVEL", DEFAULT_LOG_LEVEL.to_string()),
120 sender_comp_id: get_env_or_default(
121 "DERIBIT_SENDER_COMP_ID",
122 DEFAULT_SENDER_COMP_ID.to_string(),
123 ),
124 target_comp_id: get_env_or_default(
125 "DERIBIT_TARGET_COMP_ID",
126 DEFAULT_TARGET_COMP_ID.to_string(),
127 ),
128 cancel_on_disconnect: get_env_or_default("DERIBIT_CANCEL_ON_DISCONNECT", false),
129 app_id: get_env_optional("DERIBIT_APP_ID"),
130 app_secret: get_env_optional("DERIBIT_APP_SECRET"),
131 use_wordsafe_tags: get_env_optional::<String>("DERIBIT_USE_WORDSAFE_TAGS")
132 .map(|v| v == "Y" || v == "true"),
133 deribit_sequential: get_env_optional::<String>("DERIBIT_SEQUENTIAL")
134 .map(|v| v == "Y" || v == "true"),
135 unsubscribe_execution_reports: get_env_optional::<String>(
136 "DERIBIT_UNSUBSCRIBE_EXECUTION_REPORTS",
137 )
138 .map(|v| v == "Y" || v == "true"),
139 connection_only_execution_reports: get_env_optional::<String>(
140 "DERIBIT_CONNECTION_ONLY_EXECUTION_REPORTS",
141 )
142 .map(|v| v == "Y" || v == "true"),
143 report_fills_as_exec_reports: get_env_optional::<String>(
144 "DERIBIT_REPORT_FILLS_AS_EXEC_REPORTS",
145 )
146 .map(|v| v == "Y" || v == "true"),
147 display_increment_steps: get_env_optional::<String>("DERIBIT_DISPLAY_INCREMENT_STEPS")
148 .map(|v| v == "Y" || v == "true"),
149 }
150 }
151
152 pub fn with_credentials(mut self, username: String, password: String) -> Self {
154 self.username = username;
155 self.password = password;
156 self
157 }
158
159 pub fn production() -> Self {
161 let mut config = Self::new();
162 config.test_mode = false;
163 config.host = get_env_or_default("DERIBIT_HOST", DEFAULT_PROD_HOST.to_string());
164 config.port = if config.use_ssl {
165 get_env_or_default("DERIBIT_PORT", DEFAULT_SSL_PORT)
166 } else {
167 get_env_or_default("DERIBIT_PORT", DEFAULT_PROD_PORT)
168 };
169 config
170 }
171
172 pub fn production_with_credentials(username: String, password: String) -> Self {
174 let mut config = Self::production();
175 config.username = username;
176 config.password = password;
177 config
178 }
179
180 pub fn production_ssl() -> Self {
182 let mut config = Self::production();
183 config.use_ssl = true;
184 config.port = get_env_or_default("DERIBIT_PORT", DEFAULT_SSL_PORT);
185 config
186 }
187
188 pub fn test_ssl() -> Self {
190 let mut config = Self::new();
191 config.use_ssl = true;
192 config.port = get_env_or_default("DERIBIT_PORT", DEFAULT_SSL_PORT);
193 config
194 }
195
196 pub fn with_endpoint(mut self, host: String, port: u16) -> Self {
198 self.host = host;
199 self.port = port;
200 self
201 }
202
203 pub fn with_ssl(mut self, use_ssl: bool) -> Self {
205 self.use_ssl = use_ssl;
206 self
207 }
208
209 pub fn with_heartbeat_interval(mut self, interval: u32) -> Self {
211 self.heartbeat_interval = interval;
212 self
213 }
214
215 pub fn with_connection_timeout(mut self, timeout: Duration) -> Self {
217 self.connection_timeout = timeout;
218 self
219 }
220
221 pub fn with_reconnection(mut self, attempts: u32, delay: Duration) -> Self {
223 self.reconnect_attempts = attempts;
224 self.reconnect_delay = delay;
225 self
226 }
227
228 pub fn with_logging(mut self, enabled: bool, level: String) -> Self {
230 self.enable_logging = enabled;
231 self.log_level = level;
232 self
233 }
234
235 pub fn with_session_ids(mut self, sender_comp_id: String, target_comp_id: String) -> Self {
237 self.sender_comp_id = sender_comp_id;
238 self.target_comp_id = target_comp_id;
239 self
240 }
241
242 pub fn with_cancel_on_disconnect(mut self, cancel_on_disconnect: bool) -> Self {
244 self.cancel_on_disconnect = cancel_on_disconnect;
245 self
246 }
247
248 pub fn with_app_credentials(mut self, app_id: String, app_secret: String) -> Self {
250 self.app_id = Some(app_id);
251 self.app_secret = Some(app_secret);
252 self
253 }
254
255 pub fn with_use_wordsafe_tags(mut self, use_wordsafe_tags: bool) -> Self {
257 self.use_wordsafe_tags = Some(use_wordsafe_tags);
258 self
259 }
260
261 pub fn with_deribit_sequential(mut self, deribit_sequential: bool) -> Self {
263 self.deribit_sequential = Some(deribit_sequential);
264 self
265 }
266
267 pub fn with_unsubscribe_execution_reports(mut self, unsubscribe: bool) -> Self {
269 self.unsubscribe_execution_reports = Some(unsubscribe);
270 self
271 }
272
273 pub fn with_connection_only_execution_reports(mut self, connection_only: bool) -> Self {
275 self.connection_only_execution_reports = Some(connection_only);
276 self
277 }
278
279 pub fn with_report_fills_as_exec_reports(mut self, report_fills: bool) -> Self {
281 self.report_fills_as_exec_reports = Some(report_fills);
282 self
283 }
284
285 pub fn with_display_increment_steps(mut self, display_steps: bool) -> Self {
287 self.display_increment_steps = Some(display_steps);
288 self
289 }
290
291 pub fn connection_url(&self) -> String {
293 format!("{}:{}", self.host, self.port)
294 }
295
296 pub fn validate(&self) -> Result<()> {
298 if self.username.is_empty() {
299 return Err(DeribitFixError::Config(
300 "Username cannot be empty".to_string(),
301 ));
302 }
303
304 if self.password.is_empty() {
305 return Err(DeribitFixError::Config(
306 "Password cannot be empty".to_string(),
307 ));
308 }
309
310 if self.host.is_empty() {
311 return Err(DeribitFixError::Config("Host cannot be empty".to_string()));
312 }
313
314 if self.port == 0 {
315 return Err(DeribitFixError::Config(
316 "Port must be greater than 0".to_string(),
317 ));
318 }
319
320 if self.heartbeat_interval == 0 {
321 return Err(DeribitFixError::Config(
322 "Heartbeat interval must be greater than 0".to_string(),
323 ));
324 }
325
326 if self.sender_comp_id.is_empty() {
327 return Err(DeribitFixError::Config(
328 "Sender company ID cannot be empty".to_string(),
329 ));
330 }
331
332 if self.target_comp_id.is_empty() {
333 return Err(DeribitFixError::Config(
334 "Target company ID cannot be empty".to_string(),
335 ));
336 }
337
338 if self.app_id.is_some() && self.app_secret.is_none() {
340 return Err(DeribitFixError::Config(
341 "Application secret is required when app ID is provided".to_string(),
342 ));
343 }
344
345 if self.app_secret.is_some() && self.app_id.is_none() {
346 return Err(DeribitFixError::Config(
347 "Application ID is required when app secret is provided".to_string(),
348 ));
349 }
350
351 Ok(())
352 }
353}
354
355impl Default for DeribitFixConfig {
356 fn default() -> Self {
357 Self::new()
358 }
359}