1use nautilus_core::{python::to_pyvalue_err, string::secret::SecretString};
19use nautilus_live::book::DEFAULT_BOOK_SNAPSHOT_TIMEOUT_SECS;
20use nautilus_model::identifiers::AccountId;
21use pyo3::prelude::*;
22use rust_decimal::Decimal;
23
24use crate::config::{BetfairDataClientConfig, BetfairExecutionClientConfig};
25
26fn stringify_ids(values: Option<Vec<u64>>) -> Option<Vec<String>> {
27 values.map(|values| values.into_iter().map(|value| value.to_string()).collect())
28}
29
30#[pymethods]
31#[pyo3_stub_gen::derive::gen_stub_pymethods]
32impl BetfairDataClientConfig {
33 #[new]
35 #[pyo3(signature = (
36 account_currency = None,
37 username = None,
38 password = None,
39 app_key = None,
40 proxy_url = None,
41 request_rate_per_second = 5,
42 default_min_notional = None,
43 event_type_ids = None,
44 event_type_names = None,
45 event_ids = None,
46 country_codes = None,
47 market_types = None,
48 market_ids = None,
49 min_market_start_time = None,
50 max_market_start_time = None,
51 stream_host = None,
52 stream_port = None,
53 stream_heartbeat_secs = None,
54 stream_heartbeat_timeout_secs = None,
55 stream_reconnect_delay_initial_ms = 2_000,
56 stream_reconnect_delay_max_ms = 30_000,
57 stream_use_tls = true,
58 stream_conflate_ms = None,
59 subscription_delay_secs = None,
60 subscribe_race_data = false,
61 subscribe_cricket_data = false,
62 book_snapshot_timeout_secs = None,
63 ))]
64 #[expect(clippy::too_many_arguments)]
65 fn py_new(
66 account_currency: Option<String>,
67 username: Option<String>,
68 password: Option<String>,
69 app_key: Option<String>,
70 proxy_url: Option<String>,
71 request_rate_per_second: u32,
72 default_min_notional: Option<Decimal>,
73 event_type_ids: Option<Vec<u64>>,
74 event_type_names: Option<Vec<String>>,
75 event_ids: Option<Vec<u64>>,
76 country_codes: Option<Vec<String>>,
77 market_types: Option<Vec<String>>,
78 market_ids: Option<Vec<String>>,
79 min_market_start_time: Option<String>,
80 max_market_start_time: Option<String>,
81 stream_host: Option<String>,
82 stream_port: Option<u16>,
83 stream_heartbeat_secs: Option<u64>,
84 stream_heartbeat_timeout_secs: Option<u64>,
85 stream_reconnect_delay_initial_ms: u64,
86 stream_reconnect_delay_max_ms: u64,
87 stream_use_tls: bool,
88 stream_conflate_ms: Option<u64>,
89 subscription_delay_secs: Option<u64>,
90 subscribe_race_data: bool,
91 subscribe_cricket_data: bool,
92 book_snapshot_timeout_secs: Option<u64>,
93 ) -> PyResult<Self> {
94 let config = Self {
95 account_currency: account_currency.unwrap_or_else(|| "GBP".to_string()),
96 username: username.map(SecretString::from),
97 password: password.map(SecretString::from),
98 app_key: app_key.map(SecretString::from),
99 proxy_url: proxy_url.map(SecretString::from),
100 request_rate_per_second,
101 default_min_notional,
102 event_type_ids: stringify_ids(event_type_ids),
103 event_type_names,
104 event_ids: stringify_ids(event_ids),
105 country_codes,
106 market_types,
107 market_ids,
108 min_market_start_time,
109 max_market_start_time,
110 stream_host,
111 stream_port,
112 stream_heartbeat_secs,
113 stream_heartbeat_timeout_secs,
114 stream_reconnect_delay_initial_ms,
115 stream_reconnect_delay_max_ms,
116 stream_use_tls,
117 stream_conflate_ms,
118 subscription_delay_secs: subscription_delay_secs.unwrap_or(3),
119 subscribe_race_data,
120 subscribe_cricket_data,
121 book_snapshot_timeout_secs: book_snapshot_timeout_secs
122 .unwrap_or(DEFAULT_BOOK_SNAPSHOT_TIMEOUT_SECS),
123 };
124
125 config.validate().map_err(to_pyvalue_err)?;
126 Ok(config)
127 }
128
129 #[getter]
131 fn username(&self) -> Option<&str> {
132 self.username.as_ref().map(SecretString::expose_secret)
133 }
134
135 #[getter]
136 const fn has_proxy_url(&self) -> bool {
137 self.proxy_url.is_some()
138 }
139
140 fn __repr__(&self) -> String {
141 stringify!(BetfairDataClientConfig).to_string()
142 }
143}
144
145#[pymethods]
146#[pyo3_stub_gen::derive::gen_stub_pymethods]
147impl BetfairExecutionClientConfig {
148 #[new]
150 #[pyo3(signature = (
151 account_id = None,
152 account_currency = None,
153 username = None,
154 password = None,
155 app_key = None,
156 proxy_url = None,
157 request_rate_per_second = 5,
158 order_request_rate_per_second = 20,
159 stream_host = None,
160 stream_port = None,
161 stream_heartbeat_secs = None,
162 stream_heartbeat_timeout_secs = None,
163 stream_reconnect_delay_initial_ms = 2_000,
164 stream_reconnect_delay_max_ms = 30_000,
165 stream_use_tls = true,
166 stream_market_ids_filter = None,
167 ignore_external_orders = false,
168 calculate_account_state = true,
169 request_account_state_secs = 300,
170 reconcile_market_ids_only = false,
171 reconcile_market_ids = None,
172 use_market_version = false,
173 stream_gap_recovery_lookback_mins = 10,
174 ))]
175 #[expect(clippy::too_many_arguments)]
176 fn py_new(
177 account_id: Option<AccountId>,
178 account_currency: Option<String>,
179 username: Option<String>,
180 password: Option<String>,
181 app_key: Option<String>,
182 proxy_url: Option<String>,
183 request_rate_per_second: u32,
184 order_request_rate_per_second: u32,
185 stream_host: Option<String>,
186 stream_port: Option<u16>,
187 stream_heartbeat_secs: Option<u64>,
188 stream_heartbeat_timeout_secs: Option<u64>,
189 stream_reconnect_delay_initial_ms: u64,
190 stream_reconnect_delay_max_ms: u64,
191 stream_use_tls: bool,
192 stream_market_ids_filter: Option<Vec<String>>,
193 ignore_external_orders: bool,
194 calculate_account_state: bool,
195 request_account_state_secs: u64,
196 reconcile_market_ids_only: bool,
197 reconcile_market_ids: Option<Vec<String>>,
198 use_market_version: bool,
199 stream_gap_recovery_lookback_mins: u64,
200 ) -> PyResult<Self> {
201 let config = Self {
202 account_id: account_id.unwrap_or_else(|| AccountId::from("BETFAIR-001")),
203 account_currency: account_currency.unwrap_or_else(|| "GBP".to_string()),
204 username: username.map(SecretString::from),
205 password: password.map(SecretString::from),
206 app_key: app_key.map(SecretString::from),
207 proxy_url: proxy_url.map(SecretString::from),
208 request_rate_per_second,
209 order_request_rate_per_second,
210 stream_host,
211 stream_port,
212 stream_heartbeat_secs,
213 stream_heartbeat_timeout_secs,
214 stream_reconnect_delay_initial_ms,
215 stream_reconnect_delay_max_ms,
216 stream_use_tls,
217 stream_market_ids_filter,
218 ignore_external_orders,
219 calculate_account_state,
220 request_account_state_secs,
221 reconcile_market_ids_only,
222 reconcile_market_ids,
223 use_market_version,
224 stream_gap_recovery_lookback_mins,
225 };
226
227 config.validate().map_err(to_pyvalue_err)?;
228 Ok(config)
229 }
230
231 #[getter]
233 fn username(&self) -> Option<&str> {
234 self.username.as_ref().map(SecretString::expose_secret)
235 }
236
237 #[getter]
238 const fn has_proxy_url(&self) -> bool {
239 self.proxy_url.is_some()
240 }
241
242 fn __repr__(&self) -> String {
243 stringify!(BetfairExecutionClientConfig).to_string()
244 }
245}