longport/trade/context.rs
1use std::sync::Arc;
2
3use longport_httpcli::{HttpClient, Json, Method};
4use longport_wscli::WsClientError;
5use rust_decimal::Decimal;
6use serde::{Deserialize, Serialize};
7use tokio::sync::{mpsc, oneshot};
8use tracing::{Subscriber, dispatcher, instrument::WithSubscriber};
9
10use crate::{
11 Config, Result, serde_utils,
12 trade::{
13 AccountBalance, CashFlow, EstimateMaxPurchaseQuantityOptions, Execution,
14 FundPositionsResponse, GetCashFlowOptions, GetFundPositionsOptions,
15 GetHistoryExecutionsOptions, GetHistoryOrdersOptions, GetStockPositionsOptions,
16 GetTodayExecutionsOptions, GetTodayOrdersOptions, MarginRatio, Order, OrderDetail,
17 PushEvent, ReplaceOrderOptions, StockPositionsResponse, SubmitOrderOptions, TopicType,
18 core::{Command, Core},
19 },
20};
21
22#[derive(Debug, Deserialize)]
23struct EmptyResponse {}
24
25/// Response for submit order request
26#[derive(Debug, Serialize, Deserialize)]
27pub struct SubmitOrderResponse {
28 /// Order id
29 pub order_id: String,
30}
31
32/// Response for estimate maximum purchase quantity
33#[derive(Debug, Serialize, Deserialize)]
34pub struct EstimateMaxPurchaseQuantityResponse {
35 /// Cash available quantity
36 #[serde(with = "serde_utils::decimal_empty_is_0")]
37 pub cash_max_qty: Decimal,
38 /// Margin available quantity
39 #[serde(with = "serde_utils::decimal_empty_is_0")]
40 pub margin_max_qty: Decimal,
41}
42
43struct InnerTradeContext {
44 command_tx: mpsc::UnboundedSender<Command>,
45 http_cli: HttpClient,
46 log_subscriber: Arc<dyn Subscriber + Send + Sync>,
47}
48
49impl Drop for InnerTradeContext {
50 fn drop(&mut self) {
51 dispatcher::with_default(&self.log_subscriber.clone().into(), || {
52 tracing::info!("trade context dropped");
53 });
54 }
55}
56
57/// Trade context
58#[derive(Clone)]
59pub struct TradeContext(Arc<InnerTradeContext>);
60
61impl TradeContext {
62 /// Create a `TradeContext`
63 pub async fn try_new(
64 config: Arc<Config>,
65 ) -> Result<(Self, mpsc::UnboundedReceiver<PushEvent>)> {
66 let log_subscriber = config.create_log_subscriber("trade");
67
68 dispatcher::with_default(&log_subscriber.clone().into(), || {
69 tracing::info!(language = ?config.language, "creating trade context");
70 });
71
72 let http_cli = config.create_http_client();
73 let (command_tx, command_rx) = mpsc::unbounded_channel();
74 let (push_tx, push_rx) = mpsc::unbounded_channel();
75 let core = Core::try_new(config, command_rx, push_tx)
76 .with_subscriber(log_subscriber.clone())
77 .await?;
78 tokio::spawn(core.run().with_subscriber(log_subscriber.clone()));
79
80 dispatcher::with_default(&log_subscriber.clone().into(), || {
81 tracing::info!("trade context created");
82 });
83
84 Ok((
85 TradeContext(Arc::new(InnerTradeContext {
86 http_cli,
87 command_tx,
88 log_subscriber,
89 })),
90 push_rx,
91 ))
92 }
93
94 /// Returns the log subscriber
95 #[inline]
96 pub fn log_subscriber(&self) -> Arc<dyn Subscriber + Send + Sync> {
97 self.0.log_subscriber.clone()
98 }
99
100 /// Subscribe
101 ///
102 /// Reference: <https://open.longportapp.com/en/docs/trade/trade-push#subscribe>
103 ///
104 /// # Examples
105 ///
106 /// ```no_run
107 /// use std::sync::Arc;
108 ///
109 /// use longport::{
110 /// Config, decimal,
111 /// trade::{OrderSide, OrderType, SubmitOrderOptions, TimeInForceType, TradeContext},
112 /// };
113 ///
114 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
115 /// let config = Arc::new(Config::from_env()?);
116 /// let (ctx, mut receiver) = TradeContext::try_new(config).await?;
117 ///
118 /// let opts = SubmitOrderOptions::new(
119 /// "700.HK",
120 /// OrderType::LO,
121 /// OrderSide::Buy,
122 /// decimal!(200),
123 /// TimeInForceType::Day,
124 /// )
125 /// .submitted_price(decimal!(50i32));
126 /// let resp = ctx.submit_order(opts).await?;
127 /// println!("{:?}", resp);
128 ///
129 /// while let Some(event) = receiver.recv().await {
130 /// println!("{:?}", event);
131 /// }
132 ///
133 /// # Ok::<_, Box<dyn std::error::Error>>(())
134 /// # });
135 /// ```
136 pub async fn subscribe<I>(&self, topics: I) -> Result<()>
137 where
138 I: IntoIterator<Item = TopicType>,
139 {
140 let (reply_tx, reply_rx) = oneshot::channel();
141 self.0
142 .command_tx
143 .send(Command::Subscribe {
144 topics: topics.into_iter().collect(),
145 reply_tx,
146 })
147 .map_err(|_| WsClientError::ClientClosed)?;
148 reply_rx.await.map_err(|_| WsClientError::ClientClosed)?
149 }
150
151 /// Unsubscribe
152 ///
153 /// Reference: <https://open.longportapp.com/en/docs/trade/trade-push#cancel-subscribe>
154 pub async fn unsubscribe<I>(&self, topics: I) -> Result<()>
155 where
156 I: IntoIterator<Item = TopicType>,
157 {
158 let (reply_tx, reply_rx) = oneshot::channel();
159 self.0
160 .command_tx
161 .send(Command::Unsubscribe {
162 topics: topics.into_iter().collect(),
163 reply_tx,
164 })
165 .map_err(|_| WsClientError::ClientClosed)?;
166 reply_rx.await.map_err(|_| WsClientError::ClientClosed)?
167 }
168
169 /// Get history executions
170 ///
171 /// Reference: <https://open.longportapp.com/en/docs/trade/execution/history_executions>
172 ///
173 /// # Examples
174 ///
175 /// ```no_run
176 /// use std::sync::Arc;
177 ///
178 /// use longport::{
179 /// trade::{GetHistoryExecutionsOptions, TradeContext},
180 /// Config,
181 /// };
182 /// use time::macros::datetime;
183 ///
184 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
185 /// let config = Arc::new(Config::from_env()?);
186 /// let (ctx, _) = TradeContext::try_new(config).await?;
187 ///
188 /// let opts = GetHistoryExecutionsOptions::new()
189 /// .symbol("700.HK")
190 /// .start_at(datetime!(2022-05-09 0:00 UTC))
191 /// .end_at(datetime!(2022-05-12 0:00 UTC));
192 /// let resp = ctx.history_executions(opts).await?;
193 /// println!("{:?}", resp);
194 /// # Ok::<_, Box<dyn std::error::Error>>(())
195 /// # });
196 /// ```
197 pub async fn history_executions(
198 &self,
199 options: impl Into<Option<GetHistoryExecutionsOptions>>,
200 ) -> Result<Vec<Execution>> {
201 #[derive(Deserialize)]
202 struct Response {
203 trades: Vec<Execution>,
204 }
205
206 Ok(self
207 .0
208 .http_cli
209 .request(Method::GET, "/v1/trade/execution/history")
210 .query_params(options.into().unwrap_or_default())
211 .response::<Json<Response>>()
212 .send()
213 .with_subscriber(self.0.log_subscriber.clone())
214 .await?
215 .0
216 .trades)
217 }
218
219 /// Get today executions
220 ///
221 /// Reference: <https://open.longportapp.com/en/docs/trade/execution/today_executions>
222 ///
223 /// # Examples
224 ///
225 /// ```no_run
226 /// use std::sync::Arc;
227 ///
228 /// use longport::{
229 /// Config,
230 /// trade::{GetTodayExecutionsOptions, TradeContext},
231 /// };
232 ///
233 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
234 /// let config = Arc::new(Config::from_env()?);
235 /// let (ctx, _) = TradeContext::try_new(config).await?;
236 ///
237 /// let opts = GetTodayExecutionsOptions::new().symbol("700.HK");
238 /// let resp = ctx.today_executions(opts).await?;
239 /// println!("{:?}", resp);
240 /// # Ok::<_, Box<dyn std::error::Error>>(())
241 /// # });
242 /// ```
243 pub async fn today_executions(
244 &self,
245 options: impl Into<Option<GetTodayExecutionsOptions>>,
246 ) -> Result<Vec<Execution>> {
247 #[derive(Deserialize)]
248 struct Response {
249 trades: Vec<Execution>,
250 }
251
252 Ok(self
253 .0
254 .http_cli
255 .request(Method::GET, "/v1/trade/execution/today")
256 .query_params(options.into().unwrap_or_default())
257 .response::<Json<Response>>()
258 .send()
259 .with_subscriber(self.0.log_subscriber.clone())
260 .await?
261 .0
262 .trades)
263 }
264
265 /// Get history orders
266 ///
267 /// Reference: <https://open.longportapp.com/en/docs/trade/order/history_orders>
268 ///
269 /// # Examples
270 ///
271 /// ```no_run
272 /// use std::sync::Arc;
273 ///
274 /// use longport::{
275 /// trade::{GetHistoryOrdersOptions, OrderSide, OrderStatus, TradeContext},
276 /// Config, Market,
277 /// };
278 /// use time::macros::datetime;
279 ///
280 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
281 /// let config = Arc::new(Config::from_env()?);
282 /// let (ctx, _) = TradeContext::try_new(config).await?;
283 ///
284 /// let opts = GetHistoryOrdersOptions::new()
285 /// .symbol("700.HK")
286 /// .status([OrderStatus::Filled, OrderStatus::New])
287 /// .side(OrderSide::Buy)
288 /// .market(Market::HK)
289 /// .start_at(datetime!(2022-05-09 0:00 UTC))
290 /// .end_at(datetime!(2022-05-12 0:00 UTC));
291 /// let resp = ctx.history_orders(opts).await?;
292 /// println!("{:?}", resp);
293 /// # Ok::<_, Box<dyn std::error::Error>>(())
294 /// # });
295 /// ```
296 pub async fn history_orders(
297 &self,
298 options: impl Into<Option<GetHistoryOrdersOptions>>,
299 ) -> Result<Vec<Order>> {
300 #[derive(Deserialize)]
301 struct Response {
302 orders: Vec<Order>,
303 }
304
305 Ok(self
306 .0
307 .http_cli
308 .request(Method::GET, "/v1/trade/order/history")
309 .query_params(options.into().unwrap_or_default())
310 .response::<Json<Response>>()
311 .send()
312 .with_subscriber(self.0.log_subscriber.clone())
313 .await?
314 .0
315 .orders)
316 }
317
318 /// Get today orders
319 ///
320 /// Reference: <https://open.longportapp.com/en/docs/trade/order/today_orders>
321 ///
322 /// # Examples
323 ///
324 /// ```no_run
325 /// use std::sync::Arc;
326 ///
327 /// use longport::{
328 /// Config, Market,
329 /// trade::{GetTodayOrdersOptions, OrderSide, OrderStatus, TradeContext},
330 /// };
331 ///
332 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
333 /// let config = Arc::new(Config::from_env()?);
334 /// let (ctx, _) = TradeContext::try_new(config).await?;
335 ///
336 /// let opts = GetTodayOrdersOptions::new()
337 /// .symbol("700.HK")
338 /// .status([OrderStatus::Filled, OrderStatus::New])
339 /// .side(OrderSide::Buy)
340 /// .market(Market::HK);
341 /// let resp = ctx.today_orders(opts).await?;
342 /// println!("{:?}", resp);
343 /// # Ok::<_, Box<dyn std::error::Error>>(())
344 /// # });
345 /// ```
346 pub async fn today_orders(
347 &self,
348 options: impl Into<Option<GetTodayOrdersOptions>>,
349 ) -> Result<Vec<Order>> {
350 #[derive(Deserialize)]
351 struct Response {
352 orders: Vec<Order>,
353 }
354
355 Ok(self
356 .0
357 .http_cli
358 .request(Method::GET, "/v1/trade/order/today")
359 .query_params(options.into().unwrap_or_default())
360 .response::<Json<Response>>()
361 .send()
362 .with_subscriber(self.0.log_subscriber.clone())
363 .await?
364 .0
365 .orders)
366 }
367
368 /// Replace order
369 ///
370 /// Reference: <https://open.longportapp.com/en/docs/trade/order/replace>
371 ///
372 /// # Examples
373 ///
374 /// ```no_run
375 /// use std::sync::Arc;
376 ///
377 /// use longport::{
378 /// Config, decimal,
379 /// trade::{ReplaceOrderOptions, TradeContext},
380 /// };
381 ///
382 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
383 /// let config = Arc::new(Config::from_env()?);
384 /// let (ctx, _) = TradeContext::try_new(config).await?;
385 ///
386 /// let opts =
387 /// ReplaceOrderOptions::new("709043056541253632", decimal!(100)).price(decimal!(300i32));
388 /// let resp = ctx.replace_order(opts).await?;
389 /// println!("{:?}", resp);
390 /// # Ok::<_, Box<dyn std::error::Error>>(())
391 /// # });
392 /// ```
393 pub async fn replace_order(&self, options: ReplaceOrderOptions) -> Result<()> {
394 Ok(self
395 .0
396 .http_cli
397 .request(Method::PUT, "/v1/trade/order")
398 .body(Json(options))
399 .response::<Json<EmptyResponse>>()
400 .send()
401 .with_subscriber(self.0.log_subscriber.clone())
402 .await
403 .map(|_| ())?)
404 }
405
406 /// Submit order
407 ///
408 /// Reference: <https://open.longportapp.com/en/docs/trade/order/submit>
409 ///
410 /// # Examples
411 ///
412 /// ```no_run
413 /// use std::sync::Arc;
414 ///
415 /// use longport::{
416 /// Config, decimal,
417 /// trade::{OrderSide, OrderType, SubmitOrderOptions, TimeInForceType, TradeContext},
418 /// };
419 ///
420 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
421 /// let config = Arc::new(Config::from_env()?);
422 /// let (ctx, _) = TradeContext::try_new(config).await?;
423 ///
424 /// let opts = SubmitOrderOptions::new(
425 /// "700.HK",
426 /// OrderType::LO,
427 /// OrderSide::Buy,
428 /// decimal!(200),
429 /// TimeInForceType::Day,
430 /// )
431 /// .submitted_price(decimal!(50i32));
432 /// let resp = ctx.submit_order(opts).await?;
433 /// println!("{:?}", resp);
434 /// # Ok::<_, Box<dyn std::error::Error>>(())
435 /// # });
436 /// ```
437 pub async fn submit_order(&self, options: SubmitOrderOptions) -> Result<SubmitOrderResponse> {
438 let resp: SubmitOrderResponse = self
439 .0
440 .http_cli
441 .request(Method::POST, "/v1/trade/order")
442 .body(Json(options))
443 .response::<Json<_>>()
444 .send()
445 .with_subscriber(self.0.log_subscriber.clone())
446 .await?
447 .0;
448 _ = self.0.command_tx.send(Command::SubmittedOrder {
449 order_id: resp.order_id.clone(),
450 });
451 Ok(resp)
452 }
453
454 /// Cancel order
455 ///
456 /// Reference: <https://open.longportapp.com/en/docs/trade/order/withdraw>
457 ///
458 /// # Examples
459 ///
460 /// ```no_run
461 /// use std::sync::Arc;
462 ///
463 /// use longport::{Config, trade::TradeContext};
464 ///
465 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
466 /// let config = Arc::new(Config::from_env()?);
467 /// let (ctx, _) = TradeContext::try_new(config).await?;
468 ///
469 /// ctx.cancel_order("709043056541253632").await?;
470 /// # Ok::<_, Box<dyn std::error::Error>>(())
471 /// # });
472 /// ```
473 pub async fn cancel_order(&self, order_id: impl Into<String>) -> Result<()> {
474 #[derive(Debug, Serialize)]
475 struct Request {
476 order_id: String,
477 }
478
479 Ok(self
480 .0
481 .http_cli
482 .request(Method::DELETE, "/v1/trade/order")
483 .response::<Json<EmptyResponse>>()
484 .query_params(Request {
485 order_id: order_id.into(),
486 })
487 .send()
488 .with_subscriber(self.0.log_subscriber.clone())
489 .await
490 .map(|_| ())?)
491 }
492
493 /// Get account balance
494 ///
495 /// Reference: <https://open.longportapp.com/en/docs/trade/asset/account>
496 ///
497 /// # Examples
498 ///
499 /// ```no_run
500 /// use std::sync::Arc;
501 ///
502 /// use longport::{Config, trade::TradeContext};
503 ///
504 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
505 /// let config = Arc::new(Config::from_env()?);
506 /// let (ctx, _) = TradeContext::try_new(config).await?;
507 ///
508 /// let resp = ctx.account_balance(None).await?;
509 /// println!("{:?}", resp);
510 /// # Ok::<_, Box<dyn std::error::Error>>(())
511 /// # });
512 /// ```
513 pub async fn account_balance(&self, currency: Option<&str>) -> Result<Vec<AccountBalance>> {
514 #[derive(Debug, Serialize)]
515 struct Request<'a> {
516 currency: Option<&'a str>,
517 }
518
519 #[derive(Debug, Deserialize)]
520 struct Response {
521 list: Vec<AccountBalance>,
522 }
523
524 Ok(self
525 .0
526 .http_cli
527 .request(Method::GET, "/v1/asset/account")
528 .query_params(Request { currency })
529 .response::<Json<Response>>()
530 .send()
531 .with_subscriber(self.0.log_subscriber.clone())
532 .await?
533 .0
534 .list)
535 }
536
537 /// Get cash flow
538 ///
539 /// Reference: <https://open.longportapp.com/en/docs/trade/asset/cashflow>
540 ///
541 /// # Examples
542 ///
543 /// ```no_run
544 /// use std::sync::Arc;
545 ///
546 /// use longport::{
547 /// trade::{GetCashFlowOptions, TradeContext},
548 /// Config,
549 /// };
550 /// use time::macros::datetime;
551 ///
552 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
553 /// let config = Arc::new(Config::from_env()?);
554 /// let (ctx, _) = TradeContext::try_new(config).await?;
555 ///
556 /// let opts = GetCashFlowOptions::new(datetime!(2022-05-09 0:00 UTC), datetime!(2022-05-12 0:00 UTC));
557 /// let resp = ctx.cash_flow(opts).await?;
558 /// println!("{:?}", resp);
559 /// # Ok::<_, Box<dyn std::error::Error>>(())
560 /// # });
561 /// ```
562 pub async fn cash_flow(&self, options: GetCashFlowOptions) -> Result<Vec<CashFlow>> {
563 #[derive(Debug, Deserialize)]
564 struct Response {
565 list: Vec<CashFlow>,
566 }
567
568 Ok(self
569 .0
570 .http_cli
571 .request(Method::GET, "/v1/asset/cashflow")
572 .query_params(options)
573 .response::<Json<Response>>()
574 .send()
575 .with_subscriber(self.0.log_subscriber.clone())
576 .await?
577 .0
578 .list)
579 }
580
581 /// Get fund positions
582 ///
583 /// Reference: <https://open.longportapp.com/en/docs/trade/asset/fund>
584 ///
585 /// # Examples
586 ///
587 /// ```no_run
588 /// use std::sync::Arc;
589 ///
590 /// use longport::{Config, trade::TradeContext};
591 ///
592 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
593 /// let config = Arc::new(Config::from_env()?);
594 /// let (ctx, _) = TradeContext::try_new(config).await?;
595 ///
596 /// let resp = ctx.fund_positions(None).await?;
597 /// println!("{:?}", resp);
598 /// # Ok::<_, Box<dyn std::error::Error>>(())
599 /// # });
600 /// ```
601 pub async fn fund_positions(
602 &self,
603 opts: impl Into<Option<GetFundPositionsOptions>>,
604 ) -> Result<FundPositionsResponse> {
605 Ok(self
606 .0
607 .http_cli
608 .request(Method::GET, "/v1/asset/fund")
609 .query_params(opts.into().unwrap_or_default())
610 .response::<Json<FundPositionsResponse>>()
611 .send()
612 .with_subscriber(self.0.log_subscriber.clone())
613 .await?
614 .0)
615 }
616
617 /// Get stock positions
618 ///
619 /// Reference: <https://open.longportapp.com/en/docs/trade/asset/stock>
620 ///
621 /// # Examples
622 ///
623 /// ```no_run
624 /// use std::sync::Arc;
625 ///
626 /// use longport::{Config, trade::TradeContext};
627 ///
628 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
629 /// let config = Arc::new(Config::from_env()?);
630 /// let (ctx, _) = TradeContext::try_new(config).await?;
631 ///
632 /// let resp = ctx.stock_positions(None).await?;
633 /// println!("{:?}", resp);
634 /// # Ok::<_, Box<dyn std::error::Error>>(())
635 /// # });
636 /// ```
637 pub async fn stock_positions(
638 &self,
639 opts: impl Into<Option<GetStockPositionsOptions>>,
640 ) -> Result<StockPositionsResponse> {
641 Ok(self
642 .0
643 .http_cli
644 .request(Method::GET, "/v1/asset/stock")
645 .query_params(opts.into().unwrap_or_default())
646 .response::<Json<StockPositionsResponse>>()
647 .send()
648 .with_subscriber(self.0.log_subscriber.clone())
649 .await?
650 .0)
651 }
652
653 /// Get margin ratio
654 ///
655 /// Reference: <https://open.longportapp.com/en/docs/trade/asset/margin_ratio>
656 ///
657 /// # Examples
658 ///
659 /// ```no_run
660 /// use std::sync::Arc;
661 ///
662 /// use longport::{Config, trade::TradeContext};
663 ///
664 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
665 /// let config = Arc::new(Config::from_env()?);
666 /// let (ctx, _) = TradeContext::try_new(config).await?;
667 ///
668 /// let resp = ctx.margin_ratio("700.HK").await?;
669 /// println!("{:?}", resp);
670 /// # Ok::<_, Box<dyn std::error::Error>>(())
671 /// # });
672 /// ```
673 pub async fn margin_ratio(&self, symbol: impl Into<String>) -> Result<MarginRatio> {
674 #[derive(Debug, Serialize)]
675 struct Request {
676 symbol: String,
677 }
678
679 Ok(self
680 .0
681 .http_cli
682 .request(Method::GET, "/v1/risk/margin-ratio")
683 .query_params(Request {
684 symbol: symbol.into(),
685 })
686 .response::<Json<MarginRatio>>()
687 .send()
688 .with_subscriber(self.0.log_subscriber.clone())
689 .await?
690 .0)
691 }
692
693 /// Get order detail
694 ///
695 /// Reference: <https://open.longportapp.com/en/docs/trade/order/order_detail>
696 ///
697 /// # Examples
698 ///
699 /// ```no_run
700 /// use std::sync::Arc;
701 ///
702 /// use longport::{
703 /// Config, Market,
704 /// trade::{GetHistoryOrdersOptions, OrderSide, OrderStatus, TradeContext},
705 /// };
706 /// use time::macros::datetime;
707 ///
708 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
709 /// let config = Arc::new(Config::from_env()?);
710 /// let (ctx, _) = TradeContext::try_new(config).await?;
711 ///
712 /// let resp = ctx.order_detail("701276261045858304").await?;
713 /// println!("{:?}", resp);
714 /// # Ok::<_, Box<dyn std::error::Error>>(())
715 /// # });
716 /// ```
717 pub async fn order_detail(&self, order_id: impl Into<String>) -> Result<OrderDetail> {
718 #[derive(Debug, Serialize)]
719 struct Request {
720 order_id: String,
721 }
722
723 Ok(self
724 .0
725 .http_cli
726 .request(Method::GET, "/v1/trade/order")
727 .response::<Json<OrderDetail>>()
728 .query_params(Request {
729 order_id: order_id.into(),
730 })
731 .send()
732 .with_subscriber(self.0.log_subscriber.clone())
733 .await?
734 .0)
735 }
736
737 /// Estimating the maximum purchase quantity for Hong Kong and US stocks,
738 /// warrants, and options
739 ///
740 ///
741 /// Reference: <https://open.longportapp.com/en/docs/trade/order/estimate_available_buy_limit>
742 ///
743 /// # Examples
744 ///
745 /// ```no_run
746 /// use std::sync::Arc;
747 ///
748 /// use longport::{
749 /// Config,
750 /// trade::{EstimateMaxPurchaseQuantityOptions, OrderSide, OrderType, TradeContext},
751 /// };
752 /// use time::macros::datetime;
753 ///
754 /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
755 /// let config = Arc::new(Config::from_env()?);
756 /// let (ctx, _) = TradeContext::try_new(config).await?;
757 ///
758 /// let resp = ctx
759 /// .estimate_max_purchase_quantity(EstimateMaxPurchaseQuantityOptions::new(
760 /// "700.HK",
761 /// OrderType::LO,
762 /// OrderSide::Buy,
763 /// ))
764 /// .await?;
765 /// println!("{:?}", resp);
766 /// # Ok::<_, Box<dyn std::error::Error>>(())
767 /// # });
768 /// ```
769 pub async fn estimate_max_purchase_quantity(
770 &self,
771 opts: EstimateMaxPurchaseQuantityOptions,
772 ) -> Result<EstimateMaxPurchaseQuantityResponse> {
773 Ok(self
774 .0
775 .http_cli
776 .request(Method::GET, "/v1/trade/estimate/buy_limit")
777 .query_params(opts)
778 .response::<Json<EstimateMaxPurchaseQuantityResponse>>()
779 .send()
780 .with_subscriber(self.0.log_subscriber.clone())
781 .await?
782 .0)
783 }
784}