1use crate::client::PolymarketUsClient;
2use crate::error::PolymarketUsError;
3use crate::types;
4use reqwest::Method;
5use serde::Serialize;
6
7#[derive(Clone)]
12pub struct MarketsClient<'a> {
13 client: &'a PolymarketUsClient,
14}
15
16impl<'a> MarketsClient<'a> {
17 pub fn new(client: &'a PolymarketUsClient) -> Self {
18 Self { client }
19 }
20
21 pub async fn list(&self) -> Result<types::MarketsResponse, PolymarketUsError> {
23 self.list_with_query::<()>(None).await
24 }
25
26 pub async fn list_with_query<Q: Serialize>(
28 &self,
29 query: Option<&Q>,
30 ) -> Result<types::MarketsResponse, PolymarketUsError> {
31 self.client
32 .internal_request(Method::GET, "/v1/markets", query, None::<&()>, false)
33 .await
34 }
35
36 pub async fn list_authenticated(&self) -> Result<types::MarketsResponse, PolymarketUsError> {
38 self.list_authenticated_with_query::<()>(None).await
39 }
40
41 pub async fn list_authenticated_with_query<Q: Serialize>(
43 &self,
44 query: Option<&Q>,
45 ) -> Result<types::MarketsResponse, PolymarketUsError> {
46 self.client
47 .internal_request(Method::GET, "/v1/markets", query, None::<&()>, true)
48 .await
49 }
50
51 pub async fn order_book(&self, symbol: &str) -> Result<types::OrderBook, PolymarketUsError> {
53 self.client
54 .internal_request::<(), (), types::OrderBook>(
55 Method::GET,
56 &format!("/v1/markets/{symbol}/book"),
57 None,
58 None,
59 false,
60 )
61 .await
62 }
63
64 pub async fn bbo(&self, symbol: &str) -> Result<types::BestBidOffer, PolymarketUsError> {
66 self.client
67 .internal_request::<(), (), types::BestBidOffer>(
68 Method::GET,
69 &format!("/v1/markets/{symbol}/bbo"),
70 None,
71 None,
72 false,
73 )
74 .await
75 }
76
77 pub async fn settlement_price(
79 &self,
80 symbol: &str,
81 ) -> Result<types::SettlementPrice, PolymarketUsError> {
82 self.client
83 .internal_request::<(), (), types::SettlementPrice>(
84 Method::GET,
85 &format!("/v1/markets/{symbol}/settlement"),
86 None,
87 None,
88 false,
89 )
90 .await
91 }
92}
93
94#[derive(Clone)]
99pub struct EventsClient<'a> {
100 client: &'a PolymarketUsClient,
101}
102
103impl<'a> EventsClient<'a> {
104 pub fn new(client: &'a PolymarketUsClient) -> Self {
105 Self { client }
106 }
107
108 pub async fn list(&self) -> Result<types::EventsResponse, PolymarketUsError> {
110 self.list_with_query::<()>(None).await
111 }
112
113 pub async fn list_with_query<Q: Serialize>(
115 &self,
116 query: Option<&Q>,
117 ) -> Result<types::EventsResponse, PolymarketUsError> {
118 self.client
119 .internal_request(Method::GET, "/v1/events", query, None::<&()>, false)
120 .await
121 }
122
123 pub async fn retrieve(&self, event_id: &str) -> Result<types::UsEvent, PolymarketUsError> {
125 self.client
126 .internal_request::<(), (), types::UsEvent>(
127 Method::GET,
128 &format!("/v1/events/{event_id}"),
129 None,
130 None,
131 false,
132 )
133 .await
134 }
135
136 pub async fn retrieve_by_slug(&self, slug: &str) -> Result<types::UsEvent, PolymarketUsError> {
138 self.client
139 .internal_request::<(), (), types::UsEvent>(
140 Method::GET,
141 &format!("/v1/events/by-slug/{slug}"),
142 None,
143 None,
144 false,
145 )
146 .await
147 }
148}
149
150#[derive(Clone)]
155pub struct OrdersClient<'a> {
156 client: &'a PolymarketUsClient,
157}
158
159impl<'a> OrdersClient<'a> {
160 pub fn new(client: &'a PolymarketUsClient) -> Self {
161 Self { client }
162 }
163
164 pub async fn create(
166 &self,
167 body: &types::PlaceOrderRequest,
168 ) -> Result<types::PlaceOrderResponse, PolymarketUsError> {
169 self.client
170 .internal_request(Method::POST, "/v1/orders", None::<&()>, Some(body), true)
171 .await
172 }
173
174 pub async fn place(
176 &self,
177 body: &types::PlaceOrderRequest,
178 ) -> Result<types::PlaceOrderResponse, PolymarketUsError> {
179 self.client
180 .internal_request(
181 Method::POST,
182 "/v1/trading/orders",
183 None::<&()>,
184 Some(body),
185 true,
186 )
187 .await
188 }
189
190 pub async fn place_batch(
192 &self,
193 body: &types::BatchedOrderRequest,
194 ) -> Result<types::BatchedOrderResponse, PolymarketUsError> {
195 self.client
196 .internal_request(
197 Method::POST,
198 "/v1/orders/batched",
199 None::<&()>,
200 Some(body),
201 true,
202 )
203 .await
204 }
205
206 pub async fn open<Q: Serialize>(
208 &self,
209 query: Option<&Q>,
210 ) -> Result<types::GetOpenOrdersResponse, PolymarketUsError> {
211 self.client
212 .internal_request(Method::GET, "/v1/orders/open", query, None::<&()>, true)
213 .await
214 }
215
216 pub async fn retrieve(
218 &self,
219 order_id: &str,
220 ) -> Result<types::PlaceOrderResponse, PolymarketUsError> {
221 self.client
222 .internal_request::<(), (), types::PlaceOrderResponse>(
223 Method::GET,
224 &format!("/v1/order/{order_id}"),
225 None,
226 None,
227 true,
228 )
229 .await
230 }
231
232 pub async fn cancel(
234 &self,
235 order_id: &str,
236 body: &types::CancelOrderParams,
237 ) -> Result<(), PolymarketUsError> {
238 let _: serde_json::Value = self
239 .client
240 .internal_request(
241 Method::POST,
242 &format!("/v1/order/{order_id}/cancel"),
243 None::<&()>,
244 Some(body),
245 true,
246 )
247 .await?;
248 Ok(())
249 }
250
251 pub async fn cancel_trading(
253 &self,
254 order_id: &str,
255 ) -> Result<types::CancelOrderResponse, PolymarketUsError> {
256 self.client
257 .internal_request::<(), (), types::CancelOrderResponse>(
258 Method::DELETE,
259 &format!("/v1/trading/orders/{order_id}"),
260 None,
261 None,
262 true,
263 )
264 .await
265 }
266
267 pub async fn cancel_all(
269 &self,
270 body: &types::CancelAllOrdersParams,
271 ) -> Result<types::CancelAllOrdersResponse, PolymarketUsError> {
272 self.client
273 .internal_request(
274 Method::POST,
275 "/v1/orders/open/cancel",
276 None::<&()>,
277 Some(body),
278 true,
279 )
280 .await
281 }
282
283 pub async fn modify(
285 &self,
286 order_id: &str,
287 body: &types::ModifyOrderRequest,
288 ) -> Result<(), PolymarketUsError> {
289 let _: serde_json::Value = self
290 .client
291 .internal_request(
292 Method::POST,
293 &format!("/v1/order/{order_id}/modify"),
294 None::<&()>,
295 Some(body),
296 true,
297 )
298 .await?;
299 Ok(())
300 }
301
302 pub async fn preview(
304 &self,
305 body: &types::PreviewOrderRequest,
306 ) -> Result<types::PreviewOrderResponse, PolymarketUsError> {
307 self.client
308 .internal_request(
309 Method::POST,
310 "/v1/order/preview",
311 None::<&()>,
312 Some(body),
313 true,
314 )
315 .await
316 }
317
318 pub async fn close_position(
320 &self,
321 body: &types::ClosePositionRequest,
322 ) -> Result<types::ClosePositionResponse, PolymarketUsError> {
323 self.client
324 .internal_request(
325 Method::POST,
326 "/v1/order/close-position",
327 None::<&()>,
328 Some(body),
329 true,
330 )
331 .await
332 }
333}
334
335#[derive(Clone)]
340pub struct AccountClient<'a> {
341 client: &'a PolymarketUsClient,
342}
343
344impl<'a> AccountClient<'a> {
345 pub fn new(client: &'a PolymarketUsClient) -> Self {
346 Self { client }
347 }
348
349 pub async fn balances(&self) -> Result<types::AccountBalancesResponse, PolymarketUsError> {
351 self.client
352 .internal_request::<(), (), types::AccountBalancesResponse>(
353 Method::GET,
354 "/v1/account/balances",
355 None,
356 None,
357 true,
358 )
359 .await
360 }
361}
362
363#[derive(Clone)]
368pub struct PortfolioClient<'a> {
369 client: &'a PolymarketUsClient,
370}
371
372impl<'a> PortfolioClient<'a> {
373 pub fn new(client: &'a PolymarketUsClient) -> Self {
374 Self { client }
375 }
376
377 pub async fn positions(&self) -> Result<types::PortfolioPositionsResponse, PolymarketUsError> {
379 self.client
380 .internal_request::<(), (), types::PortfolioPositionsResponse>(
381 Method::GET,
382 "/v1/portfolio/positions",
383 None,
384 None,
385 true,
386 )
387 .await
388 }
389
390 pub async fn activities<Q: Serialize>(
392 &self,
393 query: Option<&Q>,
394 ) -> Result<types::PortfolioActivitiesResponse, PolymarketUsError> {
395 self.client
396 .internal_request(
397 Method::GET,
398 "/v1/portfolio/activities",
399 query,
400 None::<&()>,
401 true,
402 )
403 .await
404 }
405}
406
407#[derive(Clone)]
412pub struct SearchClient<'a> {
413 client: &'a PolymarketUsClient,
414}
415
416impl<'a> SearchClient<'a> {
417 pub fn new(client: &'a PolymarketUsClient) -> Self {
418 Self { client }
419 }
420
421 pub async fn search<Q: Serialize>(
423 &self,
424 query: Option<&Q>,
425 ) -> Result<types::SearchResults, PolymarketUsError> {
426 self.client
427 .internal_request(Method::GET, "/v1/search", query, None::<&()>, false)
428 .await
429 }
430
431 pub async fn markets<Q: Serialize>(
433 &self,
434 query: Option<&Q>,
435 ) -> Result<types::MarketsResponse, PolymarketUsError> {
436 self.client
437 .internal_request(Method::GET, "/v1/search/markets", query, None::<&()>, false)
438 .await
439 }
440
441 pub async fn events<Q: Serialize>(
443 &self,
444 query: Option<&Q>,
445 ) -> Result<types::EventsResponse, PolymarketUsError> {
446 self.client
447 .internal_request(Method::GET, "/v1/search/events", query, None::<&()>, false)
448 .await
449 }
450}
451
452#[cfg(test)]
453mod tests {
454 use super::*;
455
456 fn create_test_client() -> PolymarketUsClient {
457 PolymarketUsClient::builder().build().unwrap()
458 }
459
460 #[test]
465 fn markets_client_creation() {
466 let client = create_test_client();
467 let _markets = client.markets();
469 }
470
471 #[test]
472 fn markets_client_has_expected_methods() {
473 let client = create_test_client();
474 let markets = client.markets();
475
476 assert_eq!(
479 std::any::type_name_of_val(&markets),
480 "polymarket_us::resources::MarketsClient<'_>"
481 );
482 }
483
484 #[test]
489 fn events_client_creation() {
490 let client = create_test_client();
491 let _events = client.events();
492 }
493
494 #[test]
495 fn events_client_type_check() {
496 let client = create_test_client();
497 let events = client.events();
498 assert_eq!(
499 std::any::type_name_of_val(&events),
500 "polymarket_us::resources::EventsClient<'_>"
501 );
502 }
503
504 #[test]
509 fn orders_client_creation() {
510 let client = create_test_client();
511 let _orders = client.orders();
512 }
513
514 #[test]
515 fn orders_client_type_check() {
516 let client = create_test_client();
517 let orders = client.orders();
518 assert_eq!(
519 std::any::type_name_of_val(&orders),
520 "polymarket_us::resources::OrdersClient<'_>"
521 );
522 }
523
524 #[test]
525 fn place_order_request_serializes() {
526 let req = types::PlaceOrderRequest {
527 symbol: "BTC-USD".to_string(),
528 action: types::OrderAction::Buy,
529 outcome_side: types::OrderSide::Long,
530 order_type: types::OrderType::Limit,
531 price: types::Money {
532 value: "0.50".to_string(),
533 currency: "USD".to_string(),
534 },
535 quantity: 100,
536 tif: types::TimeInForce::GoodTillCancel,
537 client_order_id: Some("test-123".to_string()),
538 post_only: false,
539 expires_at: None,
540 };
541
542 let json = serde_json::to_string(&req).expect("should serialize");
543 assert!(json.contains("BTC-USD"));
544 assert!(json.contains("ORDER_ACTION_BUY"));
545 assert!(json.contains("0.50"));
546 }
547
548 #[test]
549 fn batched_order_request_serializes() {
550 let req = types::BatchedOrderRequest {
551 orders: vec![types::PlaceOrderRequest {
552 symbol: "BTC-USD".to_string(),
553 action: types::OrderAction::Buy,
554 outcome_side: types::OrderSide::Long,
555 order_type: types::OrderType::Limit,
556 price: types::Money {
557 value: "0.50".to_string(),
558 currency: "USD".to_string(),
559 },
560 quantity: 100,
561 tif: types::TimeInForce::GoodTillCancel,
562 client_order_id: None,
563 post_only: false,
564 expires_at: None,
565 }],
566 atomic: true,
567 };
568
569 let json = serde_json::to_string(&req).expect("should serialize");
570 assert!(json.contains("atomic"));
571 assert!(json.contains("BTC-USD"));
572 }
573
574 #[test]
575 fn cancel_order_params_serializes() {
576 let params = types::CancelOrderParams { quantity: Some(50) };
577 let json = serde_json::to_string(¶ms).expect("should serialize");
578 assert!(json.contains("50"));
579 }
580
581 #[test]
586 fn account_client_creation() {
587 let client = create_test_client();
588 let _account = client.account();
589 }
590
591 #[test]
592 fn account_client_type_check() {
593 let client = create_test_client();
594 let account = client.account();
595 assert_eq!(
596 std::any::type_name_of_val(&account),
597 "polymarket_us::resources::AccountClient<'_>"
598 );
599 }
600
601 #[test]
606 fn portfolio_client_creation() {
607 let client = create_test_client();
608 let _portfolio = client.portfolio();
609 }
610
611 #[test]
612 fn portfolio_client_type_check() {
613 let client = create_test_client();
614 let portfolio = client.portfolio();
615 assert_eq!(
616 std::any::type_name_of_val(&portfolio),
617 "polymarket_us::resources::PortfolioClient<'_>"
618 );
619 }
620
621 #[test]
626 fn search_client_creation() {
627 let client = create_test_client();
628 let _search = client.search();
629 }
630
631 #[test]
632 fn search_client_type_check() {
633 let client = create_test_client();
634 let search = client.search();
635 assert_eq!(
636 std::any::type_name_of_val(&search),
637 "polymarket_us::resources::SearchClient<'_>"
638 );
639 }
640
641 #[test]
646 fn money_serializes() {
647 let money = types::Money {
648 value: "100.00".to_string(),
649 currency: "USD".to_string(),
650 };
651 let json = serde_json::to_string(&money).expect("should serialize");
652 assert!(json.contains("100.00"));
653 assert!(json.contains("USD"));
654 }
655
656 #[test]
657 fn preview_order_request_serializes() {
658 let req = types::PreviewOrderRequest {
659 symbol: "BTC-USD".to_string(),
660 action: types::OrderAction::Sell,
661 outcome_side: types::OrderSide::Short,
662 order_type: types::OrderType::Limit,
663 price: types::Money {
664 value: "0.75".to_string(),
665 currency: "USD".to_string(),
666 },
667 quantity: 50,
668 };
669
670 let json = serde_json::to_string(&req).expect("should serialize");
671 assert!(json.contains("ORDER_ACTION_SELL"));
672 assert!(json.contains("0.75"));
673 }
674
675 #[test]
676 fn close_position_request_serializes() {
677 let req = types::ClosePositionRequest {
678 symbol: "BTC-USD".to_string(),
679 quantity: 100,
680 };
681 let json = serde_json::to_string(&req).expect("should serialize");
682 assert!(json.contains("BTC-USD"));
683 assert!(json.contains("100"));
684 }
685
686 #[test]
687 fn modify_order_request_serializes() {
688 let req = types::ModifyOrderRequest {
689 price: types::Money {
690 value: "0.60".to_string(),
691 currency: "USD".to_string(),
692 },
693 quantity: 200,
694 };
695 let json = serde_json::to_string(&req).expect("should serialize");
696 assert!(json.contains("0.60"));
697 assert!(json.contains("200"));
698 }
699
700 #[test]
701 fn order_book_deserializes() {
702 let json = r#"{"bids": [{"price": "0.50", "quantity": "100"}], "asks": [{"price": "0.55", "quantity": "150"}]}"#;
703 let book: types::OrderBook = serde_json::from_str(json).expect("should deserialize");
704 assert_eq!(book.bids.len(), 1);
705 assert_eq!(book.asks.len(), 1);
706 assert_eq!(book.bids[0].price, "0.50");
707 }
708
709 #[test]
710 fn best_bid_offer_deserializes() {
711 let json = r#"{"bid": {"price": "0.50", "quantity": "100"}, "ask": {"price": "0.55", "quantity": "150"}}"#;
712 let bbo: types::BestBidOffer = serde_json::from_str(json).expect("should deserialize");
713 assert!(bbo.bid.is_some());
714 assert!(bbo.ask.is_some());
715 assert_eq!(bbo.bid.unwrap().price, "0.50");
716 }
717
718 #[test]
719 fn price_level_serializes() {
720 let level = types::PriceLevel {
721 price: "0.55".to_string(),
722 quantity: "200".to_string(),
723 };
724 let json = serde_json::to_string(&level).expect("should serialize");
725 assert!(json.contains("0.55"));
726 assert!(json.contains("200"));
727 }
728
729 #[test]
730 fn settlement_price_deserializes() {
731 let json = r#"{"symbol": "BTC-USD", "price": "0.75", "timestamp": "2024-01-01T00:00:00Z"}"#;
732 let settlement: types::SettlementPrice =
733 serde_json::from_str(json).expect("should deserialize");
734 assert_eq!(settlement.symbol, "BTC-USD");
735 assert_eq!(settlement.price, "0.75");
736 }
737
738 #[test]
739 fn user_balance_deserializes() {
740 let json = r#"{
741 "currentBalance": 1000.00,
742 "currency": "USD",
743 "buyingPower": 950.00,
744 "assetNotional": 500.00,
745 "assetAvailable": 450.00
746 }"#;
747 let balance: types::UserBalance = serde_json::from_str(json).expect("should deserialize");
748 assert_eq!(balance.current_balance, 1000.00);
749 assert_eq!(balance.buying_power, 950.00);
750 assert_eq!(balance.currency, "USD");
751 }
752
753 #[test]
754 fn cancel_all_orders_params_serializes() {
755 let params = types::CancelAllOrdersParams {
756 symbol: Some("BTC-USD".to_string()),
757 };
758 let json = serde_json::to_string(¶ms).expect("should serialize");
759 assert!(json.contains("BTC-USD"));
760 }
761
762 #[test]
763 fn us_position_deserializes() {
764 let json = r#"{
765 "symbol": "BTC-USD",
766 "quantity": 100,
767 "avgEntryPrice": "0.50",
768 "unrealizedPnl": "25.00"
769 }"#;
770 let position: types::UsPosition = serde_json::from_str(json).expect("should deserialize");
771 assert_eq!(position.symbol, "BTC-USD");
772 assert_eq!(position.quantity, 100);
773 assert_eq!(position.avg_entry_price, "0.50");
774 }
775
776 #[test]
777 fn us_market_deserializes() {
778 let json = r#"{
779 "id": "market-123",
780 "slug": "btc-usd",
781 "question": "Will BTC be above $50k?",
782 "status": "open",
783 "category": "crypto",
784 "startDate": "2024-01-01",
785 "endDate": "2024-12-31",
786 "description": "Test market",
787 "active": true,
788 "closed": false,
789 "marketType": "binary",
790 "marketSides": [],
791 "instruments": [],
792 "outcomes": []
793 }"#;
794 let market: types::UsMarket = serde_json::from_str(json).expect("should deserialize");
795 assert_eq!(market.id, "market-123");
796 assert_eq!(market.slug, "btc-usd");
797 assert!(market.active);
798 }
799
800 #[test]
801 fn us_event_deserializes() {
802 let json = r#"{
803 "id": "event-123",
804 "slug": "2024-election",
805 "title": "2024 US Election",
806 "category": "politics",
807 "startDate": "2024-01-01",
808 "endDate": "2024-11-05"
809 }"#;
810 let event: types::UsEvent = serde_json::from_str(json).expect("should deserialize");
811 assert_eq!(event.id, "event-123");
812 assert_eq!(event.slug, "2024-election");
813 assert_eq!(event.title, "2024 US Election");
814 }
815
816 #[test]
817 fn client_has_all_resource_accessors() {
818 let client = create_test_client();
819
820 let _ = client.markets();
822 let _ = client.events();
823 let _ = client.orders();
824 let _ = client.account();
825 let _ = client.portfolio();
826 let _ = client.search();
827
828 }
830
831 #[test]
832 fn resources_are_cheap_to_clone() {
833 let client = create_test_client();
834 let markets1 = client.markets();
835 let _markets2 = markets1.clone();
836 }
837}