Skip to main content

brokapi_alor/
config.rs

1//! Configuration of the two connectors: the ALOR client settings plus the choices of the connector itself
2//! (source label, reconnect pauses, buffer size).
3//!
4//! Both configs wrap an `AlorConfig` from the protocol crate. It holds the refresh token, the contour, the
5//! timeouts, the portfolio and the exchange. Build it with `AlorConfig::from_env()` for a real account, or with
6//! `AlorConfig::for_mock(token, url)` for tests against `brokapi-mocks`.
7
8use std::sync::Arc;
9use std::time::Duration;
10
11use brokapi_alor_protocol::config::AlorConfig;
12use brokapi_alor_protocol::ws::session::{DEFAULT_RECONNECT_MAX, DEFAULT_RECONNECT_MIN, WsOptions};
13use brokapi_runtime::PriceGrid;
14
15/// Settings of [`MarketDataConnector`](crate::MarketDataConnector).
16///
17/// # Examples
18///
19/// ```
20/// use std::time::Duration;
21/// use brokapi_alor_protocol::config::AlorConfig;
22/// use brokapi_alor::MarketDataConfig;
23///
24/// let mut cfg = MarketDataConfig::new(AlorConfig::new("refresh-token".into()), "alor-md");
25/// assert_eq!(&*cfg.source, "alor-md");
26/// // Wait at most 10 seconds between reconnects instead of the default 30.
27/// cfg.reconnect_max = Duration::from_secs(10);
28/// ```
29#[derive(Debug, Clone)]
30pub struct MarketDataConfig {
31    /// Client settings of the ALOR API: refresh token, contour, timeouts, endpoints.
32    pub alor: AlorConfig,
33    /// Envelope source label, e.g. `"alor-md"`.
34    pub source: Arc<str>,
35    /// Pause before the first reconnect of the WebSocket, doubling up to `reconnect_max` (default 0.5 s, as at
36    /// the other brokers).
37    pub reconnect_min: Duration,
38    /// Upper bound of the pause (default 30 s). The pause returns to `reconnect_min` once a connection has
39    /// lived 10 seconds.
40    pub reconnect_max: Duration,
41    /// How long a `subscribe_*` call waits for the broker to confirm the subscription (default 3 s). The call
42    /// returns when the broker has confirmed: what happens after that is delivered. A refusal is returned as
43    /// `Rejected`. If nothing comes in time, the call fails with `Transport` and the subscription is taken back, so
44    /// the call can be repeated.
45    pub subscribe_ack_timeout: Duration,
46}
47
48impl MarketDataConfig {
49    /// Creates a config with the default reconnect pauses (0.5 s up to 30 s).
50    ///
51    /// `source` is the label put in every [`Envelope`](brokapi_core::Envelope) this connector emits, so a bot
52    /// with several connectors can tell them apart.
53    pub fn new(alor: AlorConfig, source: impl Into<Arc<str>>) -> Self {
54        Self {
55            alor,
56            source: source.into(),
57            reconnect_min: DEFAULT_RECONNECT_MIN,
58            reconnect_max: DEFAULT_RECONNECT_MAX,
59            subscribe_ack_timeout: Duration::from_secs(3),
60        }
61    }
62
63    pub(crate) fn ws_options(&self) -> WsOptions {
64        // The core is told of every connect: a reconnect voids the order books (`Resync`).
65        WsOptions {
66            announce_connects: true,
67            reconnect_min: self.reconnect_min,
68            reconnect_max: self.reconnect_max,
69        }
70    }
71}
72
73/// Settings of [`UserDataConnector`](crate::UserDataConnector).
74///
75/// The portfolio and the exchange come from the `alor` config: see
76/// [`AlorConfig::portfolio`](brokapi_alor_protocol::config::AlorConfig::portfolio).
77///
78/// # Examples
79///
80/// ```
81/// use brokapi_alor_protocol::config::AlorConfig;
82/// use brokapi_alor::UserDataConfig;
83///
84/// let alor = AlorConfig::new("refresh-token".into()).with_portfolio("D12345");
85/// let mut cfg = UserDataConfig::new(alor, "alor-ud");
86/// assert_eq!(cfg.order_state_buffer, 1024);
87/// // A bigger buffer gives a slow bot more room before an overflow closes the trading gate.
88/// cfg.order_state_buffer = 4096;
89/// ```
90#[derive(Debug, Clone)]
91pub struct UserDataConfig {
92    /// Client settings of the ALOR API: refresh token, contour, timeouts, endpoints, portfolio, exchange.
93    pub alor: AlorConfig,
94    /// Envelope source label, e.g. `"alor-ud"`.
95    pub source: Arc<str>,
96    /// Capacity of the lossless buffer of each order state and execution subscription (default 1024 events).
97    /// When it is full, the subscription reports an overflow and the connector becomes degraded: commands are
98    /// refused until the next successful `reconcile`.
99    pub order_state_buffer: usize,
100    /// Pause before the first reconnect of the WebSocket, doubling up to `reconnect_max` (default 0.5 s).
101    pub reconnect_min: Duration,
102    /// Upper bound of the pause (default 30 s).
103    pub reconnect_max: Duration,
104    /// How long a `subscribe_*` call waits for the broker to confirm the subscription (default 3 s), see
105    /// [`MarketDataConfig::subscribe_ack_timeout`].
106    pub subscribe_ack_timeout: Duration,
107    /// Price steps and lot sizes the connector checks orders against (default: empty, only the lot sizes the
108    /// connector reads from the broker). Fill it with [`PriceGrid::learn`] from the trading parameters of the
109    /// instruments you trade. A price off the step is then refused with `Rejected` (`price_off_step`) before
110    /// anything is sent.
111    pub price_grid: PriceGrid,
112}
113
114impl UserDataConfig {
115    /// Creates a config with the default buffer (1024 events) and the default reconnect pauses.
116    ///
117    /// `source` is the label put in every [`Envelope`](brokapi_core::Envelope) this connector emits.
118    pub fn new(alor: AlorConfig, source: impl Into<Arc<str>>) -> Self {
119        Self {
120            alor,
121            source: source.into(),
122            order_state_buffer: 1024,
123            reconnect_min: DEFAULT_RECONNECT_MIN,
124            reconnect_max: DEFAULT_RECONNECT_MAX,
125            subscribe_ack_timeout: Duration::from_secs(3),
126            price_grid: PriceGrid::default(),
127        }
128    }
129
130    /// Options of the session: it announces every connect, so the core sees a lost connection
131    /// ([`brokapi_core::Event::Reconnected`]).
132    pub(crate) fn ws_options(&self) -> WsOptions {
133        WsOptions {
134            announce_connects: true,
135            reconnect_min: self.reconnect_min,
136            reconnect_max: self.reconnect_max,
137        }
138    }
139}