Skip to main content

simulator_client/
client.rs

1use std::time::Duration;
2
3use bon::Builder;
4use tokio_tungstenite::{
5    connect_async,
6    tungstenite::{client::IntoClientRequest, http::HeaderValue},
7};
8
9use crate::{
10    BacktestClientError, BacktestClientResult, BacktestSession, CreateSession,
11    urls::http_base_from_ws_url,
12};
13
14const DEFAULT_CONNECT_TIMEOUT: Duration = Duration::from_secs(10);
15
16/// Backtest WebSocket client configured with a base URL and API key.
17///
18/// Build with [`BacktestClient::builder`], which supports optional timeouts and
19/// raw message logging.
20#[derive(Debug, Clone, Builder)]
21#[builder(on(String, into))]
22pub struct BacktestClient {
23    /// WebSocket endpoint, e.g. `wss://.../backtest`.
24    url: String,
25    /// API key injected as the `X-API-Key` header.
26    api_key: String,
27
28    /// Timeout for the initial connect handshake.
29    #[builder(default = DEFAULT_CONNECT_TIMEOUT)]
30    connect_timeout: Duration,
31
32    /// Default timeout for request/response operations.
33    request_timeout: Option<Duration>,
34
35    /// Log raw inbound responses at debug level.
36    #[builder(default)]
37    log_raw: bool,
38}
39
40impl BacktestClient {
41    /// Create a backtest session by connecting and sending a `CreateBacktestSession` request.
42    pub async fn create_session(
43        &self,
44        create: CreateSession,
45    ) -> BacktestClientResult<BacktestSession> {
46        let params = create.into_params()?;
47
48        let mut request = self.url.clone().into_client_request().map_err(|source| {
49            BacktestClientError::BuildRequest {
50                url: self.url.clone(),
51                source: Box::new(source),
52            }
53        })?;
54
55        request
56            .headers_mut()
57            .insert("X-API-Key", HeaderValue::from_str(&self.api_key)?);
58
59        let (stream, _) = tokio::time::timeout(self.connect_timeout, connect_async(request))
60            .await
61            .map_err(|_| BacktestClientError::Timeout {
62                action: "connecting",
63                duration: self.connect_timeout,
64            })?
65            .map_err(|source| BacktestClientError::Connect {
66                url: self.url.clone(),
67                source: Box::new(source),
68            })?;
69
70        let mut session = BacktestSession::new(stream, self.request_timeout, self.log_raw);
71        session
72            .create(params, http_base_from_ws_url(&self.url))
73            .await?;
74        Ok(session)
75    }
76}