nautilus_network/socket/config.rs
1// -------------------------------------------------------------------------------------------------
2// Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3// https://nautechsystems.io
4//
5// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6// You may not use this file except in compliance with the License.
7// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Socket configuration.
17//!
18//! # Reconnection Strategy
19//!
20//! The default configuration uses unlimited reconnection attempts (`reconnect_max_attempts: None`).
21//! This is intentional for trading systems because:
22//! - Venues may be down for extended periods but eventually recover.
23//! - Exponential backoff already prevents resource waste.
24//! - Automatic recovery can be useful when manual intervention is not desirable.
25//!
26//! Use `Some(n)` primarily for testing, development, or non-critical connections.
27
28use std::fmt::Debug;
29
30use tokio_tungstenite::tungstenite::stream::Mode;
31
32use super::types::TcpMessageHandler;
33
34/// Configuration for TCP socket connection.
35#[derive(bon::Builder)]
36#[cfg_attr(
37 feature = "python",
38 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.network", from_py_object)
39)]
40#[cfg_attr(
41 feature = "python",
42 pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.network")
43)]
44pub struct SocketConfig {
45 /// The URL to connect to.
46 pub url: String,
47 /// The connection mode {Plain, TLS}.
48 pub mode: Mode,
49 /// The sequence of bytes which separates lines.
50 pub suffix: Vec<u8>,
51 /// The optional function to handle incoming messages.
52 pub message_handler: Option<TcpMessageHandler>,
53 /// The optional heartbeat with period and beat message.
54 pub heartbeat: Option<(u64, Vec<u8>)>,
55 /// The timeout (milliseconds) for reconnection attempts.
56 pub reconnect_timeout_ms: Option<u64>,
57 /// The initial reconnection delay (milliseconds) for reconnects.
58 pub reconnect_delay_initial_ms: Option<u64>,
59 /// The maximum reconnect delay (milliseconds) for exponential backoff.
60 pub reconnect_delay_max_ms: Option<u64>,
61 /// The exponential backoff factor for reconnection delays.
62 pub reconnect_backoff_factor: Option<f64>,
63 /// The maximum jitter (milliseconds) added to reconnection delays.
64 pub reconnect_jitter_ms: Option<u64>,
65 /// The maximum number of initial connection attempts (default: 5).
66 pub connection_max_retries: Option<u32>,
67 /// The maximum number of reconnection attempts before giving up.
68 /// - `None`: Unlimited reconnection attempts (default, recommended for production).
69 /// - `Some(n)`: After n failed attempts, transition to CLOSED state.
70 pub reconnect_max_attempts: Option<u32>,
71 /// The idle timeout (milliseconds) for the read task.
72 /// When set, the read task will break and trigger reconnection if no data
73 /// is received within this duration. Useful for detecting silently dead
74 /// connections where the server stops sending without closing.
75 pub idle_timeout_ms: Option<u64>,
76 /// The path to the certificates directory.
77 pub certs_dir: Option<String>,
78}
79
80impl Debug for SocketConfig {
81 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82 f.debug_struct(stringify!(SocketConfig))
83 .field("url", &self.url)
84 .field("mode", &self.mode)
85 .field("suffix", &self.suffix)
86 .field(
87 "message_handler",
88 &self.message_handler.as_ref().map(|_| "<function>"),
89 )
90 .field("heartbeat", &self.heartbeat)
91 .field("reconnect_timeout_ms", &self.reconnect_timeout_ms)
92 .field(
93 "reconnect_delay_initial_ms",
94 &self.reconnect_delay_initial_ms,
95 )
96 .field("reconnect_delay_max_ms", &self.reconnect_delay_max_ms)
97 .field("reconnect_backoff_factor", &self.reconnect_backoff_factor)
98 .field("reconnect_jitter_ms", &self.reconnect_jitter_ms)
99 .field("connection_max_retries", &self.connection_max_retries)
100 .field("reconnect_max_attempts", &self.reconnect_max_attempts)
101 .field("idle_timeout_ms", &self.idle_timeout_ms)
102 .field("certs_dir", &self.certs_dir)
103 .finish()
104 }
105}
106
107impl Clone for SocketConfig {
108 fn clone(&self) -> Self {
109 Self {
110 url: self.url.clone(),
111 mode: self.mode,
112 suffix: self.suffix.clone(),
113 message_handler: self.message_handler.clone(),
114 heartbeat: self.heartbeat.clone(),
115 reconnect_timeout_ms: self.reconnect_timeout_ms,
116 reconnect_delay_initial_ms: self.reconnect_delay_initial_ms,
117 reconnect_delay_max_ms: self.reconnect_delay_max_ms,
118 reconnect_backoff_factor: self.reconnect_backoff_factor,
119 reconnect_jitter_ms: self.reconnect_jitter_ms,
120 connection_max_retries: self.connection_max_retries,
121 reconnect_max_attempts: self.reconnect_max_attempts,
122 idle_timeout_ms: self.idle_timeout_ms,
123 certs_dir: self.certs_dir.clone(),
124 }
125 }
126}