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 deribit_base::{impl_json_debug_pretty, impl_json_display};
16use dotenv::dotenv;
17use serde::{Deserialize, Serialize};
18use std::time::Duration;
19use tracing::debug;
20
21#[derive(Clone, Serialize, Deserialize)]
23pub struct DeribitFixConfig {
24 pub username: String,
26 pub password: String,
28 pub host: String,
30 pub port: u16,
32 pub use_ssl: bool,
34 pub test_mode: bool,
36 pub heartbeat_interval: u32,
38 pub connection_timeout: Duration,
40 pub reconnect_attempts: u32,
42 pub reconnect_delay: Duration,
44 pub enable_logging: bool,
46 pub log_level: String,
48 pub sender_comp_id: String,
50 pub target_comp_id: String,
52 pub cancel_on_disconnect: bool,
54 pub app_id: Option<String>,
56 pub app_secret: Option<String>,
58 pub use_wordsafe_tags: Option<bool>,
60 pub deribit_sequential: Option<bool>,
62 pub unsubscribe_execution_reports: Option<bool>,
64 pub connection_only_execution_reports: Option<bool>,
66 pub report_fills_as_exec_reports: Option<bool>,
68 pub display_increment_steps: Option<bool>,
70}
71
72impl DeribitFixConfig {
73 pub fn new() -> Self {
75 match dotenv() {
77 Ok(_) => debug!("Successfully loaded .env file"),
78 Err(e) => debug!("Failed to load .env file: {}", e),
79 }
80
81 let test_mode = get_env_or_default("DERIBIT_TEST_MODE", true);
82 let use_ssl = get_env_or_default("DERIBIT_USE_SSL", false);
83
84 let (default_host, default_port) = if test_mode {
85 if use_ssl {
86 (DEFAULT_TEST_HOST, DEFAULT_SSL_PORT)
87 } else {
88 (DEFAULT_TEST_HOST, DEFAULT_TEST_PORT)
89 }
90 } else if use_ssl {
91 (DEFAULT_PROD_HOST, DEFAULT_SSL_PORT)
92 } else {
93 (DEFAULT_PROD_HOST, DEFAULT_PROD_PORT)
94 };
95
96 Self {
97 username: get_env_or_default("DERIBIT_USERNAME", String::new()),
98 password: get_env_or_default("DERIBIT_PASSWORD", String::new()),
99 host: get_env_or_default("DERIBIT_HOST", default_host.to_string()),
100 port: get_env_or_default("DERIBIT_PORT", default_port),
101 use_ssl,
102 test_mode,
103 heartbeat_interval: get_env_or_default(
104 "DERIBIT_HEARTBEAT_INTERVAL",
105 DEFAULT_HEARTBEAT_INTERVAL,
106 ),
107 connection_timeout: Duration::from_secs(get_env_or_default(
108 "DERIBIT_CONNECTION_TIMEOUT",
109 DEFAULT_CONNECTION_TIMEOUT_SECS,
110 )),
111 reconnect_attempts: get_env_or_default(
112 "DERIBIT_RECONNECT_ATTEMPTS",
113 DEFAULT_RECONNECT_ATTEMPTS,
114 ),
115 reconnect_delay: Duration::from_secs(get_env_or_default(
116 "DERIBIT_RECONNECT_DELAY",
117 DEFAULT_RECONNECT_DELAY_SECS,
118 )),
119 enable_logging: get_env_or_default("DERIBIT_ENABLE_LOGGING", true),
120 log_level: get_env_or_default("DERIBIT_LOG_LEVEL", DEFAULT_LOG_LEVEL.to_string()),
121 sender_comp_id: get_env_or_default(
122 "DERIBIT_SENDER_COMP_ID",
123 DEFAULT_SENDER_COMP_ID.to_string(),
124 ),
125 target_comp_id: get_env_or_default(
126 "DERIBIT_TARGET_COMP_ID",
127 DEFAULT_TARGET_COMP_ID.to_string(),
128 ),
129 cancel_on_disconnect: get_env_or_default("DERIBIT_CANCEL_ON_DISCONNECT", false),
130 app_id: get_env_optional("DERIBIT_APP_ID"),
131 app_secret: get_env_optional("DERIBIT_APP_SECRET"),
132 use_wordsafe_tags: get_env_optional::<String>("DERIBIT_USE_WORDSAFE_TAGS")
133 .map(|v| v == "Y" || v == "true"),
134 deribit_sequential: get_env_optional::<String>("DERIBIT_SEQUENTIAL")
135 .map(|v| v == "Y" || v == "true"),
136 unsubscribe_execution_reports: get_env_optional::<String>(
137 "DERIBIT_UNSUBSCRIBE_EXECUTION_REPORTS",
138 )
139 .map(|v| v == "Y" || v == "true"),
140 connection_only_execution_reports: get_env_optional::<String>(
141 "DERIBIT_CONNECTION_ONLY_EXECUTION_REPORTS",
142 )
143 .map(|v| v == "Y" || v == "true"),
144 report_fills_as_exec_reports: get_env_optional::<String>(
145 "DERIBIT_REPORT_FILLS_AS_EXEC_REPORTS",
146 )
147 .map(|v| v == "Y" || v == "true"),
148 display_increment_steps: get_env_optional::<String>("DERIBIT_DISPLAY_INCREMENT_STEPS")
149 .map(|v| v == "Y" || v == "true"),
150 }
151 }
152
153 pub fn with_credentials(mut self, username: String, password: String) -> Self {
155 self.username = username;
156 self.password = password;
157 self
158 }
159
160 pub fn production() -> Self {
162 let mut config = Self::new();
163 config.test_mode = false;
164 config.host = get_env_or_default("DERIBIT_HOST", DEFAULT_PROD_HOST.to_string());
165 config.port = if config.use_ssl {
166 get_env_or_default("DERIBIT_PORT", DEFAULT_SSL_PORT)
167 } else {
168 get_env_or_default("DERIBIT_PORT", DEFAULT_PROD_PORT)
169 };
170 config
171 }
172
173 pub fn production_with_credentials(username: String, password: String) -> Self {
175 let mut config = Self::production();
176 config.username = username;
177 config.password = password;
178 config
179 }
180
181 pub fn production_ssl() -> Self {
183 let mut config = Self::production();
184 config.use_ssl = true;
185 config.port = get_env_or_default("DERIBIT_PORT", DEFAULT_SSL_PORT);
186 config
187 }
188
189 pub fn test_ssl() -> Self {
191 let mut config = Self::new();
192 config.use_ssl = true;
193 config.port = get_env_or_default("DERIBIT_PORT", DEFAULT_SSL_PORT);
194 config
195 }
196
197 pub fn with_endpoint(mut self, host: String, port: u16) -> Self {
199 self.host = host;
200 self.port = port;
201 self
202 }
203
204 pub fn with_ssl(mut self, use_ssl: bool) -> Self {
206 self.use_ssl = use_ssl;
207 self
208 }
209
210 pub fn with_heartbeat_interval(mut self, interval: u32) -> Self {
212 self.heartbeat_interval = interval;
213 self
214 }
215
216 pub fn with_connection_timeout(mut self, timeout: Duration) -> Self {
218 self.connection_timeout = timeout;
219 self
220 }
221
222 pub fn with_reconnection(mut self, attempts: u32, delay: Duration) -> Self {
224 self.reconnect_attempts = attempts;
225 self.reconnect_delay = delay;
226 self
227 }
228
229 pub fn with_logging(mut self, enabled: bool, level: String) -> Self {
231 self.enable_logging = enabled;
232 self.log_level = level;
233 self
234 }
235
236 pub fn with_session_ids(mut self, sender_comp_id: String, target_comp_id: String) -> Self {
238 self.sender_comp_id = sender_comp_id;
239 self.target_comp_id = target_comp_id;
240 self
241 }
242
243 pub fn with_cancel_on_disconnect(mut self, cancel_on_disconnect: bool) -> Self {
245 self.cancel_on_disconnect = cancel_on_disconnect;
246 self
247 }
248
249 pub fn with_app_credentials(mut self, app_id: String, app_secret: String) -> Self {
251 self.app_id = Some(app_id);
252 self.app_secret = Some(app_secret);
253 self
254 }
255
256 pub fn with_use_wordsafe_tags(mut self, use_wordsafe_tags: bool) -> Self {
258 self.use_wordsafe_tags = Some(use_wordsafe_tags);
259 self
260 }
261
262 pub fn with_deribit_sequential(mut self, deribit_sequential: bool) -> Self {
264 self.deribit_sequential = Some(deribit_sequential);
265 self
266 }
267
268 pub fn with_unsubscribe_execution_reports(mut self, unsubscribe: bool) -> Self {
270 self.unsubscribe_execution_reports = Some(unsubscribe);
271 self
272 }
273
274 pub fn with_connection_only_execution_reports(mut self, connection_only: bool) -> Self {
276 self.connection_only_execution_reports = Some(connection_only);
277 self
278 }
279
280 pub fn with_report_fills_as_exec_reports(mut self, report_fills: bool) -> Self {
282 self.report_fills_as_exec_reports = Some(report_fills);
283 self
284 }
285
286 pub fn with_display_increment_steps(mut self, display_steps: bool) -> Self {
288 self.display_increment_steps = Some(display_steps);
289 self
290 }
291
292 pub fn connection_url(&self) -> String {
294 format!("{}:{}", self.host, self.port)
295 }
296
297 pub fn validate(&self) -> Result<()> {
299 if self.username.is_empty() {
300 return Err(DeribitFixError::Config(
301 "Username cannot be empty".to_string(),
302 ));
303 }
304
305 if self.password.is_empty() {
306 return Err(DeribitFixError::Config(
307 "Password cannot be empty".to_string(),
308 ));
309 }
310
311 if self.host.is_empty() {
312 return Err(DeribitFixError::Config("Host cannot be empty".to_string()));
313 }
314
315 if self.port == 0 {
316 return Err(DeribitFixError::Config(
317 "Port must be greater than 0".to_string(),
318 ));
319 }
320
321 if self.heartbeat_interval == 0 {
322 return Err(DeribitFixError::Config(
323 "Heartbeat interval must be greater than 0".to_string(),
324 ));
325 }
326
327 if self.sender_comp_id.is_empty() {
328 return Err(DeribitFixError::Config(
329 "Sender company ID cannot be empty".to_string(),
330 ));
331 }
332
333 if self.target_comp_id.is_empty() {
334 return Err(DeribitFixError::Config(
335 "Target company ID cannot be empty".to_string(),
336 ));
337 }
338
339 if self.app_id.is_some() && self.app_secret.is_none() {
341 return Err(DeribitFixError::Config(
342 "Application secret is required when app ID is provided".to_string(),
343 ));
344 }
345
346 if self.app_secret.is_some() && self.app_id.is_none() {
347 return Err(DeribitFixError::Config(
348 "Application ID is required when app secret is provided".to_string(),
349 ));
350 }
351
352 Ok(())
353 }
354}
355
356impl Default for DeribitFixConfig {
357 fn default() -> Self {
358 Self::new()
359 }
360}
361
362impl_json_debug_pretty!(DeribitFixConfig);
363impl_json_display!(DeribitFixConfig);