1use std::time::Duration;
2
3#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
8#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
9#[serde(rename_all = "lowercase")]
10pub enum MarketStatusFilter {
11 Active,
12 Closed,
13 Resolved,
14 All,
15}
16
17impl std::fmt::Display for MarketStatusFilter {
18 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19 match self {
20 MarketStatusFilter::Active => write!(f, "active"),
21 MarketStatusFilter::Closed => write!(f, "closed"),
22 MarketStatusFilter::Resolved => write!(f, "resolved"),
23 MarketStatusFilter::All => write!(f, "all"),
24 }
25 }
26}
27
28impl std::str::FromStr for MarketStatusFilter {
29 type Err = String;
30
31 fn from_str(s: &str) -> Result<Self, Self::Err> {
32 match s.to_lowercase().as_str() {
33 "active" | "open" => Ok(MarketStatusFilter::Active),
34 "closed" | "inactive" | "paused" => Ok(MarketStatusFilter::Closed),
35 "resolved" | "settled" | "determined" | "finalized" => Ok(MarketStatusFilter::Resolved),
36 "all" => Ok(MarketStatusFilter::All),
37 _ => Err(format!("Unknown market status filter: {}", s)),
38 }
39 }
40}
41
42impl From<crate::models::MarketStatus> for MarketStatusFilter {
43 fn from(s: crate::models::MarketStatus) -> Self {
44 match s {
45 crate::models::MarketStatus::Active => MarketStatusFilter::Active,
46 crate::models::MarketStatus::Closed => MarketStatusFilter::Closed,
47 crate::models::MarketStatus::Resolved => MarketStatusFilter::Resolved,
48 }
49 }
50}
51
52#[derive(Debug, Clone)]
53pub struct ExchangeConfig {
54 pub timeout: Duration,
55 pub rate_limit_per_second: u32,
56 pub max_retries: u32,
57 pub retry_delay: Duration,
58 pub verbose: bool,
59}
60
61impl Default for ExchangeConfig {
62 fn default() -> Self {
63 Self {
64 timeout: Duration::from_secs(30),
65 rate_limit_per_second: 10,
66 max_retries: 3,
67 retry_delay: Duration::from_secs(1),
68 verbose: false,
69 }
70 }
71}
72
73impl ExchangeConfig {
74 pub fn new() -> Self {
75 Self::default()
76 }
77
78 pub fn with_timeout(mut self, timeout: Duration) -> Self {
79 self.timeout = timeout;
80 self
81 }
82
83 pub fn with_rate_limit(mut self, requests_per_second: u32) -> Self {
84 self.rate_limit_per_second = requests_per_second;
85 self
86 }
87
88 pub fn with_retries(mut self, max_retries: u32, delay: Duration) -> Self {
89 self.max_retries = max_retries;
90 self.retry_delay = delay;
91 self
92 }
93
94 pub fn with_verbose(mut self, verbose: bool) -> Self {
95 self.verbose = verbose;
96 self
97 }
98}
99
100#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
101#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
102pub struct FetchMarketsParams {
103 pub limit: Option<usize>,
107 #[serde(default)]
109 pub cursor: Option<String>,
110 #[serde(default)]
113 pub status: Option<MarketStatusFilter>,
114 #[serde(default)]
118 pub series_id: Option<String>,
119 #[serde(default)]
126 pub event_id: Option<String>,
127}
128
129#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
130#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
131pub struct FetchOrdersParams {
132 pub market_id: Option<String>,
133}
134
135#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
136#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
137pub struct FetchUserActivityParams {
138 pub address: String,
139 pub limit: Option<usize>,
140}
141
142#[derive(Debug, Clone)]
176pub struct PolymarketCredentials {
177 pub private_key: Option<String>,
178 pub funder: Option<String>,
179 pub api_key: Option<String>,
180 pub api_secret: Option<String>,
181 pub api_passphrase: Option<String>,
182 pub signature_type: String,
183}
184
185impl PolymarketCredentials {
186 pub fn from_fields(
192 private_key: Option<String>,
193 funder: Option<String>,
194 api_key: Option<String>,
195 api_secret: Option<String>,
196 api_passphrase: Option<String>,
197 signature_type: Option<String>,
198 ) -> Self {
199 let resolved_signature_type = signature_type.unwrap_or_else(|| {
201 if funder.is_some() {
202 "GnosisSafe".to_string()
203 } else {
204 "EOA".to_string()
205 }
206 });
207
208 Self {
209 private_key,
210 funder,
211 api_key,
212 api_secret,
213 api_passphrase,
214 signature_type: resolved_signature_type,
215 }
216 }
217}
218
219#[cfg(test)]
220mod tests {
221 use super::*;
222 use crate::models::MarketStatus;
223
224 #[test]
225 fn market_status_filter_from_str() {
226 assert_eq!(
227 "active".parse::<MarketStatusFilter>().unwrap(),
228 MarketStatusFilter::Active
229 );
230 assert_eq!(
231 "open".parse::<MarketStatusFilter>().unwrap(),
232 MarketStatusFilter::Active
233 );
234 assert_eq!(
235 "closed".parse::<MarketStatusFilter>().unwrap(),
236 MarketStatusFilter::Closed
237 );
238 assert_eq!(
239 "resolved".parse::<MarketStatusFilter>().unwrap(),
240 MarketStatusFilter::Resolved
241 );
242 assert_eq!(
243 "settled".parse::<MarketStatusFilter>().unwrap(),
244 MarketStatusFilter::Resolved
245 );
246 assert_eq!(
247 "all".parse::<MarketStatusFilter>().unwrap(),
248 MarketStatusFilter::All
249 );
250 assert_eq!(
251 "ALL".parse::<MarketStatusFilter>().unwrap(),
252 MarketStatusFilter::All
253 );
254 assert!("invalid".parse::<MarketStatusFilter>().is_err());
255 }
256
257 #[test]
258 fn market_status_filter_display() {
259 assert_eq!(MarketStatusFilter::Active.to_string(), "active");
260 assert_eq!(MarketStatusFilter::Closed.to_string(), "closed");
261 assert_eq!(MarketStatusFilter::Resolved.to_string(), "resolved");
262 assert_eq!(MarketStatusFilter::All.to_string(), "all");
263 }
264
265 #[test]
266 fn market_status_filter_from_market_status() {
267 assert_eq!(
268 MarketStatusFilter::from(MarketStatus::Active),
269 MarketStatusFilter::Active
270 );
271 assert_eq!(
272 MarketStatusFilter::from(MarketStatus::Closed),
273 MarketStatusFilter::Closed
274 );
275 assert_eq!(
276 MarketStatusFilter::from(MarketStatus::Resolved),
277 MarketStatusFilter::Resolved
278 );
279 }
280
281 #[test]
282 fn market_status_filter_serde_roundtrip() {
283 let filter = MarketStatusFilter::All;
284 let json = serde_json::to_string(&filter).unwrap();
285 assert_eq!(json, "\"all\"");
286 let parsed: MarketStatusFilter = serde_json::from_str(&json).unwrap();
287 assert_eq!(parsed, MarketStatusFilter::All);
288 }
289
290 #[test]
291 fn fetch_markets_params_default_status_is_none() {
292 let params = FetchMarketsParams::default();
293 assert!(params.status.is_none());
294 }
295
296 #[test]
297 fn fetch_markets_params_serde_with_all_status() {
298 let params = FetchMarketsParams {
299 status: Some(MarketStatusFilter::All),
300 ..Default::default()
301 };
302 let json = serde_json::to_value(¶ms).unwrap();
303 assert_eq!(json["status"], "all");
304
305 let parsed: FetchMarketsParams = serde_json::from_value(json).unwrap();
306 assert_eq!(parsed.status, Some(MarketStatusFilter::All));
307 }
308}
309
310#[derive(Debug, Clone)]
311pub struct KalshiCredentials {
312 pub api_key_id: String,
313 pub private_key: String,
314}
315
316#[derive(Debug, Clone)]
317pub struct OpinionCredentials {
318 pub api_key: String,
319 pub private_key: String,
320 pub multi_sig_addr: String,
321}