use crate::config::utils::{get_env_optional, get_env_or_default};
use crate::constants::{
DEFAULT_CONNECTION_TIMEOUT_SECS, DEFAULT_HEARTBEAT_INTERVAL, DEFAULT_LOG_LEVEL,
DEFAULT_PROD_HOST, DEFAULT_PROD_PORT, DEFAULT_RECONNECT_ATTEMPTS, DEFAULT_RECONNECT_DELAY_SECS,
DEFAULT_SENDER_COMP_ID, DEFAULT_SSL_PORT, DEFAULT_TARGET_COMP_ID, DEFAULT_TEST_HOST,
DEFAULT_TEST_PORT,
};
use crate::error::{DeribitFixError, Result};
use crate::{impl_json_debug_pretty, impl_json_display};
use dotenv::dotenv;
use serde::{Deserialize, Serialize};
use std::time::Duration;
use tracing::debug;
#[derive(Clone, Serialize, Deserialize)]
pub struct DeribitFixConfig {
pub username: String,
pub password: String,
pub host: String,
pub port: u16,
pub use_ssl: bool,
pub test_mode: bool,
pub heartbeat_interval: u32,
pub connection_timeout: Duration,
pub reconnect_attempts: u32,
pub reconnect_delay: Duration,
pub enable_logging: bool,
pub log_level: String,
pub sender_comp_id: String,
pub target_comp_id: String,
pub cancel_on_disconnect: bool,
pub app_id: Option<String>,
pub app_secret: Option<String>,
pub use_wordsafe_tags: Option<bool>,
pub deribit_sequential: Option<bool>,
pub unsubscribe_execution_reports: Option<bool>,
pub connection_only_execution_reports: Option<bool>,
pub report_fills_as_exec_reports: Option<bool>,
pub display_increment_steps: Option<bool>,
}
impl DeribitFixConfig {
pub fn new() -> Self {
match dotenv() {
Ok(_) => debug!("Successfully loaded .env file"),
Err(e) => debug!("Failed to load .env file: {}", e),
}
let test_mode = get_env_or_default("DERIBIT_TEST_MODE", true);
let use_ssl = get_env_or_default("DERIBIT_USE_SSL", false);
let (default_host, default_port) = if test_mode {
if use_ssl {
(DEFAULT_TEST_HOST, DEFAULT_SSL_PORT)
} else {
(DEFAULT_TEST_HOST, DEFAULT_TEST_PORT)
}
} else if use_ssl {
(DEFAULT_PROD_HOST, DEFAULT_SSL_PORT)
} else {
(DEFAULT_PROD_HOST, DEFAULT_PROD_PORT)
};
Self {
username: get_env_or_default("DERIBIT_USERNAME", String::new()),
password: get_env_or_default("DERIBIT_PASSWORD", String::new()),
host: get_env_or_default("DERIBIT_HOST", default_host.to_string()),
port: get_env_or_default("DERIBIT_PORT", default_port),
use_ssl,
test_mode,
heartbeat_interval: get_env_or_default(
"DERIBIT_HEARTBEAT_INTERVAL",
DEFAULT_HEARTBEAT_INTERVAL,
),
connection_timeout: Duration::from_secs(get_env_or_default(
"DERIBIT_CONNECTION_TIMEOUT",
DEFAULT_CONNECTION_TIMEOUT_SECS,
)),
reconnect_attempts: get_env_or_default(
"DERIBIT_RECONNECT_ATTEMPTS",
DEFAULT_RECONNECT_ATTEMPTS,
),
reconnect_delay: Duration::from_secs(get_env_or_default(
"DERIBIT_RECONNECT_DELAY",
DEFAULT_RECONNECT_DELAY_SECS,
)),
enable_logging: get_env_or_default("DERIBIT_ENABLE_LOGGING", true),
log_level: get_env_or_default("DERIBIT_LOG_LEVEL", DEFAULT_LOG_LEVEL.to_string()),
sender_comp_id: get_env_or_default(
"DERIBIT_SENDER_COMP_ID",
DEFAULT_SENDER_COMP_ID.to_string(),
),
target_comp_id: get_env_or_default(
"DERIBIT_TARGET_COMP_ID",
DEFAULT_TARGET_COMP_ID.to_string(),
),
cancel_on_disconnect: get_env_or_default("DERIBIT_CANCEL_ON_DISCONNECT", false),
app_id: get_env_optional("DERIBIT_APP_ID"),
app_secret: get_env_optional("DERIBIT_APP_SECRET"),
use_wordsafe_tags: get_env_optional::<String>("DERIBIT_USE_WORDSAFE_TAGS")
.map(|v| v == "Y" || v == "true"),
deribit_sequential: get_env_optional::<String>("DERIBIT_SEQUENTIAL")
.map(|v| v == "Y" || v == "true"),
unsubscribe_execution_reports: get_env_optional::<String>(
"DERIBIT_UNSUBSCRIBE_EXECUTION_REPORTS",
)
.map(|v| v == "Y" || v == "true"),
connection_only_execution_reports: get_env_optional::<String>(
"DERIBIT_CONNECTION_ONLY_EXECUTION_REPORTS",
)
.map(|v| v == "Y" || v == "true"),
report_fills_as_exec_reports: get_env_optional::<String>(
"DERIBIT_REPORT_FILLS_AS_EXEC_REPORTS",
)
.map(|v| v == "Y" || v == "true"),
display_increment_steps: get_env_optional::<String>("DERIBIT_DISPLAY_INCREMENT_STEPS")
.map(|v| v == "Y" || v == "true"),
}
}
pub fn with_credentials(mut self, username: String, password: String) -> Self {
self.username = username;
self.password = password;
self
}
pub fn production() -> Self {
let mut config = Self::new();
config.test_mode = false;
config.host = get_env_or_default("DERIBIT_HOST", DEFAULT_PROD_HOST.to_string());
config.port = if config.use_ssl {
get_env_or_default("DERIBIT_PORT", DEFAULT_SSL_PORT)
} else {
get_env_or_default("DERIBIT_PORT", DEFAULT_PROD_PORT)
};
config
}
pub fn production_with_credentials(username: String, password: String) -> Self {
let mut config = Self::production();
config.username = username;
config.password = password;
config
}
pub fn production_ssl() -> Self {
let mut config = Self::production();
config.use_ssl = true;
config.port = get_env_or_default("DERIBIT_PORT", DEFAULT_SSL_PORT);
config
}
pub fn test_ssl() -> Self {
let mut config = Self::new();
config.use_ssl = true;
config.port = get_env_or_default("DERIBIT_PORT", DEFAULT_SSL_PORT);
config
}
pub fn with_endpoint(mut self, host: String, port: u16) -> Self {
self.host = host;
self.port = port;
self
}
pub fn with_ssl(mut self, use_ssl: bool) -> Self {
self.use_ssl = use_ssl;
self
}
pub fn with_heartbeat_interval(mut self, interval: u32) -> Self {
self.heartbeat_interval = interval;
self
}
pub fn with_connection_timeout(mut self, timeout: Duration) -> Self {
self.connection_timeout = timeout;
self
}
pub fn with_reconnection(mut self, attempts: u32, delay: Duration) -> Self {
self.reconnect_attempts = attempts;
self.reconnect_delay = delay;
self
}
pub fn with_logging(mut self, enabled: bool, level: String) -> Self {
self.enable_logging = enabled;
self.log_level = level;
self
}
pub fn with_session_ids(mut self, sender_comp_id: String, target_comp_id: String) -> Self {
self.sender_comp_id = sender_comp_id;
self.target_comp_id = target_comp_id;
self
}
pub fn with_cancel_on_disconnect(mut self, cancel_on_disconnect: bool) -> Self {
self.cancel_on_disconnect = cancel_on_disconnect;
self
}
pub fn with_app_credentials(mut self, app_id: String, app_secret: String) -> Self {
self.app_id = Some(app_id);
self.app_secret = Some(app_secret);
self
}
pub fn with_use_wordsafe_tags(mut self, use_wordsafe_tags: bool) -> Self {
self.use_wordsafe_tags = Some(use_wordsafe_tags);
self
}
pub fn with_deribit_sequential(mut self, deribit_sequential: bool) -> Self {
self.deribit_sequential = Some(deribit_sequential);
self
}
pub fn with_unsubscribe_execution_reports(mut self, unsubscribe: bool) -> Self {
self.unsubscribe_execution_reports = Some(unsubscribe);
self
}
pub fn with_connection_only_execution_reports(mut self, connection_only: bool) -> Self {
self.connection_only_execution_reports = Some(connection_only);
self
}
pub fn with_report_fills_as_exec_reports(mut self, report_fills: bool) -> Self {
self.report_fills_as_exec_reports = Some(report_fills);
self
}
pub fn with_display_increment_steps(mut self, display_steps: bool) -> Self {
self.display_increment_steps = Some(display_steps);
self
}
pub fn connection_url(&self) -> String {
format!("{}:{}", self.host, self.port)
}
pub fn validate(&self) -> Result<()> {
if self.username.is_empty() {
return Err(DeribitFixError::Config(
"Username cannot be empty".to_string(),
));
}
if self.password.is_empty() {
return Err(DeribitFixError::Config(
"Password cannot be empty".to_string(),
));
}
if self.host.is_empty() {
return Err(DeribitFixError::Config("Host cannot be empty".to_string()));
}
if self.port == 0 {
return Err(DeribitFixError::Config(
"Port must be greater than 0".to_string(),
));
}
if self.heartbeat_interval == 0 {
return Err(DeribitFixError::Config(
"Heartbeat interval must be greater than 0".to_string(),
));
}
if self.sender_comp_id.is_empty() {
return Err(DeribitFixError::Config(
"Sender company ID cannot be empty".to_string(),
));
}
if self.target_comp_id.is_empty() {
return Err(DeribitFixError::Config(
"Target company ID cannot be empty".to_string(),
));
}
if self.app_id.is_some() && self.app_secret.is_none() {
return Err(DeribitFixError::Config(
"Application secret is required when app ID is provided".to_string(),
));
}
if self.app_secret.is_some() && self.app_id.is_none() {
return Err(DeribitFixError::Config(
"Application ID is required when app secret is provided".to_string(),
));
}
Ok(())
}
}
impl Default for DeribitFixConfig {
fn default() -> Self {
Self::new()
}
}
impl_json_debug_pretty!(DeribitFixConfig);
impl_json_display!(DeribitFixConfig);