Skip to main content

nautilus_betfair/python/
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//! Python bindings for Betfair configuration.
17
18use nautilus_core::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    /// Configuration for the Betfair live data client.
34    #[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    ) -> Self {
94        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
126    /// Returns the username.
127    #[getter]
128    fn username(&self) -> Option<&str> {
129        self.username.as_ref().map(SecretString::expose_secret)
130    }
131
132    #[getter]
133    const fn has_proxy_url(&self) -> bool {
134        self.proxy_url.is_some()
135    }
136
137    fn __repr__(&self) -> String {
138        stringify!(BetfairDataClientConfig).to_string()
139    }
140}
141
142#[pymethods]
143#[pyo3_stub_gen::derive::gen_stub_pymethods]
144impl BetfairExecutionClientConfig {
145    /// Configuration for the Betfair live execution client.
146    #[new]
147    #[pyo3(signature = (
148        account_id = None,
149        account_currency = None,
150        username = None,
151        password = None,
152        app_key = None,
153        proxy_url = None,
154        request_rate_per_second = 5,
155        order_request_rate_per_second = 20,
156        stream_host = None,
157        stream_port = None,
158        stream_heartbeat_secs = None,
159        stream_heartbeat_timeout_secs = None,
160        stream_reconnect_delay_initial_ms = 2_000,
161        stream_reconnect_delay_max_ms = 30_000,
162        stream_use_tls = true,
163        stream_market_ids_filter = None,
164        ignore_external_orders = false,
165        calculate_account_state = true,
166        request_account_state_secs = 300,
167        reconcile_market_ids_only = false,
168        reconcile_market_ids = None,
169        use_market_version = false,
170        stream_gap_recovery_lookback_mins = 10,
171    ))]
172    #[expect(clippy::too_many_arguments)]
173    fn py_new(
174        account_id: Option<AccountId>,
175        account_currency: Option<String>,
176        username: Option<String>,
177        password: Option<String>,
178        app_key: Option<String>,
179        proxy_url: Option<String>,
180        request_rate_per_second: u32,
181        order_request_rate_per_second: u32,
182        stream_host: Option<String>,
183        stream_port: Option<u16>,
184        stream_heartbeat_secs: Option<u64>,
185        stream_heartbeat_timeout_secs: Option<u64>,
186        stream_reconnect_delay_initial_ms: u64,
187        stream_reconnect_delay_max_ms: u64,
188        stream_use_tls: bool,
189        stream_market_ids_filter: Option<Vec<String>>,
190        ignore_external_orders: bool,
191        calculate_account_state: bool,
192        request_account_state_secs: u64,
193        reconcile_market_ids_only: bool,
194        reconcile_market_ids: Option<Vec<String>>,
195        use_market_version: bool,
196        stream_gap_recovery_lookback_mins: u64,
197    ) -> Self {
198        Self {
199            account_id: account_id.unwrap_or_else(|| AccountId::from("BETFAIR-001")),
200            account_currency: account_currency.unwrap_or_else(|| "GBP".to_string()),
201            username: username.map(SecretString::from),
202            password: password.map(SecretString::from),
203            app_key: app_key.map(SecretString::from),
204            proxy_url: proxy_url.map(SecretString::from),
205            request_rate_per_second,
206            order_request_rate_per_second,
207            stream_host,
208            stream_port,
209            stream_heartbeat_secs,
210            stream_heartbeat_timeout_secs,
211            stream_reconnect_delay_initial_ms,
212            stream_reconnect_delay_max_ms,
213            stream_use_tls,
214            stream_market_ids_filter,
215            ignore_external_orders,
216            calculate_account_state,
217            request_account_state_secs,
218            reconcile_market_ids_only,
219            reconcile_market_ids,
220            use_market_version,
221            stream_gap_recovery_lookback_mins,
222        }
223    }
224
225    /// Returns the username.
226    #[getter]
227    fn username(&self) -> Option<&str> {
228        self.username.as_ref().map(SecretString::expose_secret)
229    }
230
231    #[getter]
232    const fn has_proxy_url(&self) -> bool {
233        self.proxy_url.is_some()
234    }
235
236    fn __repr__(&self) -> String {
237        stringify!(BetfairExecutionClientConfig).to_string()
238    }
239}