Skip to main content

nautilus_polymarket/
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//! Configuration structures for the Polymarket adapter.
17
18use std::{fmt::Debug, sync::Arc};
19
20use nautilus_model::identifiers::{AccountId, TraderId};
21use nautilus_network::websocket::TransportBackend;
22
23use crate::{
24    common::{enums::SignatureType, urls},
25    filters::InstrumentFilter,
26};
27
28/// Configuration for the Polymarket data client.
29#[derive(Debug, Clone, bon::Builder)]
30#[cfg_attr(
31    feature = "python",
32    pyo3::pyclass(
33        module = "nautilus_trader.core.nautilus_pyo3.polymarket",
34        from_py_object
35    )
36)]
37#[cfg_attr(
38    feature = "python",
39    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.polymarket")
40)]
41pub struct PolymarketDataClientConfig {
42    pub base_url_http: Option<String>,
43    pub base_url_ws: Option<String>,
44    pub base_url_gamma: Option<String>,
45    pub base_url_data_api: Option<String>,
46    /// HTTP timeout in seconds.
47    #[builder(default = 60)]
48    pub http_timeout_secs: u64,
49    /// WebSocket timeout in seconds.
50    #[builder(default = 30)]
51    pub ws_timeout_secs: u64,
52    #[builder(default = crate::common::consts::WS_DEFAULT_SUBSCRIPTIONS)]
53    pub ws_max_subscriptions: usize,
54    /// Instrument reload interval in minutes.
55    #[builder(default = 60)]
56    pub update_instruments_interval_mins: u64,
57    /// Whether to subscribe to new market discovery events via WebSocket.
58    #[builder(default)]
59    pub subscribe_new_markets: bool,
60    /// Whether subscribe and request commands referencing an unknown instrument should
61    /// trigger an ad-hoc load via the instrument provider. Concurrent misses within
62    /// `auto_load_debounce_ms` are coalesced into a single batched request.
63    #[builder(default = true)]
64    pub auto_load_missing_instruments: bool,
65    /// The window (milliseconds) over which concurrent auto-load requests are batched.
66    #[builder(default = 100)]
67    pub auto_load_debounce_ms: u64,
68    /// Instrument filters applied to all instruments during loading and discovery.
69    #[builder(default)]
70    pub filters: Vec<Arc<dyn InstrumentFilter>>,
71    /// Optional filter applied to newly discovered markets before instrument emission.
72    pub new_market_filter: Option<Arc<dyn InstrumentFilter>>,
73    /// WebSocket transport backend (defaults to `Tungstenite`).
74    #[builder(default)]
75    pub transport_backend: TransportBackend,
76}
77
78impl Default for PolymarketDataClientConfig {
79    fn default() -> Self {
80        Self::builder().build()
81    }
82}
83
84impl PolymarketDataClientConfig {
85    #[must_use]
86    pub fn new() -> Self {
87        Self::default()
88    }
89
90    #[must_use]
91    pub fn http_url(&self) -> String {
92        self.base_url_http
93            .clone()
94            .unwrap_or_else(|| urls::clob_http_url().to_string())
95    }
96
97    #[must_use]
98    pub fn ws_url(&self) -> String {
99        self.base_url_ws
100            .clone()
101            .unwrap_or_else(|| urls::clob_ws_url().to_string())
102    }
103
104    #[must_use]
105    pub fn gamma_url(&self) -> String {
106        self.base_url_gamma
107            .clone()
108            .unwrap_or_else(|| urls::gamma_api_url().to_string())
109    }
110
111    #[must_use]
112    pub fn data_api_url(&self) -> String {
113        self.base_url_data_api
114            .clone()
115            .unwrap_or_else(|| "https://data-api.polymarket.com".to_string())
116    }
117}
118
119/// Configuration for the Polymarket execution client.
120#[derive(Clone, bon::Builder)]
121#[cfg_attr(
122    feature = "python",
123    pyo3::pyclass(
124        module = "nautilus_trader.core.nautilus_pyo3.polymarket",
125        from_py_object
126    )
127)]
128#[cfg_attr(
129    feature = "python",
130    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.polymarket")
131)]
132pub struct PolymarketExecClientConfig {
133    #[builder(default)]
134    pub trader_id: TraderId,
135    #[builder(default = AccountId::from("POLYMARKET-001"))]
136    pub account_id: AccountId,
137    /// Falls back to `POLYMARKET_PK` env var.
138    pub private_key: Option<String>,
139    /// Falls back to `POLYMARKET_API_KEY` env var.
140    pub api_key: Option<String>,
141    /// Falls back to `POLYMARKET_API_SECRET` env var.
142    pub api_secret: Option<String>,
143    /// Falls back to `POLYMARKET_PASSPHRASE` env var.
144    pub passphrase: Option<String>,
145    /// Falls back to `POLYMARKET_FUNDER` env var.
146    pub funder: Option<String>,
147    #[builder(default = SignatureType::Eoa)]
148    pub signature_type: SignatureType,
149    pub base_url_http: Option<String>,
150    pub base_url_ws: Option<String>,
151    pub base_url_data_api: Option<String>,
152    #[builder(default = 60)]
153    pub http_timeout_secs: u64,
154    #[builder(default = 3)]
155    pub max_retries: u32,
156    #[builder(default = 1000)]
157    pub retry_delay_initial_ms: u64,
158    #[builder(default = 10000)]
159    pub retry_delay_max_ms: u64,
160    /// Timeout waiting for WS order acknowledgment (seconds).
161    #[builder(default = 5)]
162    pub ack_timeout_secs: u64,
163    /// WebSocket transport backend (defaults to `Tungstenite`).
164    #[builder(default)]
165    pub transport_backend: TransportBackend,
166}
167
168impl Debug for PolymarketExecClientConfig {
169    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
170        f.debug_struct(stringify!(PolymarketExecClientConfig))
171            .field("trader_id", &self.trader_id)
172            .field("account_id", &self.account_id)
173            .field("private_key", &"***")
174            .field("api_key", &"***")
175            .field("api_secret", &"***")
176            .field("passphrase", &"***")
177            .field("funder", &self.funder)
178            .field("signature_type", &self.signature_type)
179            .field("base_url_http", &self.base_url_http)
180            .field("base_url_ws", &self.base_url_ws)
181            .field("base_url_data_api", &self.base_url_data_api)
182            .field("http_timeout_secs", &self.http_timeout_secs)
183            .field("max_retries", &self.max_retries)
184            .field("retry_delay_initial_ms", &self.retry_delay_initial_ms)
185            .field("retry_delay_max_ms", &self.retry_delay_max_ms)
186            .field("ack_timeout_secs", &self.ack_timeout_secs)
187            .finish()
188    }
189}
190
191impl Default for PolymarketExecClientConfig {
192    fn default() -> Self {
193        Self::builder().build()
194    }
195}
196
197impl PolymarketExecClientConfig {
198    #[must_use]
199    pub fn new() -> Self {
200        Self::default()
201    }
202
203    #[must_use]
204    pub fn has_credentials(&self) -> bool {
205        self.private_key
206            .as_deref()
207            .is_some_and(|s| !s.trim().is_empty())
208            || self
209                .api_key
210                .as_deref()
211                .is_some_and(|s| !s.trim().is_empty())
212    }
213
214    #[must_use]
215    pub fn http_url(&self) -> String {
216        self.base_url_http
217            .clone()
218            .unwrap_or_else(|| urls::clob_http_url().to_string())
219    }
220
221    #[must_use]
222    pub fn ws_url(&self) -> String {
223        self.base_url_ws
224            .clone()
225            .unwrap_or_else(|| urls::clob_ws_url().to_string())
226    }
227
228    #[must_use]
229    pub fn data_api_url(&self) -> String {
230        self.base_url_data_api
231            .clone()
232            .unwrap_or_else(|| "https://data-api.polymarket.com".to_string())
233    }
234}